├── .gitignore
├── COPYING
├── Makefile_Linux
├── Makefile_Windows
├── README.md
├── Showcase.gif
├── build
├── AUDIO
│ ├── complete.wav
│ ├── hold.wav
│ ├── land.wav
│ ├── move.wav
│ ├── musicIntro.wav
│ ├── musicLoop.wav
│ ├── over.wav
│ ├── pause.wav
│ ├── rotate.wav
│ ├── specs.wav
│ └── unlock.wav
└── SPRITES
│ ├── game_ss.png
│ └── string_ss.png
├── build_windows
├── AUDIO
│ ├── complete.wav
│ ├── hold.wav
│ ├── land.wav
│ ├── move.wav
│ ├── musicIntro.wav
│ ├── musicLoop.wav
│ ├── over.wav
│ ├── pause.wav
│ ├── rotate.wav
│ ├── specs.wav
│ └── unlock.wav
└── SPRITES
│ ├── game_ss.png
│ └── string_ss.png
└── src
├── Definitions.h
├── Globals.h
├── MGF.h
├── Macros.h
├── Structures.h
├── audio.c
├── audio.h
├── controls.c
├── controls.h
├── credits.c
├── credits.h
├── draw.c
├── draw.h
├── file.c
├── file.h
├── generate.c
├── generate.h
├── instance.c
├── instance.h
├── layout.c
├── layout.h
├── main.c
├── main.h
├── mainLoop.c
├── mainLoop.h
├── map.c
├── map.h
├── memory.c
├── memory.h
├── multiplayerLobby.c
├── multiplayerLobby.h
├── network.c
├── network.h
├── playMode.c
├── playMode.h
├── rotate.c
├── rotate.h
├── server.c
└── server.h
/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 |
3 | !src/
4 | !src/*
5 | !src/HEADERS
6 | !src/HEADERS/*
7 |
8 | !build/
9 | !build/AUDIO
10 | !build/AUDIO/*
11 | !build/SPRITES
12 | !build/SPRITES/*
13 |
14 | !build_windows/
15 | !build_windows/AUDIO
16 | !build_windows/AUDIO/*
17 | !build_windows/SPRITES
18 | !build_windows/SPRITES/*
19 |
20 | !Makefile_Linux
21 | !Makefile_Windows
22 |
23 | !unix_obj/
24 | !win_obj/
25 |
26 | !server/
27 | !server/src
28 | !server/src/*
29 | !server/build
30 | !server/build/*
31 | !server/build_windows
32 | !server/build_windows/*
33 | !server/unix_obj
34 | !server/unix_obj/*
35 | !server/win_obj
36 | !server/win_obj/*
--------------------------------------------------------------------------------
/Makefile_Linux:
--------------------------------------------------------------------------------
1 | ########################################################################
2 | ####################### Makefile Template ##############################
3 | ########################################################################
4 |
5 | # Compiler settings - Can be customized.
6 | CC = gcc
7 | CXXFLAGS = -std=c17 -Wall -Wextra -g -O3
8 | LDFLAGS = '-Wl,-rpath,$$ORIGIN' -lSDL2 -lSDL2_image -lSDL2_net -lSDL2_mixer -lm
9 |
10 | # Makefile settings - Can be customized.
11 | APPNAME = build/Multris
12 | EXT = .c
13 | SRCDIR = src
14 | OBJDIR = unix_obj
15 |
16 | SRC = $(wildcard $(SRCDIR)/*$(EXT))
17 | OBJ = $(SRC:$(SRCDIR)/%$(EXT)=$(OBJDIR)/%.o)
18 | # UNIX-based OS variables & settings
19 | RM = rm
20 | DELOBJ = $(OBJ)
21 | # Windows OS variables & settings
22 | DEL = del
23 | EXE = .exe
24 | WDELOBJ = $(SRC:$(SRCDIR)/%$(EXT)=$(OBJDIR)\\%.o)
25 |
26 | ########################################################################
27 | ####################### Targets beginning here #########################
28 | ########################################################################
29 |
30 | $(shell mkdir -p $(OBJDIR))
31 |
32 | all: $(APPNAME)
33 | rm -rf $(OBJDIR)
34 |
35 | # Builds the app
36 | $(APPNAME): $(OBJ)
37 | $(CC) $(CXXFLAGS) -o $@ $^ $(LDFLAGS)
38 |
39 | # Creates the dependecy rules
40 | %.d: $(SRCDIR)/%$(EXT)
41 | @$(CPP) $(CFLAGS) $< -MM -MT $(@:%.d=$(OBJDIR)/%.o) >$@
42 |
43 | # Building rule for .o files and its .c/.cpp in combination with all .h
44 | $(OBJDIR)/%.o: $(SRCDIR)/%$(EXT)
45 | $(CC) $(CXXFLAGS) -o $@ -c $<
--------------------------------------------------------------------------------
/Makefile_Windows:
--------------------------------------------------------------------------------
1 | ########################################################################
2 | ####################### Makefile Template ##############################
3 | ########################################################################
4 |
5 | SDL2_DIR = SDL2-devel-2.32.2-mingw/SDL2-2.32.2/x86_64-w64-mingw32
6 | SDL2IMAGE_DIR = SDL2_image-devel-2.8.8-mingw/SDL2_image-2.8.8/x86_64-w64-mingw32
7 | SDL2NET_DIR = SDL2_net-devel-2.2.0-mingw/SDL2_net-2.2.0/x86_64-w64-mingw32
8 | SDL2MIXER_DIR = SDL2_mixer-devel-2.8.1-mingw/SDL2_mixer-2.8.1/x86_64-w64-mingw32
9 |
10 | # Compiler settings - Can be customized.
11 | CC = x86_64-w64-mingw32-gcc \
12 | -L$(SDL2_DIR)/lib \
13 | -I$(SDL2_DIR)/include \
14 | -I$(SDL2_DIR)/include/SDL2 \
15 | -L$(SDL2IMAGE_DIR)/lib \
16 | -I$(SDL2IMAGE_DIR)/include \
17 | -L$(SDL2NET_DIR)/lib \
18 | -I$(SDL2NET_DIR)/include \
19 | -L$(SDL2MIXER_DIR)/lib \
20 | -I$(SDL2MIXER_DIR)/include \
21 |
22 | CXXFLAGS = -std=c17 -Wall -Wextra -g -O3
23 | LDFLAGS = -lmingw32 -mwindows -lSDL2main -lSDL2 -lSDL2_image -lSDL2_net -lSDL2_mixer -lssp -lm
24 |
25 | # Makefile settings - Can be customized.
26 | APPNAME = build_windows/Multris
27 | EXT = .c
28 | SRCDIR = src
29 | OBJDIR = win_obj
30 |
31 | SRC = $(wildcard $(SRCDIR)/*$(EXT))
32 | OBJ = $(SRC:$(SRCDIR)/%$(EXT)=$(OBJDIR)/%.o)
33 | # UNIX-based OS variables & settings
34 | RM = rm
35 | DELOBJ = $(OBJ)
36 | # Windows OS variables & settings
37 | DEL = del
38 | EXE = .exe
39 | WDELOBJ = $(SRC:$(SRCDIR)/%$(EXT)=$(OBJDIR)\\%.o)
40 |
41 | ########################################################################
42 | ####################### Targets beginning here #########################
43 | ########################################################################
44 |
45 | $(shell mkdir -p $(OBJDIR))
46 |
47 | all: $(APPNAME)
48 | rm -rf $(OBJDIR)
49 |
50 | # Builds the app
51 | $(APPNAME): $(OBJ)
52 | $(CC) $(CXXFLAGS) -o $@ $^ $(LDFLAGS)
53 |
54 | # Creates the dependecy rules
55 | %.d: $(SRCDIR)/%$(EXT)
56 | @$(CPP) $(CFLAGS) $< -MM -MT $(@:%.d=$(OBJDIR)/%.o) >$@
57 |
58 | # Building rule for .o files and its .c/.cpp in combination with all .h
59 | $(OBJDIR)/%.o: $(SRCDIR)/%$(EXT)
60 | $(CC) $(CXXFLAGS) -o $@ -c $<
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Multris
2 | A modified version of the classic puzzle game, with randomly generated polyominoes of varying sizes.
3 |
4 | ## Table of Contents
5 | [Showcase](#Showcase)
6 |
7 | [How to Play](#How_to_Play)
8 |
9 | [Building From Source](#Building_From_Source)
10 |
11 | [Pre-Compiled Downloads](#Pre_Compiled_Downloads)
12 |
13 |
14 |
15 | ## Showcase
16 | 
17 |
18 |
19 |
20 | ## How to Play
21 |
22 | ##### DEFAULT CONTROLS
23 | ```
24 | LEFT / RIGHT ARROW - Move. Hold to move quicker.
25 | DOWN ARROW - Soft Drop
26 | UP ARROW - Hard Drop
27 | SPACE - Hold current piece / Swap current piece with held piece
28 | Z - Rotate CCW
29 | X - Rotate CW
30 | C - Mirror piece over Y-Axis
31 | ENTER - Select/Pause
32 | ESCAPE - Exit current menu.
33 | ```
34 | ##### GAME MODES
35 | ```
36 | MULTRIS - All pieces are a random size.
37 | NUMERICAL - All pieces are the selected size.
38 | Unlock larger sizes by reaching high levels in smaller sizes.
39 | CUSTOM - Same as MULTRIS mode except that the maximum piece size can be selected.
40 | MULTIPLAYER - Battle against another player in MULTRIS mode.
41 | ```
42 | ##### GAMEPLAY
43 | ```
44 | SPEED - Speed increases as level increases.
45 | LEVEL - Level increases as you complete lines.
46 | The number of completed lines needed to advance to the next level increases with each level.
47 | Score - You score points by completing lines.
48 | The more lines you complete at one time, the more points you get.
49 | Completing lines are also worth more points at higher levels.
50 | ```
51 | ##### OPTIONS
52 | ```
53 | FULLSCREEN - Enable or disable fullscreen mode
54 | SFX VOL - Set the volume of the game audio. Between 0-100.
55 | MUSIC VOL - Set the volume of the background music. Between 0-100.
56 | LIMIT FPS - Limit the FPS of the game to twice that of the refresh rate of the display.
57 | This is to lower CPU usage.
58 | SHOW FPS - Enable/Disable drawing the FPS to the screen.
59 | CENTER DOT - Enable to draw a dot in the center of the current piece while playing.
60 | GRAPHICS - Switch between newer graphics and older legacy graphics.
61 | CONTROLS - Modify the keybindings for the game.
62 | ```
63 |
64 |
65 |
66 | ## Building From Source
67 |
68 | ### Requirements
69 |
70 | ###### Make sure that you have the development libraries as well as the runtime binaries installed
71 |
72 | [SDL2](https://github.com/libsdl-org/SDL)
73 |
74 | [SDL_Image](https://github.com/libsdl-org/SDL_image)
75 |
76 | [SDL_Net](https://github.com/libsdl-org/SDL_net)
77 |
78 | [SDL_Mixer](https://github.com/libsdl-org/SDL_mixer)
79 |
80 | For all following instructions, replace `make -f Makefile_Linux` with `make -f Makefile_Windows` if you are on Windows. Also, edit `Makefile_Windows` to set the `SDL2_DIR`, `SDL2IMAGE_DIR`, `SDL2NET_DIR`, `SDL2MIXER_DIR` to the appropriate folders for your SDL2 development files.
81 |
82 | #### Building the Latest Development Version (Unstable)
83 |
84 | ```
85 | git clone https://github.com/RustyReich/Multris
86 | cd Multris
87 | make -f Makefile_Linux
88 | ```
89 |
90 | #### Building a Specific Release Version (Stable)
91 |
92 | ###### For versions 1.0.8+
93 |
94 | ```
95 | git clone https://github.com/RustyReich/Multris --branch v1.x.x
96 | cd Multris
97 | make -f Makefile_Linux
98 | ```
99 |
100 | ###### For versions 1.0.4 - 1.0.7
101 |
102 | ```
103 | git clone https://github.com/RustyReich/Multris --branch v1.x.x
104 | cd Multris
105 | mkdir unix_obj
106 | mkdir unix_dep
107 | mkdir build
108 | wget https://raw.githubusercontent.com/RustyReich/Multris/main/Makefile_Linux
109 | make -f Makefile_Linux
110 | cp -R ./src/AUDIO ./build/AUDIO
111 | cp -R ./src/SPRITES ./build/SPRITES
112 | ```
113 |
114 |
115 | ###### Build instructions are not available for versions before 1.0.4
116 |
117 | ###### The executable will be in `/Multris/build`
118 |
119 |
120 |
121 | ## Pre-Compiled Downloads
122 | Since version 1.0.7, Pre-Compiled builds are only available for purchase on [Steam](https://store.steampowered.com/app/1768350/Multris/)
123 |
124 | Older versions (1.0.0 - 1.0.6) are available for free on [Itch.io](https://rustymonster.itch.io/multris)
125 |
--------------------------------------------------------------------------------
/Showcase.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RustyReich/Multris/86e01da11db7239f536272f0ebfbc109fc5ca043/Showcase.gif
--------------------------------------------------------------------------------
/build/AUDIO/complete.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RustyReich/Multris/86e01da11db7239f536272f0ebfbc109fc5ca043/build/AUDIO/complete.wav
--------------------------------------------------------------------------------
/build/AUDIO/hold.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RustyReich/Multris/86e01da11db7239f536272f0ebfbc109fc5ca043/build/AUDIO/hold.wav
--------------------------------------------------------------------------------
/build/AUDIO/land.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RustyReich/Multris/86e01da11db7239f536272f0ebfbc109fc5ca043/build/AUDIO/land.wav
--------------------------------------------------------------------------------
/build/AUDIO/move.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RustyReich/Multris/86e01da11db7239f536272f0ebfbc109fc5ca043/build/AUDIO/move.wav
--------------------------------------------------------------------------------
/build/AUDIO/musicIntro.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RustyReich/Multris/86e01da11db7239f536272f0ebfbc109fc5ca043/build/AUDIO/musicIntro.wav
--------------------------------------------------------------------------------
/build/AUDIO/musicLoop.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RustyReich/Multris/86e01da11db7239f536272f0ebfbc109fc5ca043/build/AUDIO/musicLoop.wav
--------------------------------------------------------------------------------
/build/AUDIO/over.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RustyReich/Multris/86e01da11db7239f536272f0ebfbc109fc5ca043/build/AUDIO/over.wav
--------------------------------------------------------------------------------
/build/AUDIO/pause.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RustyReich/Multris/86e01da11db7239f536272f0ebfbc109fc5ca043/build/AUDIO/pause.wav
--------------------------------------------------------------------------------
/build/AUDIO/rotate.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RustyReich/Multris/86e01da11db7239f536272f0ebfbc109fc5ca043/build/AUDIO/rotate.wav
--------------------------------------------------------------------------------
/build/AUDIO/specs.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RustyReich/Multris/86e01da11db7239f536272f0ebfbc109fc5ca043/build/AUDIO/specs.wav
--------------------------------------------------------------------------------
/build/AUDIO/unlock.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RustyReich/Multris/86e01da11db7239f536272f0ebfbc109fc5ca043/build/AUDIO/unlock.wav
--------------------------------------------------------------------------------
/build/SPRITES/game_ss.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RustyReich/Multris/86e01da11db7239f536272f0ebfbc109fc5ca043/build/SPRITES/game_ss.png
--------------------------------------------------------------------------------
/build/SPRITES/string_ss.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RustyReich/Multris/86e01da11db7239f536272f0ebfbc109fc5ca043/build/SPRITES/string_ss.png
--------------------------------------------------------------------------------
/build_windows/AUDIO/complete.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RustyReich/Multris/86e01da11db7239f536272f0ebfbc109fc5ca043/build_windows/AUDIO/complete.wav
--------------------------------------------------------------------------------
/build_windows/AUDIO/hold.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RustyReich/Multris/86e01da11db7239f536272f0ebfbc109fc5ca043/build_windows/AUDIO/hold.wav
--------------------------------------------------------------------------------
/build_windows/AUDIO/land.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RustyReich/Multris/86e01da11db7239f536272f0ebfbc109fc5ca043/build_windows/AUDIO/land.wav
--------------------------------------------------------------------------------
/build_windows/AUDIO/move.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RustyReich/Multris/86e01da11db7239f536272f0ebfbc109fc5ca043/build_windows/AUDIO/move.wav
--------------------------------------------------------------------------------
/build_windows/AUDIO/musicIntro.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RustyReich/Multris/86e01da11db7239f536272f0ebfbc109fc5ca043/build_windows/AUDIO/musicIntro.wav
--------------------------------------------------------------------------------
/build_windows/AUDIO/musicLoop.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RustyReich/Multris/86e01da11db7239f536272f0ebfbc109fc5ca043/build_windows/AUDIO/musicLoop.wav
--------------------------------------------------------------------------------
/build_windows/AUDIO/over.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RustyReich/Multris/86e01da11db7239f536272f0ebfbc109fc5ca043/build_windows/AUDIO/over.wav
--------------------------------------------------------------------------------
/build_windows/AUDIO/pause.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RustyReich/Multris/86e01da11db7239f536272f0ebfbc109fc5ca043/build_windows/AUDIO/pause.wav
--------------------------------------------------------------------------------
/build_windows/AUDIO/rotate.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RustyReich/Multris/86e01da11db7239f536272f0ebfbc109fc5ca043/build_windows/AUDIO/rotate.wav
--------------------------------------------------------------------------------
/build_windows/AUDIO/specs.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RustyReich/Multris/86e01da11db7239f536272f0ebfbc109fc5ca043/build_windows/AUDIO/specs.wav
--------------------------------------------------------------------------------
/build_windows/AUDIO/unlock.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RustyReich/Multris/86e01da11db7239f536272f0ebfbc109fc5ca043/build_windows/AUDIO/unlock.wav
--------------------------------------------------------------------------------
/build_windows/SPRITES/game_ss.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RustyReich/Multris/86e01da11db7239f536272f0ebfbc109fc5ca043/build_windows/SPRITES/game_ss.png
--------------------------------------------------------------------------------
/build_windows/SPRITES/string_ss.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RustyReich/Multris/86e01da11db7239f536272f0ebfbc109fc5ca043/build_windows/SPRITES/string_ss.png
--------------------------------------------------------------------------------
/src/Definitions.h:
--------------------------------------------------------------------------------
1 | #ifndef DEFINITIONS_H_
2 | #define DEFINITIONS_H_
3 |
4 | #include "MGF.h"
5 |
6 | #define FONT_WIDTH 12
7 | #define FONT_HEIGHT 12
8 | #define STRING_GAP 2
9 |
10 | #define SPRITE_WIDTH 12
11 | #define SPRITE_HEIGHT 12
12 |
13 | #define MAX_PIECE_SIZE 8
14 |
15 | #define OPTIONS 9
16 |
17 | #define BASE_PLAYFIELD_WIDTH 2.5
18 | #define BASE_PLAYFIELD_HEIGHT 5
19 |
20 | #define NUM_MOVING_TITLE_PIECES 14
21 |
22 | #define MOVEMENT_WAIT 500
23 | #define MOVEMENT_TIME 50
24 |
25 | //Speed is in cells per second
26 | //Calculated using values from NES version of Tetris
27 | #define INITIAL_SPEED (double)(60.0988 / 48)
28 |
29 | #define CLEARING_TIME 20
30 |
31 | #define HOLD_TEXTURE_MULTI 0.75
32 |
33 | #define MIN_CUSTOM_SIZE 4
34 | #define MAX_CUSTOM_SIZE 15
35 |
36 | #define LEVELUP_INCREASE 3
37 |
38 | #define NUM_OF_OPTIONS 8
39 | #define NUM_OF_MULTIPLAYER_OPTIONS 3
40 | #define NUM_OF_PAUSED_OPTIONS 7
41 |
42 | #define MULTIPLAYER_PULSE_DELAY_SECONDS 1
43 |
44 | #define MAX_NAME_LENGTH 7
45 |
46 | #define SERVERMESSAGE_BUFFER_SIZE 1024
47 | #define CONNECTION_TIMEOUT_SECONDS 5
48 | #define SERVER_TICK_RATE 128
49 | #define HOST_GAME_DATA_MAX_LENGTH 48
50 | #define CONNECT_GAME_DATA_MAX_LENGTH 48
51 |
52 | #define MUSIC_FADEOUT_MS 1000
53 |
54 | #define FIRST_NUMERICAL_SIZE 4
55 |
56 | enum Control_Types {
57 |
58 | LEFT_BUTTON,
59 | RIGHT_BUTTON,
60 | SOFT_DROP_BUTTON,
61 | HARD_DROP_BUTTON,
62 | HOLD_BUTTON,
63 | ROTATE_CCW_BUTTON,
64 | ROTATE_CW_BUTTON,
65 | MIRROR_BUTTON,
66 | SELECT_BUTTON,
67 | EXIT_BUTTON,
68 | DOWN_BUTTON,
69 | UP_BUTTON,
70 | NUM_OF_CONTROLS
71 |
72 | };
73 |
74 | enum Music_Types {
75 |
76 | INTRO_MUSIC_TRACK,
77 | LOOP_MUSIC_TRACK,
78 | NUM_OF_MUSIC_TRACKS
79 |
80 | };
81 |
82 | enum Sound_Types {
83 |
84 | COMPLETE_SOUND,
85 | HOLD_SOUND,
86 | LAND_SOUND,
87 | MOVE_SOUND,
88 | OVER_SOUND,
89 | PAUSE_SOUND,
90 | ROTATE_SOUND,
91 | UNLOCK_SOUND,
92 | NUM_OF_SOUNDS
93 |
94 | };
95 |
96 | enum Color_Types {
97 |
98 | GRAY = 1,
99 | WHITE = 2,
100 | YELLOW = 3,
101 | GREEN = 4,
102 | CYAN = 5,
103 | MAGENTA = 6,
104 | ORANGE = 7,
105 | BLUE = 8,
106 | RED = 9,
107 | BLACK = 10,
108 | DARK_GRAY = 11
109 |
110 | };
111 |
112 | enum Texture_Types {
113 |
114 | SCORE_TEXT,
115 | LEVEL_TEXT,
116 | LINES_TEXT,
117 | PAUSED_TEXT,
118 | FOREGROUND_TEXT,
119 | TITLE_TEXT,
120 | CURSOR_TEXT,
121 | VALUES_TEXT,
122 | VOLSLIDE_TEXT,
123 | CONTROLS_TEXT,
124 | CUSTOM_TEXT,
125 | SIZEBAG_TEXT,
126 | CONNECTIONVALUES_TEXT,
127 | CONNECTIONMESSAGE_TEXT,
128 | NAME_TEXT,
129 | HOSTINGVALUES_TEXT,
130 | PAUSEDVALUES_TEXT,
131 | CREDITS_TEXT,
132 | NUM_OF_TEXTURES
133 |
134 | };
135 |
136 | enum List_Types {
137 |
138 | MODES_LIST,
139 | NUMERICAL_LIST,
140 | CUSTOM_LIST,
141 | OPTIONS_LIST,
142 | CONNECTION_LIST,
143 | MULTIPLAYER_LIST,
144 | HOSTING_LIST,
145 | PAUSE_LIST,
146 | NUM_OF_LISTS
147 |
148 | };
149 |
150 | enum Data_Type {
151 |
152 | TYPE_NA,
153 | VARIABLE,
154 | PIECE,
155 | TEXTURE,
156 | MOVING_TITLE_PIECES,
157 | UI_LIST,
158 | SIZE_BAG,
159 | NUM_OF_DATA_TYPES
160 |
161 | };
162 |
163 | enum Screen_Type {
164 |
165 | TITLE_SCREEN,
166 | PLAY_SCREEN,
167 | CONTROLS_SCREEN,
168 | MULTIPLAYERLOBBY_SCREEN,
169 | RESET,
170 | CREDITS_SCREEN,
171 | NUM_OF_SCREENS
172 |
173 | };
174 |
175 | enum Rotation_Type {
176 |
177 | CW,
178 | CCW,
179 | NUM_OF_ROTATIONS
180 |
181 | };
182 |
183 | enum Direction_Type {
184 |
185 | UP,
186 | RIGHT,
187 | DOWN,
188 | LEFT,
189 | NONE,
190 | NUM_OF_DIRECTIONS
191 |
192 | };
193 |
194 | #endif
--------------------------------------------------------------------------------
/src/Globals.h:
--------------------------------------------------------------------------------
1 | #ifndef GLOBALS_H_
2 | #define GLOBALS_H_
3 |
4 | #include "MGF.h"
5 |
6 | extern gameInstance *globalInstance;
7 |
8 | //Global variables
9 | extern bool UPDATE_FULLSCREEN_MODE;
10 |
11 | extern bool FULLSCREEN_MODE;
12 | extern unsigned short VOLUME;
13 | extern unsigned short MUSIC_VOLUME;
14 | extern bool LIMIT_FPS;
15 | extern bool SHOW_FPS;
16 | extern bool CENTER_DOT;
17 | extern bool GRAPHICS;
18 |
19 | extern int PROGRESS;
20 |
21 | extern int MODE;
22 | extern int MAP_WIDTH;
23 | extern int MAP_HEIGHT;
24 |
25 | extern bool CUSTOM_MODE;
26 |
27 | extern int BLOCK_SPRITE_ID;
28 | extern int WALL_SPRITE_ID;
29 |
30 | extern bool DEBUG;
31 |
32 | extern bool MULTIPLAYER;
33 |
34 | #endif
--------------------------------------------------------------------------------
/src/MGF.h:
--------------------------------------------------------------------------------
1 | #ifndef MGF_H_
2 | #define MGF_H_
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | #include "SDL2/SDL.h"
12 | #include "SDL2/SDL_image.h"
13 | #include "SDL2/SDL_net.h"
14 | #include "SDL2/SDL_mixer.h"
15 |
16 | #include "Structures.h"
17 | #include "Globals.h"
18 | #include "Definitions.h"
19 | #include "Macros.h"
20 | #include "audio.h"
21 | #include "controls.h"
22 | #include "draw.h"
23 | #include "file.h"
24 | #include "generate.h"
25 | #include "instance.h"
26 | #include "layout.h"
27 | #include "main.h"
28 | #include "mainLoop.h"
29 | #include "map.h"
30 | #include "memory.h"
31 | #include "playMode.h"
32 | #include "rotate.h"
33 | #include "multiplayerLobby.h"
34 | #include "network.h"
35 | #include "server.h"
36 | #include "credits.h"
37 |
38 | // OS-specific includes/macros
39 | #ifdef _WIN32
40 | #include
41 | #define mkdir(dir, mode) mkdir(dir)
42 | #endif
43 |
44 | #endif
--------------------------------------------------------------------------------
/src/Macros.h:
--------------------------------------------------------------------------------
1 | #ifndef MACROS_H_
2 | #define MACROS_H_
3 |
4 | #include "MGF.h"
5 |
6 | //Declare a variable on the varVector array
7 | #define declareEnd(val) _Generic((val), \
8 | int: declare_int, \
9 | char: declare_char, \
10 | double: declare_double, \
11 | unsigned short: declare_unsigned_short, \
12 | bool: declare_bool, \
13 | unsigned int: declare_unsigned_int, \
14 | short: declare_short, \
15 | unsigned long: declare_unsigned_long \
16 | )
17 | #define declareStart(var, val) _Generic((var) , \
18 | int *: declareEnd(val), \
19 | char *: declareEnd(val), \
20 | double *: declareEnd(val), \
21 | unsigned short *: declareEnd(val), \
22 | bool *: declareEnd(val), \
23 | unsigned int *: declareEnd(val), \
24 | short*: declareEnd(val), \
25 | unsigned long*: declareEnd(val) \
26 | )(((void**)&var), (val))
27 | #define DECLARE_VARIABLE(type, varName, value) static type* varName; declareStart(varName, value)
28 |
29 | //Macros for instance
30 | #define onPress(var) globalInstance->controls[var].onPress
31 | #define onHold(var) globalInstance->controls[var].onHold
32 | #define playSound(var) _playSound(var)
33 |
34 | //Macros for variadic functions
35 | #define create_list(...) _create_list(__VA_ARGS__, NULL)
36 |
37 | //In-line function macros
38 | #define swap(a, b, t) { t tmp = a; a = b; b = tmp; }
39 |
40 | #endif
--------------------------------------------------------------------------------
/src/Structures.h:
--------------------------------------------------------------------------------
1 | #ifndef STRUCTURES_H_
2 | #define STRUCTURES_H_
3 |
4 | #include "MGF.h"
5 |
6 | typedef struct UI
7 | {
8 |
9 | SDL_Texture* texture;
10 |
11 | //Position of the UI element, relative to the renderer
12 | int x;
13 | int y;
14 |
15 | //Wether the current UI element is currently being interacted with by the player
16 | bool currentlyInteracting;
17 |
18 | } UI;
19 |
20 | typedef struct UI_list
21 | {
22 |
23 | UI* ui;
24 | char** entry_texts;
25 | int selected_entry;
26 | int num_entries;
27 |
28 | } UI_list;
29 |
30 | typedef struct varVector
31 | {
32 |
33 | void** ptrs;
34 | unsigned short* types;
35 |
36 | int count;
37 |
38 | } varVector;
39 |
40 | typedef struct control
41 | {
42 |
43 | Uint16 button;
44 |
45 | bool onPress;
46 | bool onHold;
47 |
48 | } control;
49 |
50 | typedef struct sound
51 | {
52 |
53 | Uint32 length;
54 | Uint8* buffer;
55 |
56 | } sound;
57 |
58 | typedef struct gameInstance
59 | {
60 |
61 | SDL_Window* window;
62 | SDL_DisplayMode* DM;
63 | SDL_Renderer* renderer;
64 | SDL_Event event;
65 | SDL_Surface* windowSurface;
66 |
67 | int numDisplays;
68 |
69 | int minDisplayX;
70 | int minDisplayY;
71 | int maxDisplayX;
72 | int maxDisplayY;
73 |
74 | int minimizedWindow_W;
75 | int minimizedWindow_H;
76 | int minimizedWindow_X;
77 | int minimizedWindow_Y;
78 |
79 | SDL_AudioDeviceID* audioDevice;
80 | SDL_AudioSpec* wavSpec;
81 |
82 | SDL_Texture* stringSS;
83 | int stringSS_W;
84 | int stringSS_H;
85 | SDL_Texture* gameSS;
86 | int gameSS_W;
87 | int gameSS_H;
88 |
89 | bool running;
90 |
91 | int FPS;
92 | double frame_time;
93 |
94 | Uint8* keys;
95 | bool* onKeyPress;
96 | bool* onKeyHold;
97 |
98 | control* controls;
99 |
100 | sound** sounds; //This holds the volume adjusted sounds
101 | sound** masterSounds; //This holds the original sonuds, that are not volume adjusted
102 |
103 | Mix_Music** musicTracks;
104 |
105 | IPaddress serverIP;
106 | TCPsocket serverSocket;
107 | SDLNet_SocketSet serverSocketSet;
108 | unsigned long connectionThreadID;
109 | bool hosting;
110 |
111 | int randState;
112 |
113 | } gameInstance;
114 |
115 | typedef struct block
116 | {
117 |
118 | signed short X;
119 | signed short Y;
120 |
121 | } block;
122 |
123 | typedef struct piece
124 | {
125 |
126 | block* blocks;
127 |
128 | block* centerBlock;
129 |
130 | unsigned short numOfBlocks;
131 | unsigned short color;
132 |
133 | unsigned short width;
134 | unsigned short height;
135 |
136 | signed short minX;
137 | signed short minY;
138 |
139 | } piece;
140 |
141 | typedef struct SizeBag
142 | {
143 |
144 | unsigned short size;
145 | unsigned short* sizesInBag;
146 |
147 | } SizeBag;
148 |
149 | #endif
--------------------------------------------------------------------------------
/src/audio.c:
--------------------------------------------------------------------------------
1 | #include "MGF.h"
2 |
3 | //Play a sound
4 | //Only one sound can be played at a time.
5 | //Any sound currently being played when this function is called will be cutoff and the new sound will
6 | //start.
7 | void _playSound(int id)
8 | {
9 |
10 | sound* Sound = globalInstance->sounds[id];
11 |
12 | //Clear the audio buffer and queue the audio currently trying to be played
13 | SDL_ClearQueuedAudio(*globalInstance->audioDevice);
14 | SDL_QueueAudio(*globalInstance->audioDevice, Sound->buffer, Sound->length);
15 |
16 | //Play the audio currently queued
17 | SDL_PauseAudioDevice(*globalInstance->audioDevice, 0);
18 |
19 | }
20 |
21 | //Load a sound
22 | sound* loadSound(char* file)
23 | {
24 |
25 | //Allocate memory for the sound
26 | sound* newSound;
27 | newSound = SDL_malloc(sizeof(*newSound));
28 |
29 | //Load the sound into the allocated memory
30 | if (newSound != NULL)
31 | SDL_LoadWAV(file, globalInstance->wavSpec, &newSound->buffer, &newSound->length);
32 |
33 | return newSound;
34 |
35 | }
36 |
37 | //Clear memory allocated by a sound
38 | void delSound(sound** Sound)
39 | {
40 |
41 | //Free memory taken by the sounds buffer
42 | SDL_FreeWAV((*Sound)->buffer);
43 | (*Sound)->buffer = NULL;
44 |
45 | //Now free memory taken by the actual sound struct itself
46 | SDL_free(*Sound);
47 | *Sound = NULL;
48 |
49 | }
50 |
51 | //Set volume of sound
52 | void setVolume(sound** Sound, unsigned short volume)
53 | {
54 |
55 | //Buffer that stores audio after volume adjustment
56 | Uint8* dstStream;
57 | dstStream = SDL_calloc((*Sound)->length, sizeof(*dstStream));
58 |
59 | //Mix audio with volume adjustment
60 | volume = SDL_MIX_MAXVOLUME * (volume / 100.0);
61 | SDL_MixAudioFormat(dstStream, (*Sound)->buffer, globalInstance->wavSpec->format, (*Sound)->length, volume);
62 |
63 | //Copy volume adjusted buffer back to Sound->buffer
64 | if (dstStream != NULL)
65 | for (unsigned int i = 0; i < (*Sound)->length; i++)
66 | (*Sound)->buffer[i] = dstStream[i];
67 |
68 | //Free dstStream
69 | SDL_free(dstStream);
70 | dstStream = NULL;
71 |
72 | }
73 |
74 | //Get the length of a sound struct in milliseconds
75 | int getAudioLengthInMS(sound* Sound) {
76 |
77 | //Not really sure why we need to divide by 2 here
78 | double length_in_seconds = (double)(Sound->length) / (double)(globalInstance->wavSpec->freq) / (double)2;
79 | int length_in_ms = SDL_ceil(length_in_seconds * 1000);
80 |
81 | return length_in_ms;
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/src/audio.h:
--------------------------------------------------------------------------------
1 | #ifndef AUDIO_H_
2 | #define AUDIO_H_
3 |
4 | #include "MGF.h"
5 |
6 | void _playSound(int id);
7 | sound* loadSound(char* file);
8 | void delSound(sound** Sound);
9 | void setVolume(sound** Sound, unsigned short volume);
10 | int getAudioLengthInMS(sound* Sound);
11 |
12 | #endif
--------------------------------------------------------------------------------
/src/controls.c:
--------------------------------------------------------------------------------
1 | #include "MGF.h"
2 |
3 | //Lookup table for control names
4 | static const char* control_name[] =
5 | {
6 |
7 | [0] = "LEFT",
8 | [1] = "RIGHT",
9 | [2] = "SOFT DROP",
10 | [3] = "HARD DROP",
11 | [4] = "HOLD",
12 | [5] = "ROTATE CCW",
13 | [6] = "ROTATE CW",
14 | [7] = "MIRROR",
15 | [8] = "SELECT",
16 | [9] = "EXIT",
17 | [10] = "DOWN",
18 | [11] = "UP",
19 |
20 | };
21 |
22 | unsigned short controlsScreen(piece** Piece)
23 | {
24 |
25 | //Variables
26 | DECLARE_VARIABLE(int, nextText_Width, 0);
27 | DECLARE_VARIABLE(int, nextText_Height, 0);
28 | DECLARE_VARIABLE(bool, firstLoop, true);
29 | DECLARE_VARIABLE(unsigned short, selected_control, 0);
30 | DECLARE_VARIABLE(bool, editing_control, false);
31 | DECLARE_VARIABLE(int, new_key, -1);
32 |
33 | //Textures
34 | static SDL_Texture* Texture_Score; declare_HUD_Text(&Texture_Score, SCORE_TEXT);
35 | static SDL_Texture* Texture_Level; declare_HUD_Text(&Texture_Level, LEVEL_TEXT);
36 | static SDL_Texture* Texture_Lines; declare_HUD_Text(&Texture_Lines, LINES_TEXT);
37 | static SDL_Texture* Texture_Next; declare_Piece_Text(&Texture_Next, *Piece, false);
38 | static SDL_Texture* Texture_Controls; declare_HUD_Text(&Texture_Controls, CONTROLS_TEXT);
39 | static SDL_Texture* Texture_Cursor; declare_HUD_Text(&Texture_Cursor, CURSOR_TEXT);
40 |
41 | //Get the width and height of the Next Piece texture during the first frame
42 | if (*firstLoop == true)
43 | {
44 |
45 | SDL_QueryTexture(Texture_Next, NULL, NULL, nextText_Width, nextText_Height);
46 | *firstLoop = false;
47 |
48 | }
49 |
50 | // Control Logic ---------------------------------------------------
51 |
52 | if (onPress(SELECT_BUTTON))
53 | {
54 |
55 | // Start editing the control as long as you dont have "RESET CONTROLS" selected
56 | if ((*selected_control) != NUM_OF_CONTROLS)
57 | {
58 |
59 | //If you press SELECT when you're not currently editing a control, you start editing the currently
60 | //selected control
61 | if ((*editing_control) == false)
62 | {
63 |
64 | playSound(ROTATE_SOUND);
65 | *editing_control = true;
66 |
67 | //Update controls texture, which will make the currently edited control red
68 | updateControlsText(Texture_Controls, *selected_control, *editing_control);
69 |
70 | }
71 |
72 | }
73 | else { // If you have "RESET CONTROLS" selected
74 |
75 | playSound(ROTATE_SOUND);
76 |
77 | // Just recreate the controls.cfg file with the default values
78 | createControls();
79 |
80 | // Then reload all of the controls from the file
81 | if (fileExists("SAVES/controls.cfg")) {
82 |
83 | globalInstance->controls[LEFT_BUTTON].button = getFileValue("SAVES/controls.cfg", "LEFT");
84 | globalInstance->controls[RIGHT_BUTTON].button = getFileValue("SAVES/controls.cfg", "RIGHT");
85 | globalInstance->controls[SOFT_DROP_BUTTON].button = getFileValue("SAVES/controls.cfg", "SOFT DROP");
86 | globalInstance->controls[HARD_DROP_BUTTON].button = getFileValue("SAVES/controls.cfg", "HARD DROP");
87 | globalInstance->controls[HOLD_BUTTON].button = getFileValue("SAVES/controls.cfg", "HOLD");
88 | globalInstance->controls[ROTATE_CCW_BUTTON].button = getFileValue("SAVES/controls.cfg", "ROTATE CCW");
89 | globalInstance->controls[ROTATE_CW_BUTTON].button = getFileValue("SAVES/controls.cfg", "ROTATE CW");
90 | globalInstance->controls[MIRROR_BUTTON].button = getFileValue("SAVES/controls.cfg", "MIRROR");
91 | globalInstance->controls[SELECT_BUTTON].button = getFileValue("SAVES/controls.cfg", "SELECT");
92 | globalInstance->controls[EXIT_BUTTON].button = getFileValue("SAVES/controls.cfg", "EXIT");
93 | globalInstance->controls[DOWN_BUTTON].button = getFileValue("SAVES/controls.cfg", "DOWN");
94 | globalInstance->controls[UP_BUTTON].button = getFileValue("SAVES/controls.cfg", "UP");
95 |
96 | }
97 |
98 | // And then update the controls texture
99 | updateControlsText(Texture_Controls, NUM_OF_CONTROLS, false);
100 |
101 | }
102 |
103 | }
104 | else
105 | {
106 |
107 | //If you're editing the currently selected control
108 | //Make sure that SELECT is not being held down. This is to allow you to reset the current SELECT
109 | //button
110 | if (*editing_control && !onHold(SELECT_BUTTON))
111 | {
112 |
113 | //Get the currently pressed key
114 | int key = getPressedKey();
115 |
116 | //If they key that is currently pressed is a valid key, set it to new_key
117 | if (key != -1 && *new_key == -1)
118 | *new_key = key;
119 |
120 | //Once we have a valid key, we wait until that key is no longer being pressed
121 | //This means that the control is only set upon the release of that new key
122 | //This is to avoid the control being immediately activated since it's new button is pressed
123 | if (*new_key != -1 && !globalInstance->keys[*new_key])
124 | {
125 |
126 | playSound(ROTATE_SOUND);
127 |
128 | //Set button for selected_control to the new key and get out of editing_control mode
129 | globalInstance->controls[*selected_control].button = *new_key;
130 | *editing_control = false;
131 |
132 | //Save this control
133 | saveToFile("SAVES/controls.cfg", control_name[*selected_control], *new_key);
134 |
135 | //Update the controls texture, returning the selected_control back to yellow
136 | updateControlsText(Texture_Controls, *selected_control, *editing_control);
137 |
138 | //Reset new_key to -1
139 | *new_key = -1;
140 |
141 | }
142 |
143 | }
144 |
145 | }
146 |
147 | //Controls for moving up and down the list of controls
148 | if (onPress(DOWN_BUTTON) && !(*editing_control))
149 | {
150 | if (*selected_control < NUM_OF_CONTROLS)
151 | {
152 | playSound(MOVE_SOUND);
153 | (*selected_control)++;
154 | }
155 | }
156 | if (onPress(UP_BUTTON) && !(*editing_control))
157 | {
158 | if (*selected_control > 0)
159 | {
160 | playSound(MOVE_SOUND);
161 | (*selected_control)--;
162 | }
163 | }
164 |
165 | //Exit controls screen if EXIT_BUTTON is pressed
166 | if (onPress(EXIT_BUTTON) && !(*editing_control))
167 | {
168 | playSound(LAND_SOUND);
169 | freeVars();
170 | return RESET;
171 | }
172 |
173 | // -----------------------------------------------------------------
174 |
175 | // Rendering --------------------------------------------------------
176 |
177 | drawTexture(Texture_Score, getScoreDrawX(MODE), getScoreDrawY(MODE), 1.0);
178 | drawTexture(Texture_Level, 312, 115, 1.0);
179 | drawTexture(Texture_Lines, 312, 189, 1.0);
180 | drawTexture(Texture_Next, 318 - (*nextText_Width / 2), 282 - (*nextText_Height / 2), 1.0);
181 | drawTexture(Texture_Controls, 12, 14, 0.9);
182 | drawTexture(Texture_Cursor, 14, 14 + ((*selected_control) * 3 * (FONT_HEIGHT + STRING_GAP)) * 0.9, 1.0);
183 |
184 | // ------------------------------------------------------------------
185 |
186 | return CONTROLS_SCREEN;
187 |
188 | }
189 |
190 | //Function for updating the onPress and onHold status of a control
191 | void updateControls()
192 | {
193 |
194 | //Iterate over all controls
195 | for (int i = 0; i < NUM_OF_CONTROLS; i++)
196 | {
197 |
198 | //Update logic
199 | //If button[i] is currently pushed
200 | if (globalInstance->keys[globalInstance->controls[i].button])
201 | {
202 |
203 | //If this is the first frame of the button being pushed
204 | if (globalInstance->controls[i].onHold == false)
205 | {
206 |
207 | //onHold and onPress are true
208 | globalInstance->controls[i].onPress = true;
209 | globalInstance->controls[i].onHold = true;
210 |
211 | }
212 | else
213 | //onPress is only true for a single frame
214 | globalInstance->controls[i].onPress = false;
215 |
216 | // Hide cursor if a button is pressed and the cursor is currently visible
217 | if (SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE)
218 | SDL_ShowCursor(SDL_DISABLE);
219 |
220 | }
221 | else
222 | {
223 |
224 | //If button is not being pushed, onHold and onPress are both false
225 | globalInstance->controls[i].onHold = false;
226 | globalInstance->controls[i].onPress = false;
227 |
228 | }
229 |
230 | }
231 |
232 | }
233 |
234 | // Function for updating the onKeyPress and onKeyHold status of a key
235 | void updateKeys()
236 | {
237 |
238 | // Iterate over all keys
239 | for (int i = 0; i < SDL_NUM_SCANCODES; i++)
240 | {
241 |
242 | // Update logic
243 | // If key[i] is currently pushed
244 | if (globalInstance->keys[i])
245 | {
246 |
247 | // If this is the first frame of the key being pushed
248 | if (globalInstance->onKeyHold[i] == false)
249 | {
250 |
251 | // onKeyPress and onKeyHold are both true
252 | globalInstance->onKeyPress[i] = true;
253 | globalInstance->onKeyHold[i] = true;
254 |
255 | }
256 | else // onKeyPress is only true for a single frame
257 | globalInstance->onKeyPress[i] = false;
258 |
259 | }
260 | else
261 | {
262 |
263 | // If key is not being pushed, onKeyHold and onKeyPress are both false
264 | globalInstance->onKeyHold[i] = false;
265 | globalInstance->onKeyPress[i] = false;
266 |
267 | }
268 |
269 | }
270 |
271 | }
272 |
273 | //Get the key that is currently pressed
274 | //Returns -1 if no valid keys are pressed
275 | //If multiple are pressed, it returns the one that comes earliest in the SCANCODE list
276 | int getPressedKey()
277 | {
278 |
279 | //Valid buttons are SDL_SCANCODE_A to SDL_SCANCODE_NUMLOCKCLEAR
280 | for (int i = SDL_SCANCODE_A; i <= SDL_SCANCODE_NUMLOCKCLEAR; i++)
281 | if (globalInstance->keys[i])
282 | return i;
283 | return -1;
284 |
285 | }
286 |
287 | //Check if the specified key ID is out of range of valid keys
288 | bool invalidKey(int key)
289 | {
290 |
291 | return (key < SDL_SCANCODE_A || key > SDL_SCANCODE_NUMLOCKCLEAR);
292 |
293 | }
--------------------------------------------------------------------------------
/src/controls.h:
--------------------------------------------------------------------------------
1 | #ifndef CONTROLS_H_
2 | #define CONTROLS_H_
3 |
4 | #include "MGF.h"
5 |
6 | unsigned short controlsScreen(piece** Piece);
7 | void updateControls();
8 | int getPressedKey();
9 | bool invalidKey(int key);
10 | void updateKeys();
11 |
12 | #endif
--------------------------------------------------------------------------------
/src/credits.c:
--------------------------------------------------------------------------------
1 | #include "MGF.h"
2 |
3 | unsigned short creditsScreen(piece** Piece)
4 | {
5 |
6 | //Variables
7 | DECLARE_VARIABLE(int, nextText_Width, 0);
8 | DECLARE_VARIABLE(int, nextText_Height, 0);
9 | DECLARE_VARIABLE(bool, firstLoop, true);
10 |
11 | //Textures
12 | static SDL_Texture* Texture_Score; declare_HUD_Text(&Texture_Score, SCORE_TEXT);
13 | static SDL_Texture* Texture_Level; declare_HUD_Text(&Texture_Level, LEVEL_TEXT);
14 | static SDL_Texture* Texture_Lines; declare_HUD_Text(&Texture_Lines, LINES_TEXT);
15 | static SDL_Texture* Texture_Next; declare_Piece_Text(&Texture_Next, *Piece, false);
16 | static SDL_Texture* Texture_Credits; declare_HUD_Text(&Texture_Credits, CREDITS_TEXT);
17 |
18 | //Get the width and height of the Next Piece texture during the first frame
19 | if (*firstLoop == true)
20 | {
21 |
22 | SDL_QueryTexture(Texture_Next, NULL, NULL, nextText_Width, nextText_Height);
23 | *firstLoop = false;
24 |
25 | }
26 |
27 | // Control Logic ---------------------------------------------------
28 |
29 | //Exit credits screen if EXIT_BUTTON or SELECT_BUTTON is pressed
30 | if (onPress(EXIT_BUTTON) || onPress(SELECT_BUTTON))
31 | {
32 | playSound(LAND_SOUND);
33 | freeVars();
34 | return RESET;
35 | }
36 |
37 | // Rendering --------------------------------------------------------
38 |
39 | drawTexture(Texture_Score, getScoreDrawX(MODE), getScoreDrawY(MODE), 1.0);
40 | drawTexture(Texture_Level, 312, 115, 1.0);
41 | drawTexture(Texture_Lines, 312, 189, 1.0);
42 | drawTexture(Texture_Next, 318 - (*nextText_Width / 2), 282 - (*nextText_Height / 2), 1.0);
43 | drawTexture(Texture_Credits, SPRITE_WIDTH * 2, SPRITE_HEIGHT * 2, 1.0);
44 |
45 | // ------------------------------------------------------------------
46 |
47 | return CREDITS_SCREEN;
48 |
49 | }
--------------------------------------------------------------------------------
/src/credits.h:
--------------------------------------------------------------------------------
1 | #ifndef CREDITS_H_
2 | #define CREDITS_H_
3 |
4 | #include "MGF.h"
5 |
6 | unsigned short creditsScreen(piece** Piece);
7 |
8 | #endif
--------------------------------------------------------------------------------
/src/draw.c:
--------------------------------------------------------------------------------
1 | #include "MGF.h"
2 |
3 | //Return the "Red" value for a given color
4 | unsigned short getColor_R(unsigned short color)
5 | {
6 |
7 | if (color == GRAY)
8 | return 198;
9 | else if (color == WHITE)
10 | return 250;
11 | else if (color == YELLOW)
12 | return 253;
13 | else if (color == GREEN)
14 | return 119;
15 | else if (color == CYAN)
16 | return 220;
17 | else if (color == MAGENTA)
18 | return 178;
19 | else if (color == ORANGE)
20 | return 255;
21 | else if (color == BLUE)
22 | return 119;
23 | else if (color == RED)
24 | return 194;
25 | else if (color == BLACK)
26 | return 0;
27 | else if (color == DARK_GRAY)
28 | return 100;
29 | else
30 | return -1;
31 |
32 | }
33 |
34 | //Return the "Green" value for a given color
35 | unsigned short getColor_G(unsigned short color)
36 | {
37 |
38 | if (color == GRAY)
39 | return 203;
40 | else if (color == WHITE)
41 | return 250;
42 | else if (color == YELLOW)
43 | return 253;
44 | else if (color == GREEN)
45 | return 221;
46 | else if (color == CYAN)
47 | return 255;
48 | else if (color == MAGENTA)
49 | return 60;
50 | else if (color == ORANGE)
51 | return 179;
52 | else if (color == BLUE)
53 | return 157;
54 | else if (color == RED)
55 | return 59;
56 | else if (color == BLACK)
57 | return 0;
58 | else if (color == DARK_GRAY)
59 | return 100;
60 | else
61 | return -1;
62 |
63 | }
64 |
65 | //Return the "Blue" value for a given color
66 | unsigned short getColor_B(unsigned short color)
67 | {
68 |
69 | if (color == GRAY)
70 | return 206;
71 | else if (color == WHITE)
72 | return 250;
73 | else if (color == YELLOW)
74 | return 150;
75 | else if (color == GREEN)
76 | return 119;
77 | else if (color == CYAN)
78 | return 251;
79 | else if (color == MAGENTA)
80 | return 121;
81 | else if (color == ORANGE)
82 | return 71;
83 | else if (color == BLUE)
84 | return 202;
85 | else if (color == RED)
86 | return 34;
87 | else if (color == BLACK)
88 | return 0;
89 | else if (color == DARK_GRAY)
90 | return 100;
91 | else
92 | return -1;
93 |
94 | }
95 |
96 | //Get the number of digits in an integer
97 | unsigned short getIntLength(int num)
98 | {
99 |
100 | unsigned short returnValue = 0;
101 | bool isNegative = false;
102 |
103 | // If the value is negative, make it positive...
104 | if (num < 0)
105 | {
106 |
107 | num = num * -1;
108 | isNegative = true;
109 |
110 | }
111 |
112 | if (num == 0)
113 | returnValue = 1;
114 | else
115 | returnValue = (unsigned short)(SDL_floor(SDL_log10(abs(num))) + 1);
116 |
117 | // ...And then add an extra 1 to the returnValue to account for the minus sign.
118 | if (isNegative)
119 | returnValue += 1;
120 |
121 | return returnValue;
122 |
123 | }
124 |
125 | //Print string to a texture
126 | void printToTexture(char* string, SDL_Texture* dstTexture, int X, int Y, float multiplier, unsigned short color)
127 | {
128 |
129 | //Save current rendering target
130 | SDL_Texture* currentTarget = SDL_GetRenderTarget(globalInstance->renderer);
131 |
132 | //Set rendering target
133 | SDL_SetRenderTarget(globalInstance->renderer, dstTexture);
134 |
135 | //Rectangles used for copying
136 | SDL_Rect srcR;
137 | SDL_Rect dstR;
138 |
139 | //Change color of string_SS
140 | SDL_SetTextureColorMod(globalInstance->stringSS, getColor_R(color), getColor_G(color), getColor_B(color));
141 |
142 | //Iterate over whole string
143 | size_t string_length = SDL_strlen(string);
144 | for (size_t i = 0; i < string_length; i++)
145 | {
146 |
147 | srcR.w = FONT_WIDTH;
148 | srcR.h = FONT_HEIGHT;
149 |
150 | //Copy the corresponding character to the dstTexture
151 | //Drawable characters
152 | if (string[i] >= ' ' && string[i] <= '~')
153 | {
154 |
155 | //Source rectangle
156 | srcR.x = (string[i] - ' ') % (globalInstance->stringSS_W / FONT_WIDTH) * FONT_WIDTH;
157 | srcR.y = (string[i] - ' ') / (globalInstance->stringSS_H / FONT_HEIGHT) * FONT_HEIGHT;
158 |
159 | }
160 | else //Un-drawable character will draw the "missing ?" character
161 | {
162 |
163 | srcR.x = ('~' - ' ' + 1) % (globalInstance->stringSS_W / FONT_WIDTH) * FONT_WIDTH;
164 | srcR.y = ('~' - ' ' + 1) / (globalInstance->stringSS_H / FONT_HEIGHT) * FONT_HEIGHT;
165 |
166 | }
167 |
168 | //Destination rectangle
169 | //Calculate correct positioning and sizing
170 | dstR.x = X + i * (STRING_GAP + FONT_WIDTH) * multiplier;
171 | dstR.y = Y;
172 | dstR.w = FONT_WIDTH * multiplier;
173 | dstR.h = FONT_HEIGHT * multiplier;
174 |
175 | //Copy the letter from stringSS to dstTexture
176 | SDL_RenderCopy(globalInstance->renderer, globalInstance->stringSS, &srcR, &dstR);
177 |
178 | }
179 |
180 | //Reset string_SS color to white
181 | SDL_SetTextureColorMod(globalInstance->stringSS, 255, 255, 255);
182 |
183 | //Reset rendering target
184 | SDL_SetRenderTarget(globalInstance->renderer, currentTarget);
185 |
186 | }
187 |
188 | //Print integer to a texture
189 | void intToTexture(int num, SDL_Texture* dstTexture, int X, int Y, float multiplier, unsigned short color)
190 | {
191 |
192 | //Stores the number of digits in the integer
193 | unsigned short length;
194 |
195 | if (num == 0)
196 | {
197 |
198 | //Printing 0 sometimes goes wrong with my algorithm, so this is just a
199 | //simple way to fix that
200 | //No idea why this happens
201 | printToTexture("0", dstTexture, X, Y, multiplier, color);
202 | return;
203 |
204 | }
205 | else //Get the number of digits in the integer
206 | length = getIntLength(num);
207 |
208 | //Convert integer to a string
209 | char* string = SDL_malloc((length + 1) * sizeof(*string));
210 | if (string != NULL)
211 | {
212 |
213 | //Store each digit from num into string in reverse order
214 | unsigned short count = 0;
215 | while (num)
216 | {
217 |
218 | string[length - count - 1] = '0' + (num % 10);
219 |
220 | count = count + 1;
221 | num /= 10;
222 |
223 | }
224 |
225 | //End the string with a end-character
226 | string[length] = '\0';
227 |
228 | }
229 |
230 | //Now print that string
231 | printToTexture(string, dstTexture, X, Y, multiplier, color);
232 |
233 | //Delete string from memory
234 | SDL_free(string);
235 |
236 | }
237 |
238 | //Draw a sprite to a texture
239 | void drawToTexture(int SpriteID, SDL_Texture* dstTexture, int X, int Y, float multiplier, Uint8 color)
240 | {
241 |
242 | //Save current rendering target
243 | SDL_Texture* currentTarget = SDL_GetRenderTarget(globalInstance->renderer);
244 |
245 | //Set rendering target
246 | SDL_SetRenderTarget(globalInstance->renderer, dstTexture);
247 |
248 | //Rectangles used for copying
249 | SDL_Rect srcR;
250 | SDL_Rect dstR;
251 |
252 | //Change color of game_SS
253 | SDL_SetTextureColorMod(globalInstance->gameSS, getColor_R(color), getColor_G(color), getColor_B(color));
254 |
255 | srcR.w = SPRITE_WIDTH;
256 | srcR.h = SPRITE_HEIGHT;
257 |
258 | //Copy the corresponding sprite to the dstTexture
259 | //Make sure SpriteID is within bounds of the number of sprites
260 | if (SpriteID < ((globalInstance->gameSS_W / SPRITE_WIDTH) * (globalInstance->gameSS_H / SPRITE_HEIGHT)))
261 | {
262 |
263 | //Source rectangle
264 | srcR.x = SpriteID % (globalInstance->gameSS_W / SPRITE_WIDTH) * SPRITE_WIDTH;
265 | srcR.y = SpriteID / (globalInstance->gameSS_H / SPRITE_HEIGHT) * SPRITE_HEIGHT;
266 |
267 | //Destination rectangle
268 | //Calculate correct positioning and sizing
269 | dstR.x = X;
270 | dstR.y = Y;
271 | dstR.w = SPRITE_WIDTH * multiplier;
272 | dstR.h = SPRITE_HEIGHT * multiplier;
273 |
274 | //Copy the letter from gameSS to dstTexture
275 | SDL_RenderCopy(globalInstance->renderer, globalInstance->gameSS, &srcR, &dstR);
276 |
277 | }
278 | else //SpriteID's outside valid bounds will draw the "missing ?" character
279 | {
280 |
281 | char str[2];
282 | str[0] = '~' + 1;
283 | str[1] = '\0';
284 |
285 | printToTexture(str, dstTexture, X, Y, multiplier, color);
286 |
287 | }
288 |
289 | //Reset game_SS color to white
290 | SDL_SetTextureColorMod(globalInstance->gameSS, 255, 255, 255);
291 |
292 | //Reset rendering target
293 | SDL_SetRenderTarget(globalInstance->renderer, currentTarget);
294 |
295 | }
296 |
297 | //Draw a rectangle, filled or unfilled, with the passed sprite to a texture
298 | void drawRectangle(int spriteID, SDL_Texture* dstTexture, int X, int Y, int W, int H, int color, bool fill)
299 | {
300 |
301 | for (int i = 0; i < W; i++)
302 | {
303 |
304 | for (int j = 0; j < H; j++)
305 | {
306 |
307 | if (fill)
308 | drawToTexture(spriteID, dstTexture, X + i * FONT_WIDTH, Y + j * FONT_HEIGHT, 1.0, color);
309 | else
310 | if ((i == 0 || i == W - 1) || (j == 0 || j == H - 1))
311 | drawToTexture(spriteID, dstTexture, X + i * FONT_WIDTH, Y + j * FONT_HEIGHT, 1.0, color);
312 |
313 | }
314 |
315 | }
316 |
317 | }
318 |
319 | //Draw a solid rectangle of the given color, doesn't use sprites
320 | void drawSimpleRect(SDL_Texture* dstTexture, int x, int y, int width, int height, int color, int opacity)
321 | {
322 |
323 | //Save current rendering target
324 | SDL_Texture* currentTarget = SDL_GetRenderTarget(globalInstance->renderer);
325 |
326 | //Save the current rendering color
327 | Uint8 currR = 0;
328 | Uint8 currB = 0;
329 | Uint8 currG = 0;
330 | Uint8 currA = 0;
331 | SDL_GetRenderDrawColor(globalInstance->renderer, &currR, &currG, &currB, &currA);
332 |
333 | //Set rendering target
334 | SDL_SetRenderTarget(globalInstance->renderer, dstTexture);
335 |
336 | //Set rendering color and opacity
337 | SDL_SetRenderDrawColor(globalInstance->renderer,getColor_R(color),getColor_G(color),getColor_B(color),255 * (opacity / 100.0));
338 |
339 | //Draw rectangle
340 | SDL_Rect rect = { .x = x, .y = y, .w = width, .h = height };
341 | SDL_RenderFillRect(globalInstance->renderer, &rect);
342 |
343 | //Reset rendering color
344 | SDL_SetRenderDrawColor(globalInstance->renderer, currR, currG, currB, currA);
345 |
346 | //Reset rendering target
347 | SDL_SetRenderTarget(globalInstance->renderer, currentTarget);
348 |
349 | }
350 |
351 | //Return the length, in pixels, of a string
352 | int getStringLength(char* str, float multiplier)
353 | {
354 |
355 | int length = SDL_strlen(str);
356 |
357 | return multiplier * (length * FONT_WIDTH + (length - 1) * STRING_GAP);
358 |
359 | }
360 |
361 | //Return the length, in pixels, of an Int
362 | int getIntStringLength(int num, float multiplier)
363 | {
364 |
365 | //Convert integer to string
366 | char* str = SDL_calloc(getIntLength(num) + 1, sizeof(char));
367 | SDL_itoa(num, str, 10);
368 |
369 | //Get length of string
370 | int stringLength = getStringLength(str, multiplier);
371 |
372 | // Free string
373 | SDL_free(str);
374 |
375 | return stringLength;
376 |
377 | }
378 |
379 | //Clear a texture
380 | void clearTexture(SDL_Texture* texture)
381 | {
382 |
383 | //Save current rendering target
384 | SDL_Texture* currentTarget = SDL_GetRenderTarget(globalInstance->renderer);
385 | //Save current rendering draw color
386 | Uint8 currentR;
387 | Uint8 currentG;
388 | Uint8 currentB;
389 | Uint8 currentA;
390 | SDL_GetRenderDrawColor(globalInstance->renderer, ¤tR, ¤tG, ¤tB, ¤tA);
391 |
392 | //Clear texture with "clear" color
393 | SDL_SetRenderDrawColor(globalInstance->renderer, 0, 0, 0, 0);
394 | SDL_SetRenderTarget(globalInstance->renderer, texture);
395 | SDL_RenderClear(globalInstance->renderer);
396 |
397 | //Reset rendering draw color
398 | SDL_SetRenderDrawColor(globalInstance->renderer, currentR, currentG, currentB, currentA);
399 | //Reset rendering target
400 | SDL_SetRenderTarget(globalInstance->renderer, currentTarget);
401 |
402 | }
403 |
404 | // Clear a texture with a given color
405 | void clearTextureWithColor(SDL_Texture* texture, Uint8 colorR, Uint8 colorG, Uint8 colorB, Uint8 colorA)
406 | {
407 |
408 | //Save current rendering target
409 | SDL_Texture* currentTarget = SDL_GetRenderTarget(globalInstance->renderer);
410 | //Save current rendering draw color
411 | Uint8 currentR;
412 | Uint8 currentG;
413 | Uint8 currentB;
414 | Uint8 currentA;
415 | SDL_GetRenderDrawColor(globalInstance->renderer, ¤tR, ¤tG, ¤tB, ¤tA);
416 |
417 | //Clear texture with given color
418 | SDL_SetRenderDrawColor(globalInstance->renderer, colorR, colorG, colorB, colorA);
419 | SDL_SetRenderTarget(globalInstance->renderer, texture);
420 | SDL_RenderClear(globalInstance->renderer);
421 |
422 | //Reset rendering draw color
423 | SDL_SetRenderDrawColor(globalInstance->renderer, currentR, currentG, currentB, currentA);
424 | //Reset rendering target
425 | SDL_SetRenderTarget(globalInstance->renderer, currentTarget);
426 |
427 | }
428 |
429 | //Create a texture of the given width and height
430 | SDL_Texture* createTexture(int width, int height)
431 | {
432 |
433 | //Save current rendering target
434 | SDL_Texture* currentTarget = SDL_GetRenderTarget(globalInstance->renderer);
435 |
436 | SDL_Rect dstR = { .x = 0, .y = 0, .w = width, .h = height };
437 |
438 | SDL_Texture* texture;
439 | SDL_Renderer* renderer = globalInstance->renderer;
440 | texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, dstR.w, dstR.h);
441 |
442 | //For some reason, you have to clear the texture
443 | clearTexture(texture);
444 |
445 | //Reset rendering target
446 | SDL_SetRenderTarget(globalInstance->renderer, currentTarget);
447 |
448 | //Make it so stuff can render on top of it
449 | SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
450 |
451 | return texture;
452 |
453 | }
454 |
455 | //Draw texture to current rendering target
456 | void drawTexture(SDL_Texture* texture, int X, int Y, float multiplier)
457 | {
458 |
459 | //Rect used to draw the texture
460 | SDL_Rect dstR = { .x = X, .y = Y, .w = 0, .h = 0 };
461 | //Get the width and height of the texture
462 | SDL_QueryTexture(texture, NULL, NULL, &dstR.w, &dstR.h);
463 |
464 | //Resize texture appropriately
465 | dstR.w *= multiplier;
466 | dstR.h *= multiplier;
467 |
468 | //Copy the texture to the current rendering target
469 | SDL_RenderCopy(globalInstance->renderer, texture, NULL, &dstR);
470 |
471 | }
472 |
473 | //Draw a piece to the screen
474 | //Piece will be drawn with the top left of the Piece corresponding to X,Y
475 | void drawPiece(piece Piece, SDL_Texture* dstTexture, unsigned short X, unsigned short Y)
476 | {
477 |
478 | //For every block in the piece
479 | for (unsigned short i = 0; i < Piece.numOfBlocks; i++) {
480 |
481 | unsigned int SpriteID = BLOCK_SPRITE_ID;
482 | int block_X = X + SPRITE_WIDTH * (Piece.blocks[i].X - Piece.minX);
483 | int block_Y = Y + SPRITE_HEIGHT * (Piece.blocks[i].Y - Piece.minY);
484 |
485 | drawToTexture(SpriteID, dstTexture, block_X, block_Y, 1.0, Piece.color);
486 |
487 | }
488 |
489 | }
490 |
491 | //Create a texture from the given piece
492 | SDL_Texture* createPieceTexture(piece Piece, bool drawCenterDot)
493 | {
494 |
495 | SDL_Rect DestR;
496 | DestR.w = SPRITE_WIDTH * Piece.width;
497 | DestR.h = SPRITE_HEIGHT * Piece.height;
498 |
499 | SDL_Texture* texture;
500 | texture = createTexture(DestR.w, DestR.h);
501 |
502 | //For some reason, you have to clear the texture
503 | clearTexture(texture);
504 |
505 | //Pretty simple, we just draw the piece to the texture
506 | drawPiece(Piece, texture, 0, 0);
507 |
508 | // Draw center dot if specified
509 | if (drawCenterDot == true)
510 | {
511 |
512 | int centerX = Piece.centerBlock->X - Piece.minX;
513 | int centerY = Piece.centerBlock->Y - Piece.minY;
514 |
515 |
516 | int drawX = (SPRITE_WIDTH / 2) + (SPRITE_WIDTH * centerX) - 2;
517 | int drawY = (SPRITE_HEIGHT / 2) + (SPRITE_HEIGHT * centerY) - 2;
518 | drawSimpleRect(texture, drawX, drawY, 4, 4, DARK_GRAY, 100);
519 |
520 | }
521 |
522 | return texture;
523 |
524 | }
525 |
526 | // Function for converting a string to all upper case
527 | void stringToUpper(char* str)
528 | {
529 |
530 | for (unsigned short i = 0; i < SDL_strlen(str); i++)
531 | str[i] = SDL_toupper(str[i]);
532 |
533 | }
--------------------------------------------------------------------------------
/src/draw.h:
--------------------------------------------------------------------------------
1 | #ifndef DRAW_H_
2 | #define DRAW_H_
3 |
4 | #include "MGF.h"
5 |
6 | unsigned short getColor_R(unsigned short color);
7 | unsigned short getColor_G(unsigned short color);
8 | unsigned short getColor_B(unsigned short color);
9 | unsigned short getIntLength(int num);
10 | void printToTexture(char* string, SDL_Texture* dstTexture, int X, int Y, float multiplier, unsigned short color);
11 | void intToTexture(int num, SDL_Texture* dstTexture, int X, int Y, float multiplier, unsigned short color);
12 | void drawToTexture(int SpriteID, SDL_Texture* dstTexture, int X, int Y, float multiplier, Uint8 color);
13 | void drawRectangle(int spriteID, SDL_Texture* dstTexture, int X, int Y, int W, int H, int color, bool fill);
14 | void drawSimpleRect(SDL_Texture* dstTexture, int x, int y, int width, int height, int color, int opacity);
15 | int getStringLength(char* str, float multiplier);
16 | int getIntStringLength(int num, float multiplier);
17 | void clearTexture(SDL_Texture* texture);
18 | void clearTextureWithColor(SDL_Texture* texture, Uint8 colorR, Uint8 colorG, Uint8 colorB, Uint8 colorA);
19 | SDL_Texture* createTexture(int width, int height);
20 | void drawTexture(SDL_Texture* texture, int X, int Y, float multiplier);
21 | void drawPiece(piece Piece, SDL_Texture* dstTexture, unsigned short X, unsigned short Y);
22 | SDL_Texture* createPieceTexture(piece Piece, bool drawCenterDot);
23 | void stringToUpper(char* str);
24 |
25 | #endif
--------------------------------------------------------------------------------
/src/file.h:
--------------------------------------------------------------------------------
1 | #ifndef FILE_H_
2 | #define FILE_H_
3 |
4 | #include "MGF.h"
5 |
6 | unsigned short getLineCount(char* fileName);
7 | bool fileExists(char* fileName);
8 | void createFile(char* file_path);
9 | void createProgressFile();
10 | void createCustomTopFile();
11 | void createTopFile();
12 | void createOptions();
13 | void createWindowFile();
14 | void createControls();
15 | char* getNameAtLine(const char* file_path, int line);
16 | int getFileValue(const char* file_path, const char* name);
17 | bool brokenProgress();
18 | bool brokenOptions();
19 | bool brokenControls();
20 | bool brokenWindowFile();
21 | void saveToFile(const char* file_path, const char* str, int value);
22 | unsigned int loadTop(unsigned short size, bool inCustomMode);
23 | unsigned int loadProgress();
24 | void saveTop(unsigned int score, unsigned short size, bool inCustomMode);
25 | void saveProgress();
26 | void saveWindowSettings();
27 | void createNameFile();
28 | bool brokenNameFile();
29 | void saveName(char* name);
30 | char* loadName();
31 | void createHostPortFile();
32 | bool brokenHostPortFile();
33 | void saveHostPort(char* portString);
34 | char* loadHostPort();
35 | void createIPFile();
36 | bool brokenIPFile();
37 | void saveIP(char* ipString);
38 | char* loadIP();
39 | char* loadConnectPort();
40 | void saveConnectPort(char* portString);
41 | bool brokenConnectPort();
42 | void createConnectPortFile();
43 |
44 | #endif
--------------------------------------------------------------------------------
/src/generate.c:
--------------------------------------------------------------------------------
1 | #include "MGF.h"
2 |
3 | // Function for finding and creating the centerBlock for a piece
4 | void createCenterBlock(piece* piece)
5 | {
6 |
7 | piece->centerBlock = SDL_calloc(1, sizeof(block));
8 | piece->centerBlock->X = piece->minX + SDL_floor((float)piece->width / (float)2);
9 | piece->centerBlock->Y = piece->minY + SDL_floor((float)piece->height / (float)2);
10 |
11 | }
12 |
13 | //Function for copying all blocks from one piece to another
14 | //Make sure piece2 has allocated enough space to store all blocks
15 | void copyBlocks(piece* piece1, piece* piece2)
16 | {
17 |
18 | //Copy over all blocks
19 | for (unsigned short i = 0; i < piece1->numOfBlocks; i++)
20 | {
21 |
22 | piece2->blocks[i].X = piece1->blocks[i].X;
23 | piece2->blocks[i].Y = piece1->blocks[i].Y;
24 |
25 | }
26 |
27 | // Copy over the coordinates for the centerBlock
28 | piece2->centerBlock->X = piece1->centerBlock->X;
29 | piece2->centerBlock->Y = piece1->centerBlock->Y;
30 |
31 | }
32 |
33 | //Copy a piece
34 | void copyPiece(piece* piece1, piece* piece2)
35 | {
36 |
37 | //Copy all piece attributes
38 | piece2->color = piece1->color;
39 | piece2->height = piece1->height;
40 | piece2->width = piece1->width;
41 | piece2->minX = piece1->minX;
42 | piece2->minY = piece1->minY;
43 | piece2->numOfBlocks = piece1->numOfBlocks;
44 |
45 | //Copy the blocks from the piece
46 | copyBlocks(piece1, piece2);
47 |
48 | }
49 |
50 | //Clear memory allocated by a Piece
51 | void delPiece(piece** Piece)
52 | {
53 |
54 | // Delete the centerBlock
55 | SDL_free((*Piece)->centerBlock);
56 | (*Piece)->centerBlock = NULL;
57 |
58 | //Delete blocks
59 | SDL_free((*Piece)->blocks);
60 | (*Piece)->blocks = NULL;
61 |
62 | //Delete Piece itself
63 | SDL_free(*Piece);
64 | *Piece = NULL;
65 |
66 | }
67 |
68 | //Check if a block exists in a specified direction from an existing block in a piece
69 | bool isOccupied(piece* Piece, unsigned short blockID, unsigned short direction)
70 | {
71 |
72 | if (direction == UP)
73 | {
74 |
75 | for (unsigned short i = 0; i < Piece->numOfBlocks; i++)
76 | {
77 |
78 | if (Piece->blocks[i].X == Piece->blocks[blockID].X &&
79 | Piece->blocks[i].Y == Piece->blocks[blockID].Y - 1)
80 | return true;
81 |
82 | }
83 |
84 | }
85 | else if (direction == RIGHT)
86 | {
87 |
88 | for (unsigned short i = 0; i < Piece->numOfBlocks; i++)
89 | {
90 |
91 | if (Piece->blocks[i].X == Piece->blocks[blockID].X + 1 &&
92 | Piece->blocks[i].Y == Piece->blocks[blockID].Y)
93 | return true;
94 |
95 | }
96 |
97 | }
98 | else if (direction == DOWN)
99 | {
100 |
101 | for (unsigned short i = 0; i < Piece->numOfBlocks; i++)
102 | {
103 |
104 | if (Piece->blocks[i].X == Piece->blocks[blockID].X &&
105 | Piece->blocks[i].Y == Piece->blocks[blockID].Y + 1)
106 | return true;
107 |
108 | }
109 |
110 | }
111 | else if (direction == LEFT)
112 | {
113 |
114 | for (unsigned short i = 0; i < Piece->numOfBlocks; i++)
115 | {
116 |
117 | if (Piece->blocks[i].X == Piece->blocks[blockID].X - 1 &&
118 | Piece->blocks[i].Y == Piece->blocks[blockID].Y)
119 | return true;
120 |
121 | }
122 |
123 | }
124 |
125 | return false;
126 |
127 | }
128 |
129 | //Check if a specified block in a piece is surrounded by other blocks in the piece
130 | bool isSurrounded(piece* Piece, unsigned short blockID)
131 | {
132 |
133 | bool occupied[4];
134 |
135 | //We can basically just check if each direction from the block is occupied
136 | occupied[UP] = isOccupied(Piece, blockID, UP);
137 | occupied[RIGHT] = isOccupied(Piece, blockID, RIGHT);
138 | occupied[DOWN] = isOccupied(Piece, blockID, DOWN);
139 | occupied[LEFT] = isOccupied(Piece, blockID, LEFT);
140 |
141 | if (occupied[UP] == true && occupied[RIGHT] == true && occupied[DOWN] == true && occupied[LEFT] == true)
142 | return true;
143 | else
144 | return false;
145 |
146 | }
147 |
148 | //Get the width, in blocks, of a piece
149 | unsigned short calcWidth(piece* Piece)
150 | {
151 |
152 | //Initialize minX and maX to the X value of our first block
153 | signed short minX = Piece->blocks[0].X;
154 | signed short maxX = Piece->blocks[0].X;
155 |
156 | //Go through all blocks to find the min and max X values
157 | for (unsigned short i = 0; i < Piece->numOfBlocks; i++)
158 | {
159 |
160 | if (Piece->blocks[i].X < minX)
161 | minX = Piece->blocks[i].X;
162 | else if (Piece->blocks[i].X > maxX)
163 | maxX = Piece->blocks[i].X;
164 |
165 | }
166 |
167 | Piece->minX = minX;
168 |
169 | return ((maxX - minX) + 1);
170 |
171 | }
172 |
173 | //Get the height, in blocks, of a piece
174 | unsigned short calcHeight(piece* Piece)
175 | {
176 |
177 | //Initialize minY and maxY to the Y value of our first block
178 | signed short minY = Piece->blocks[0].Y;
179 | signed short maxY = Piece->blocks[0].Y;
180 |
181 | //Go through all blocks to find the min and max Y values
182 | for (unsigned short i = 0; i < Piece->numOfBlocks; i++)
183 | {
184 |
185 | if (Piece->blocks[i].Y < minY)
186 | minY = Piece->blocks[i].Y;
187 | else if (Piece->blocks[i].Y > maxY)
188 | maxY = Piece->blocks[i].Y;
189 |
190 | }
191 |
192 | Piece->minY = minY;
193 |
194 | return ((maxY - minY) + 1);
195 |
196 | }
197 |
198 | //Function for making a piece more random. This should be called on a pseudo-random piece created by generateGamePiece().
199 | piece* makePieceMoreRandom(piece* Piece)
200 | {
201 |
202 | //Only perform on pieces that are more than 2 block because there is only one unique polyomino for pieces of size 1 or 2.
203 | if (Piece->numOfBlocks > 2)
204 | {
205 |
206 | //Define mixing time (the number of times we run through the algorithm)
207 | //We're choosing to do it twice as many times as the number of blocks in our piece
208 | int mixingTime = 2 * Piece->numOfBlocks;
209 | int currentIteration = 0;
210 |
211 | //Go through the algorithm a mixingTime number of times
212 | while (currentIteration < mixingTime)
213 | {
214 |
215 | //Pick a random block in the piece
216 | int currentBlockIndex = MGF_rand() % Piece->numOfBlocks;
217 |
218 | //Get all of the blocks that neighbor this block
219 | int* occupiedNeighborBlocks = NULL;
220 | int numOccupiedNeighborBlocks = 0;
221 | for (unsigned short direction = 0; direction < 4; direction++)
222 | {
223 |
224 | int x = Piece->blocks[currentBlockIndex].X;
225 | int y = Piece->blocks[currentBlockIndex].Y;
226 |
227 | if (direction == 0) // Above
228 | y = y - 1;
229 | else if (direction == 1) // Right
230 | x = x + 1;
231 | else if (direction == 2) // Below
232 | y = y + 1;
233 | else if (direction == 3) // Left
234 | x = x - 1;
235 |
236 | for (unsigned short i = 0; i < Piece->numOfBlocks; i++)
237 | {
238 |
239 | if (x == Piece->blocks[i].X && y == Piece->blocks[i].Y)
240 | {
241 |
242 | if (occupiedNeighborBlocks == NULL)
243 | occupiedNeighborBlocks = SDL_calloc(1, sizeof(int));
244 | else
245 | occupiedNeighborBlocks = SDL_realloc(occupiedNeighborBlocks, (numOccupiedNeighborBlocks + 1) * sizeof(int));
246 | occupiedNeighborBlocks[numOccupiedNeighborBlocks] = i;
247 | numOccupiedNeighborBlocks = numOccupiedNeighborBlocks + 1;
248 |
249 | }
250 |
251 | }
252 |
253 | }
254 |
255 | //Get coords of unoocupied neighbor cells of all blocks other than our currentBlock
256 | int* unoccupiedX = SDL_calloc(1, sizeof(int));
257 | int* unoccupiedY = SDL_calloc(1, sizeof(int));
258 | int numUnoccupiedNeighbors = 0;
259 |
260 | //Go through every block
261 | for (unsigned short otherBlockIndex = 0; otherBlockIndex < Piece->numOfBlocks; otherBlockIndex++)
262 | {
263 |
264 | // For every block other than our current block
265 | if (otherBlockIndex != currentBlockIndex)
266 | {
267 |
268 | // Check all directions to see if it's neighboring cell is occupied
269 | for (unsigned short direction = 0; direction < 4; direction++)
270 | {
271 |
272 | int x = Piece->blocks[otherBlockIndex].X;
273 | int y = Piece->blocks[otherBlockIndex].Y;
274 | bool occupied = false;
275 | bool alreadyInList = false;
276 |
277 | if (direction == 0) // Above
278 | y = y - 1;
279 | else if (direction == 1) // Right
280 | x = x + 1;
281 | else if (direction == 2) // Below
282 | y = y + 1;
283 | else if (direction == 3) // Left
284 | x = x - 1;
285 |
286 |
287 | //Check if this coord is already in our list so that we don't have duplicates in our list
288 | if (numUnoccupiedNeighbors > 0)
289 | {
290 |
291 | for (unsigned short indexInList = 0; indexInList < numUnoccupiedNeighbors; indexInList++)
292 | {
293 |
294 | if (x == unoccupiedX[indexInList] && y == unoccupiedY[indexInList])
295 | {
296 |
297 | alreadyInList = true;
298 | break;
299 |
300 | }
301 |
302 | }
303 |
304 | }
305 |
306 | //If this coord is not already in the list
307 | if (alreadyInList == false)
308 | {
309 |
310 | //Check all other blocks to see if this spot is occupied by any of them
311 | for (unsigned short testBlockIndex = 0; testBlockIndex < Piece->numOfBlocks; testBlockIndex++)
312 | {
313 |
314 | if (testBlockIndex != otherBlockIndex)
315 | {
316 |
317 | if (x == Piece->blocks[testBlockIndex].X && y == Piece->blocks[testBlockIndex].Y)
318 | {
319 |
320 | occupied = true;
321 | break;
322 |
323 | }
324 |
325 | }
326 |
327 | }
328 |
329 | }
330 |
331 | //If the coord is not already in our list and it is not occupied by a block, that means it is an unoccupied neighbor
332 | //cell. So, store it in our list.
333 | if (occupied == false && alreadyInList == false)
334 | {
335 |
336 | if (numUnoccupiedNeighbors > 0)
337 | {
338 |
339 | unoccupiedX = SDL_realloc(unoccupiedX, (numUnoccupiedNeighbors + 1) * sizeof(int));
340 | unoccupiedY = SDL_realloc(unoccupiedY, (numUnoccupiedNeighbors + 1) * sizeof(int));
341 |
342 | }
343 |
344 | unoccupiedX[numUnoccupiedNeighbors] = x;
345 | unoccupiedY[numUnoccupiedNeighbors] = y;
346 |
347 | numUnoccupiedNeighbors = numUnoccupiedNeighbors + 1;
348 |
349 | }
350 |
351 | }
352 |
353 | }
354 |
355 | }
356 |
357 | //Save original coords of currentBlock
358 | int originalX = Piece->blocks[currentBlockIndex].X;
359 | int originalY = Piece->blocks[currentBlockIndex].Y;
360 |
361 | //Move currentBlock to a random unoccupied neighbor cell
362 | int randomUnoccupiedNeighborCell = MGF_rand() % numUnoccupiedNeighbors;
363 | Piece->blocks[currentBlockIndex].X = unoccupiedX[randomUnoccupiedNeighborCell];
364 | Piece->blocks[currentBlockIndex].Y = unoccupiedY[randomUnoccupiedNeighborCell];
365 |
366 | //Check if the move is valid
367 | bool moveIsValid = true;
368 | bool breakOut = false;
369 |
370 | //To check if the move is valid, we check if all neighboring blocks of the original location of currentBlock are still
371 | //connected to each other now that currentBlock has been moved
372 | if (numOccupiedNeighborBlocks > 1)
373 | {
374 |
375 | for (unsigned short i = 0; i < SDL_ceil((double)numOccupiedNeighborBlocks / (double)2); i++)
376 | {
377 |
378 | for (unsigned short j = i + 1; j < numOccupiedNeighborBlocks; j++)
379 | {
380 |
381 | if (areBlocksConnected(Piece, occupiedNeighborBlocks[i], occupiedNeighborBlocks[j], NULL, NULL) == false)
382 | {
383 |
384 | moveIsValid = false;
385 | breakOut = true;
386 |
387 | }
388 |
389 | if (breakOut)
390 | break;
391 |
392 | }
393 |
394 | if (breakOut)
395 | break;
396 |
397 | }
398 |
399 | }
400 |
401 | //If move is not valid, move it back to it's original spot
402 | if (moveIsValid == false)
403 | {
404 |
405 | Piece->blocks[currentBlockIndex].X = originalX;
406 | Piece->blocks[currentBlockIndex].Y = originalY;
407 |
408 | }
409 |
410 | //Free memory used by our lists
411 | SDL_free(unoccupiedX);
412 | SDL_free(unoccupiedY);
413 | SDL_free(occupiedNeighborBlocks);
414 |
415 | //Iterate our counter
416 | currentIteration = currentIteration + 1;
417 |
418 | }
419 |
420 | }
421 |
422 | return Piece;
423 |
424 | }
425 |
426 | //Generate a random piece of the given size
427 | piece* generateGamePiece(unsigned short size)
428 | {
429 |
430 | piece* Piece = NULL;
431 |
432 | Piece = SDL_malloc(sizeof(*Piece));
433 |
434 | if (Piece != NULL)
435 | {
436 |
437 | Piece->numOfBlocks = 0;
438 |
439 | //Pick random color for piece
440 | Piece->color = (MGF_rand() % (RED - YELLOW + 1)) + YELLOW;
441 |
442 | //Allocate memory for all blocks
443 | if (size > 0)
444 | Piece->blocks = SDL_malloc(size * sizeof(*Piece->blocks));
445 |
446 | if (Piece->blocks != NULL)
447 | {
448 |
449 | //Firstly, start off by placing a single block
450 | Piece->blocks[0].X = 0;
451 | Piece->blocks[0].Y = 0;
452 | Piece->numOfBlocks = Piece->numOfBlocks + 1;
453 |
454 | //There is a 1 in 7 chance of the game piece spawning as just a straight line
455 | //It is this way for game balancing reasons
456 | if (MGF_rand() % 7 == 3)
457 | {
458 |
459 | //When the game piece does spawn as a straight line, there is a 50/50 shot of whether it will
460 | //spawn horizontally or vertically
461 | if (MGF_rand() % 2 == 1)
462 | {
463 |
464 | //Go through each block that needs to be generated, simply creating each one directly to the
465 | //left of the last one
466 | //This is for the horizontal staight-line piece
467 | for (unsigned short i = 1; i < size; i++)
468 | {
469 |
470 | Piece->blocks[i].X = Piece->blocks[i - 1].X - 1;
471 | Piece->blocks[i].Y = Piece->blocks[i - 1].Y;
472 | Piece->numOfBlocks = Piece->numOfBlocks + 1;
473 |
474 | }
475 |
476 | }
477 | else
478 | {
479 |
480 | //Now go through each block that needs to be generated, and create each one immediately below
481 | //the previous one
482 | //This is for the vertical straight-line piece
483 | for (unsigned short i = 1; i < size; i++)
484 | {
485 |
486 | Piece->blocks[i].X = Piece->blocks[i - 1].X;
487 | Piece->blocks[i].Y = Piece->blocks[i - 1].Y + 1;
488 | Piece->numOfBlocks = Piece->numOfBlocks + 1;
489 |
490 | }
491 |
492 | }
493 |
494 | }
495 | else //This is the section that actually procedurally builds a random game piece 6 out of 7 times
496 | {
497 |
498 | //Go through each block that needs to be generated
499 | for (unsigned short i = 1; i < size; i++)
500 | {
501 |
502 | unsigned short randID;
503 | unsigned short randDirection;
504 |
505 | //Here, we randomly chose a block that we want our next block to build off of, making sure to
506 | //only pick a block that isnt already surrounded by blocks
507 | do
508 | randID = MGF_rand() % i; //Randomly choose a block to build off of
509 | while (isSurrounded(Piece, randID));
510 |
511 | //Here, we randomly choose a direction from our chosen block to build off from, making sure to
512 | //choose a direction that isnt already currently occupied
513 | do
514 | randDirection = MGF_rand() % 4;
515 | while (isOccupied(Piece, randID, randDirection));
516 |
517 | //Set the coordinates of our next block
518 | if (randDirection == UP)
519 | {
520 |
521 | Piece->blocks[i].X = Piece->blocks[randID].X;
522 | Piece->blocks[i].Y = Piece->blocks[randID].Y - 1;
523 | Piece->numOfBlocks = Piece->numOfBlocks + 1;
524 |
525 | }
526 | else if (randDirection == RIGHT)
527 | {
528 |
529 | Piece->blocks[i].X = Piece->blocks[randID].X + 1;
530 | Piece->blocks[i].Y = Piece->blocks[randID].Y;
531 | Piece->numOfBlocks = Piece->numOfBlocks + 1;
532 |
533 | }
534 | else if (randDirection == DOWN)
535 | {
536 |
537 | Piece->blocks[i].X = Piece->blocks[randID].X;
538 | Piece->blocks[i].Y = Piece->blocks[randID].Y + 1;
539 | Piece->numOfBlocks = Piece->numOfBlocks + 1;
540 |
541 | }
542 | else if (randDirection == LEFT)
543 | {
544 |
545 | Piece->blocks[i].X = Piece->blocks[randID].X - 1;
546 | Piece->blocks[i].Y = Piece->blocks[randID].Y;
547 | Piece->numOfBlocks = Piece->numOfBlocks + 1;
548 |
549 | }
550 |
551 | }
552 |
553 | //If we are not making a straight-line piece, make this piece more random
554 | Piece = makePieceMoreRandom(Piece);
555 |
556 | }
557 |
558 | //Calculate the width and height of the piece
559 | Piece->width = calcWidth(Piece);
560 | Piece->height = calcHeight(Piece);
561 |
562 | // Create the centerBlock
563 | createCenterBlock(Piece);
564 |
565 | }
566 |
567 | }
568 |
569 | return Piece;
570 |
571 | }
572 |
573 | // Function for converting a piece into a string of data to be sent to a server.
574 | // This function dynamically allocates memory for the return value. Ensure to free it with SDL_free().
575 | // Information is encoded in the format "COLOR|NUM_OF_BLOCKS|X|Y|X|Y|...|X|Y|"
576 | char* convertPieceToString(piece* Piece)
577 | {
578 |
579 | char* string = SDL_calloc(2, sizeof(char));
580 | string[0] = '0' + (char)Piece->color;
581 | string[1] = '\0';
582 |
583 | char* numOfBlocksString = SDL_calloc(getIntLength(Piece->numOfBlocks) + 1, sizeof(char));
584 | SDL_itoa(Piece->numOfBlocks, numOfBlocksString, 10);
585 | numOfBlocksString[getIntLength(Piece->numOfBlocks)] = '\0';
586 |
587 | int len = SDL_strlen(string) + SDL_strlen("|") + SDL_strlen(numOfBlocksString) + 1;
588 | string = SDL_realloc(string, len * sizeof(char));
589 | SDL_strlcat(string, "|", len);
590 | SDL_strlcat(string, numOfBlocksString, len);
591 |
592 | SDL_free(numOfBlocksString);
593 |
594 | for (unsigned short i = 0; i < Piece->numOfBlocks; i++)
595 | {
596 |
597 | int x = Piece->blocks[i].X;
598 | int y = Piece->blocks[i].Y;
599 |
600 | char* xString = SDL_calloc(getIntLength(x) + 1, sizeof(char));
601 | SDL_itoa(x, xString, 10);
602 | xString[getIntLength(x)] = '\0';
603 |
604 | char* yString = SDL_calloc(getIntLength(y) + 1, sizeof(char));
605 | SDL_itoa(y, yString, 10);
606 | yString[getIntLength(y)] = '\0';
607 |
608 | len = SDL_strlen(string) + SDL_strlen("|") + SDL_strlen(xString) + 1;
609 | string = SDL_realloc(string, len * sizeof(char));
610 | SDL_strlcat(string, "|", len);
611 | SDL_strlcat(string, xString, len);
612 |
613 | len = SDL_strlen(string) + SDL_strlen("|") + SDL_strlen(yString) + 1;
614 | string = SDL_realloc(string, len * sizeof(char));
615 | SDL_strlcat(string, "|", len);
616 | SDL_strlcat(string, yString, len);
617 |
618 | SDL_free(xString);
619 | SDL_free(yString);
620 |
621 | }
622 |
623 | len = SDL_strlen(string) + SDL_strlen("|") + 1;
624 | string = SDL_realloc(string, len * sizeof(char));
625 | SDL_strlcat(string, "|", len);
626 |
627 | return string;
628 |
629 | }
630 |
631 | // Function for creating a piece structure from a string of data received from the server
632 | // Information is encoded in the format "COLOR|NUM_OF_BLOCKS|X|Y|X|Y|...|X|Y|"
633 | piece* createPieceFromString(char* string)
634 | {
635 |
636 | // Allocate memory for the piece
637 | piece* Piece = SDL_calloc(1, sizeof(piece));
638 |
639 | int numValues = 0;
640 | char** values = extractStringsFromDelimitedBytes(string, SDL_strlen(string) + 1, &numValues, '|');
641 |
642 | int blockIndex = 0;
643 | for (unsigned short i = 0; i < numValues; i++)
644 | {
645 |
646 | if (i == 0)
647 | Piece->color = SDL_atoi(values[i]);
648 | else if (i == 1)
649 | {
650 |
651 | Piece->numOfBlocks = SDL_atoi(values[i]);
652 | Piece->blocks = SDL_calloc(Piece->numOfBlocks, sizeof(block));
653 |
654 | }
655 | else if (i % 2 == 0)
656 | Piece->blocks[blockIndex].X = SDL_atoi(values[i]);
657 | else if (i % 2 == 1)
658 | {
659 |
660 | Piece->blocks[blockIndex].Y = SDL_atoi(values[i]);
661 | blockIndex++;
662 |
663 | }
664 |
665 | }
666 |
667 | for (int i = 0; i < numValues; i++)
668 | SDL_free(values[i]);
669 | SDL_free(values);
670 |
671 | Piece->width = calcWidth(Piece);
672 | Piece->height = calcHeight(Piece);
673 |
674 | createCenterBlock(Piece);
675 |
676 | return Piece;
677 |
678 | }
679 |
680 | //Function for checking whether two blocks in a piece are connected by a list of blocks
681 | //For a piece to be valid, all blocks must be connected to all other blocks
682 | bool areBlocksConnected(piece* Piece, int blockIndex1, int blockIndex2, int* traversedBlockIndices, int* numOfTraversedBlockIndices)
683 | {
684 |
685 | //Allocate space to keep track of the number of blocks that are currently in our list of traversed block on our current path
686 | if (numOfTraversedBlockIndices == NULL)
687 | {
688 |
689 | numOfTraversedBlockIndices = SDL_calloc(1, sizeof(int));
690 | *numOfTraversedBlockIndices = 0;
691 |
692 | }
693 |
694 | //Allocate space to keep track of the indices of all blocks that neighbor the block we are currently looking at
695 | int* neighboringBlockIndices = SDL_calloc(4, sizeof(int));
696 | int numOfNeighboringBlocks = 0;
697 |
698 | //Allocate space to store our list of blocks that have been traversed on our current path
699 | //Since we never repeat blocks in a given path, we only need to allocate space to store as many block as are in our piece, because
700 | //we know our path will never be longer than that
701 | if (traversedBlockIndices == NULL)
702 | traversedBlockIndices = SDL_calloc(Piece->numOfBlocks, sizeof(int));
703 | traversedBlockIndices[*numOfTraversedBlockIndices] = blockIndex1;
704 | *numOfTraversedBlockIndices = *numOfTraversedBlockIndices + 1;
705 |
706 | //Check all neighboring cells of block1
707 | for (unsigned short direction = 0; direction < 4; direction++)
708 | {
709 |
710 | int x = Piece->blocks[blockIndex1].X;
711 | int y = Piece->blocks[blockIndex1].Y;
712 |
713 | if (direction == 0) // Above
714 | y = y - 1;
715 | else if (direction == 1) // Right
716 | x = x + 1;
717 | else if (direction == 2) // Below
718 | y = y + 1;
719 | else if (direction == 3) // Left
720 | x = x - 1;
721 |
722 | //Check all blocks to see if a block exists in the neighboring cell in the current direction
723 | for (unsigned short otherBlockIndex = 0; otherBlockIndex < Piece->numOfBlocks; otherBlockIndex++)
724 | {
725 |
726 | //Skip the block index of block1
727 | if (otherBlockIndex != blockIndex1)
728 | {
729 |
730 | //If the coords for otherBlock are equal to the coords of the neighboring cell of block1, that otherBlock neighbors block1
731 | if (x == Piece->blocks[otherBlockIndex].X && y == Piece->blocks[otherBlockIndex].Y)
732 | {
733 |
734 | //If a neighboring cell of block1 is occupied by block2, they are direct neighbors so we can return true
735 | if (otherBlockIndex == blockIndex2)
736 | {
737 |
738 | //numOfTraversedBlockIndices and traversedBlockIndices are shared between levels of recurssion, so we only want to
739 | //free them once at the very end: either when we know for sure that our initial block1 and block2 are connected or
740 | //we know for sure that they are not.
741 | SDL_free(numOfTraversedBlockIndices);
742 | SDL_free(traversedBlockIndices);
743 |
744 | //neighboringBlockIndices is unique for each level of recurssion so we need to free it every time we return from this
745 | //function.
746 | SDL_free(neighboringBlockIndices);
747 |
748 | return true;
749 |
750 | }
751 | else
752 | {
753 |
754 | //Otherwise, keep track of the block indices of all blocks that neighbor block1
755 | neighboringBlockIndices[numOfNeighboringBlocks] = otherBlockIndex;
756 | numOfNeighboringBlocks = numOfNeighboringBlocks + 1;
757 |
758 | }
759 |
760 | }
761 |
762 | }
763 |
764 | }
765 |
766 | }
767 |
768 | //If block2 is not a direct neighbor of block1:
769 | //For all neighboring blocks of block1, check if they are connected to block2
770 | for (unsigned short i = 0; i < numOfNeighboringBlocks; i++)
771 | {
772 |
773 | //Check if we have already traversed the neighboring block in our current path
774 | //If we have, we don't want to call areBlocksConnected on it again because we will get stuck in a loop
775 | int alreadyTraversed = false;
776 | for (unsigned short j = 0; j < *numOfTraversedBlockIndices; j++)
777 | if (neighboringBlockIndices[i] == traversedBlockIndices[j])
778 | alreadyTraversed = true;
779 | if (alreadyTraversed)
780 | continue;
781 |
782 | //If we have not traversed the neighboring block before, check if it is connected to block2.
783 | //If one neighboring block of block1 is connected to block2, that means that block1 is connected to block2 so we can return true.
784 | if (areBlocksConnected(Piece, neighboringBlockIndices[i], blockIndex2, traversedBlockIndices, numOfTraversedBlockIndices))
785 | {
786 |
787 | //neighboringBlockIndices is unique for each level of recurssion so we need to free it every time we return from this
788 | //function.
789 | SDL_free(neighboringBlockIndices);
790 |
791 | return true;
792 |
793 | }
794 |
795 | //If the current neighbor is not connected to block2, then "pop" it off our stack of traversedBlockIndices by reducing
796 | //numOfTraversedBlockIndices by 1. This is basically removing it from our path because we know it's not connected to block2.
797 | //We only need to reduce numOfTraversedBlockIndices by 1 to achieve popping the neighbor off, because we know that the neighbor
798 | //will always be at the top of our stack.
799 | *numOfTraversedBlockIndices = *numOfTraversedBlockIndices - 1;
800 |
801 | }
802 |
803 | //If we have gone through all neighbors of block1 and our numOfTraversedBlockIndices == 1, then that means we are currently looking at
804 | //our initial block1 and none of it's neighbors are connected to block2. This means that block1 is not connected to block2. So, we have
805 | //reached our end state of recurssion and thus need to free numOfTraversedBlockIndices and traversedBlockIndices.
806 | if (*numOfTraversedBlockIndices == 1)
807 | {
808 |
809 | SDL_free(numOfTraversedBlockIndices);
810 | SDL_free(traversedBlockIndices);
811 |
812 | }
813 |
814 | //neighboringBlockIndices is unique for each level of recurssion so we need to free it every time we return from this
815 | //function.
816 | SDL_free(neighboringBlockIndices);
817 |
818 | //Block1 is not a direct neighbor of block2, and none of the neighbors of block1 are connected to block2. Therefore, block1 is not
819 | //connected to block2.
820 | return false;
821 |
822 | }
--------------------------------------------------------------------------------
/src/generate.h:
--------------------------------------------------------------------------------
1 | #ifndef GENERATE_H_
2 | #define GENERATE_H_
3 |
4 | #include "MGF.h"
5 |
6 | void createCenterBlock(piece* piece);
7 | void copyBlocks(piece* piece1, piece* piece2);
8 | void copyPiece(piece* piece1, piece* piece2);
9 | void delPiece(piece** Piece);
10 | bool isOccupied(piece* Piece, unsigned short blockID, unsigned short direction);
11 | bool isSurrounded(piece* Piece, unsigned short blockID);
12 | unsigned short calcWidth(piece* Piece);
13 | unsigned short calcHeight(piece* Piece);
14 | piece* generateGamePiece(unsigned short size);
15 | char* convertPieceToString(piece* Piece);
16 | piece* createPieceFromString(char* string);
17 | piece* makePieceMoreRandom(piece* Piece);
18 | bool areBlocksConnected(piece* Piece, int blockIndex1, int blockIndex2, int* traversedBlockIndices, int* numOfTraversedBlockIndices);
19 |
20 | #endif
--------------------------------------------------------------------------------
/src/instance.c:
--------------------------------------------------------------------------------
1 | #include "MGF.h"
2 |
3 | //The internal resolution of the game on the title screen
4 | #define INITIAL_INTERNAL_WIDTH 384
5 | #define INITIAL_INTERNAL_HEIGHT 504
6 |
7 | //Function for initializing an audio device
8 | SDL_AudioDeviceID* prepareAudioDevice(SDL_AudioSpec** wavSpec)
9 | {
10 |
11 | //Populate wavSpec structure
12 | *wavSpec = SDL_calloc(1, sizeof(**wavSpec));
13 | Uint32 wavLength;
14 | Uint8* wavBuffer;
15 | //All sound files have the same specs as specs.wav, so we can use it to load
16 | //the audio specs
17 | SDL_LoadWAV("AUDIO/specs.wav", (*wavSpec), &wavBuffer, &wavLength);
18 |
19 | //Open the Audio device
20 | SDL_AudioDeviceID* device;
21 | device = SDL_calloc(1, sizeof(*device));
22 | *device = SDL_OpenAudioDevice(NULL, 0, *wavSpec, NULL, 0);
23 |
24 | //Free the memory taken by loading "specs.wav"
25 | SDL_FreeWAV(wavBuffer);
26 |
27 | return device;
28 |
29 | }
30 |
31 | control* initControls()
32 | {
33 |
34 | //Allocate memory for controls
35 | control* controls;
36 | controls = SDL_calloc(NUM_OF_CONTROLS, sizeof(*controls));
37 |
38 | //Set default controls
39 | //Control ID's are defined in Definitions.h
40 | controls[LEFT_BUTTON].button = SDL_SCANCODE_LEFT;
41 | controls[RIGHT_BUTTON].button = SDL_SCANCODE_RIGHT;
42 | controls[SOFT_DROP_BUTTON].button = SDL_SCANCODE_DOWN;
43 | controls[HARD_DROP_BUTTON].button = SDL_SCANCODE_UP;
44 | controls[HOLD_BUTTON].button = SDL_SCANCODE_SPACE;
45 | controls[ROTATE_CCW_BUTTON].button = SDL_SCANCODE_Z;
46 | controls[ROTATE_CW_BUTTON].button = SDL_SCANCODE_X;
47 | controls[MIRROR_BUTTON].button = SDL_SCANCODE_C;
48 | controls[SELECT_BUTTON].button = SDL_SCANCODE_RETURN;
49 | controls[EXIT_BUTTON].button = SDL_SCANCODE_ESCAPE;
50 | controls[DOWN_BUTTON].button = SDL_SCANCODE_DOWN;
51 | controls[UP_BUTTON].button = SDL_SCANCODE_UP;
52 |
53 | return controls;
54 |
55 | }
56 |
57 | sound** initSounds()
58 | {
59 |
60 | //Allocate memory for sounds
61 | sound** sounds = SDL_calloc(NUM_OF_SOUNDS, sizeof(**sounds));
62 | for (unsigned short i = 0; i < NUM_OF_SOUNDS; i++)
63 | sounds[i] = SDL_calloc(1, sizeof(*sounds));
64 |
65 | //Load all sounds
66 | sounds[COMPLETE_SOUND] = loadSound("AUDIO/complete.wav");
67 | sounds[HOLD_SOUND] = loadSound("AUDIO/hold.wav");
68 | sounds[LAND_SOUND] = loadSound("AUDIO/land.wav");
69 | sounds[MOVE_SOUND] = loadSound("AUDIO/move.wav");
70 | sounds[OVER_SOUND] = loadSound("AUDIO/over.wav");
71 | sounds[PAUSE_SOUND] = loadSound("AUDIO/pause.wav");
72 | sounds[ROTATE_SOUND] = loadSound("AUDIO/rotate.wav");
73 | sounds[UNLOCK_SOUND] = loadSound("AUDIO/unlock.wav");
74 |
75 | return sounds;
76 |
77 | }
78 |
79 | Mix_Music** initMusic()
80 | {
81 |
82 | // Allocate memory for all music tracks
83 | Mix_Music** musicTracks = SDL_calloc(NUM_OF_MUSIC_TRACKS, sizeof(Mix_Music*));
84 |
85 | // Load all music tracks
86 | musicTracks[INTRO_MUSIC_TRACK] = Mix_LoadMUS("AUDIO/musicIntro.wav");
87 | musicTracks[LOOP_MUSIC_TRACK] = Mix_LoadMUS("AUDIO/musicLoop.wav");
88 |
89 | return musicTracks;
90 |
91 | }
92 |
93 | //Function for scaling the renderer to the current window size
94 | void scaleRenderer()
95 | {
96 |
97 | //Get window dimensions
98 | int windowWidth;
99 | int windowHeight;
100 | SDL_GetWindowSize(globalInstance->window, &windowWidth, &windowHeight);
101 |
102 | //Get renderer dimensions
103 | int renderWidth;
104 | int renderHeight;
105 | SDL_RenderGetLogicalSize(globalInstance->renderer, &renderWidth, &renderHeight);
106 |
107 | SDL_Rect rect = { .x = 0, .y = 0, .w = renderWidth, .h = renderHeight};
108 | float scale = 1;
109 |
110 | //Scale renderer appropriately
111 | //Basically scale relative to width, check if game is rendering outside window. If it is, scale relative
112 | //to height instead
113 | scale = (float)windowWidth / (float)renderWidth;
114 | if ((int)((float)renderWidth * scale) > windowWidth || (int)((float)renderHeight * scale) > windowHeight)
115 | scale = (float)windowHeight / (float)renderHeight;
116 |
117 | //Adjust viewport
118 | //Honestly, I don't really remember how this works
119 | if (windowWidth == SDL_round((float)renderWidth * scale))
120 | {
121 |
122 | rect.y = (int)((windowHeight * ((float)renderWidth / windowWidth) - renderHeight) / 2);
123 | rect.x = 0;
124 |
125 | }
126 | else
127 | {
128 |
129 | rect.x = (int)((windowWidth * ((float)renderHeight / windowHeight) - renderWidth) / 2);
130 | rect.y = 0;
131 |
132 | }
133 |
134 | SDL_RenderSetScale(globalInstance->renderer, scale, scale);
135 |
136 | SDL_RenderSetViewport(globalInstance->renderer, &rect);
137 |
138 | }
139 |
140 | //Initialize game instance
141 | void initInstance(gameInstance** instance)
142 | {
143 |
144 | //Allocate memory for instance
145 | *instance = SDL_calloc(1, sizeof(**instance));
146 |
147 | (*instance)->running = true;
148 |
149 | // Get number of displays and create array of DisplayModes
150 | (*instance)->numDisplays = SDL_GetNumVideoDisplays();
151 | (*instance)->DM = SDL_calloc((*instance)->numDisplays, sizeof(SDL_DisplayMode));
152 |
153 | // Get screen information for each display
154 | for (unsigned short i = 0; i < (*instance)->numDisplays; i++)
155 | SDL_GetCurrentDisplayMode(i, &(*instance)->DM[i]);
156 | // Find the combined screen bounds across all displays
157 | findDisplayBounds(*instance);
158 |
159 | //Initialize window
160 | //Initialize in top left of screen
161 | //Window will be half the width and half the height of the screen
162 | int x = SDL_WINDOWPOS_CENTERED;
163 | int y = SDL_WINDOWPOS_CENTERED;
164 | int w = (*instance)->DM[0].w / 2;
165 | int h = (*instance)->DM[0].h / 2;
166 | Uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_INPUT_FOCUS;
167 | (*instance)->window = SDL_CreateWindow("Multris", x, y, w, h, flags);
168 |
169 | //Save window size
170 | SDL_GetWindowSize((*instance)->window, &(*instance)->minimizedWindow_W, &(*instance)->minimizedWindow_H);
171 |
172 | //Initialize renderer
173 | (*instance)->renderer = SDL_CreateRenderer((*instance)->window, -1, SDL_RENDERER_ACCELERATED);
174 | //Set renderer size
175 | SDL_RenderSetLogicalSize(globalInstance->renderer, INITIAL_INTERNAL_WIDTH, INITIAL_INTERNAL_HEIGHT);
176 |
177 | // Set renderer blend mode
178 | SDL_SetRenderDrawBlendMode((*instance)->renderer, SDL_BLENDMODE_BLEND);
179 |
180 | //Initialize audioDevice and wavSpec
181 | (*instance)->audioDevice = prepareAudioDevice(&globalInstance->wavSpec);
182 |
183 | //Load all game sounds
184 | (*instance)->masterSounds = initSounds();
185 | (*instance)->sounds = initSounds();
186 |
187 | // Load music files
188 | (*instance)->musicTracks = initMusic();
189 |
190 | //Initialize PNG loading
191 | int imgFlags = IMG_INIT_PNG;
192 | if (!(IMG_Init(imgFlags) & imgFlags))
193 | {
194 |
195 | fprintf(stderr, "SDL_image could not initialize! SDL_image Error: %s\n", IMG_GetError());
196 | exit(-1);
197 |
198 | }
199 | else
200 | (*instance)->windowSurface = SDL_GetWindowSurface((*instance)->window);
201 |
202 | //Load string sprites
203 | (*instance)->stringSS = IMG_LoadTexture((*instance)->renderer, "SPRITES/string_ss.png");
204 | //Query string spritesheet for width and height
205 | SDL_QueryTexture((*instance)->stringSS, NULL, NULL, &(*instance)->stringSS_W, &(*instance)->stringSS_H);
206 |
207 | //Load game sprites
208 | (*instance)->gameSS = IMG_LoadTexture((*instance)->renderer, "SPRITES/game_ss.png");
209 | //Query game spritesheet for width and height
210 | SDL_QueryTexture((*instance)->gameSS, NULL, NULL, &(*instance)->gameSS_W, &(*instance)->gameSS_H);
211 |
212 | //Get keyboard state
213 | (*instance)->keys = (Uint8*)SDL_GetKeyboardState(NULL);
214 |
215 | // Store the onKeyPress and onKeyHold status of all keys
216 | (*instance)->onKeyPress = SDL_calloc(SDL_NUM_SCANCODES, sizeof(bool));
217 | (*instance)->onKeyHold = SDL_calloc(SDL_NUM_SCANCODES, sizeof(bool));
218 |
219 | //Initialize controls with the default controls
220 | (*instance)->controls = initControls();
221 |
222 | //Initialize FPS and frame_time to 0
223 | (*instance)->FPS = 0;
224 | (*instance)->frame_time = 0;
225 |
226 | }
227 |
228 | void findDisplayBounds(gameInstance* instance)
229 | {
230 |
231 | instance->minDisplayX = 0;
232 | instance->minDisplayY = 0;
233 | instance->maxDisplayX = 0;
234 | instance->maxDisplayY = 0;
235 |
236 | // Go through each display and find the minimum and maximum X and Y coordinates
237 | for (unsigned short i = 0; i < SDL_GetNumVideoDisplays(); i++)
238 | {
239 |
240 | SDL_Rect currentBounds;
241 | SDL_GetDisplayBounds(i, ¤tBounds);
242 |
243 | if (currentBounds.x < instance->minDisplayX)
244 | instance->minDisplayX = currentBounds.x;
245 | else if (currentBounds.x + currentBounds.w > instance->maxDisplayX)
246 | instance->maxDisplayX = currentBounds.x + currentBounds.w;
247 |
248 | if (currentBounds.y < instance->minDisplayY)
249 | instance->minDisplayY = currentBounds.y;
250 | else if (currentBounds.y + currentBounds.h > instance->maxDisplayY)
251 | instance->maxDisplayY = currentBounds.y + currentBounds.h;
252 |
253 | }
254 |
255 | }
256 |
257 | //Function for updating the volume of the game sounds
258 | void updateVolume()
259 | {
260 |
261 | //Iterate through all sounds
262 | for (unsigned short i = 0; i < NUM_OF_SOUNDS; i++)
263 | {
264 |
265 | //Buffer that stores audio after volume adjustment
266 | Uint8* dstStream = SDL_calloc(globalInstance->masterSounds[i]->length, sizeof(*dstStream));
267 |
268 | //Mix audio with volume adjustment
269 | SDL_AudioFormat format = globalInstance->wavSpec->format;
270 | Uint32 length = globalInstance->masterSounds[i]->length;
271 | int volume = SDL_MIX_MAXVOLUME * (VOLUME / 100.0);
272 | SDL_MixAudioFormat(dstStream, globalInstance->masterSounds[i]->buffer, format, length, volume);
273 |
274 | //Copy volume adjusted buffer to sounds[i]->buffer
275 | for (unsigned int j = 0 ; j < globalInstance->masterSounds[i]->length; j++)
276 | globalInstance->sounds[i]->buffer[j] = dstStream[j];
277 |
278 | //Free dstStream memory
279 | SDL_free(dstStream);
280 |
281 | }
282 |
283 | }
284 |
285 | //Function for updating window mode between FULLSCREEN and not FULLSCREEN
286 | void setWindowMode(unsigned short mode)
287 | {
288 |
289 | if (mode == 0)
290 | {
291 |
292 | SDL_SetWindowFullscreen(globalInstance->window, 0);
293 |
294 | //Reset window size
295 | int minimized_W = globalInstance->minimizedWindow_W;
296 | int minimized_H = globalInstance->minimizedWindow_H;
297 | SDL_SetWindowSize(globalInstance->window, minimized_W, minimized_H);
298 |
299 | //Reset window position
300 | int minimized_X = globalInstance->minimizedWindow_X;
301 | int minimized_Y = globalInstance->minimizedWindow_Y;
302 | SDL_SetWindowPosition(globalInstance->window, minimized_X, minimized_Y);
303 |
304 | //Scale renderer accordingly
305 | scaleRenderer();
306 |
307 | }
308 | else if (mode == 1)
309 | {
310 |
311 | //Save window size
312 | int* minimized_W = &globalInstance->minimizedWindow_W;
313 | int* minimized_H = &globalInstance->minimizedWindow_H;
314 | SDL_GetWindowSize(globalInstance->window, minimized_W, minimized_H);
315 |
316 | //Save window position
317 | int* minimized_X = &globalInstance->minimizedWindow_X;
318 | int* minimized_Y = &globalInstance->minimizedWindow_Y;
319 | SDL_GetWindowPosition(globalInstance->window, minimized_X, minimized_Y);
320 |
321 | //Save window settings to file
322 | saveWindowSettings();
323 |
324 | // Get the bounds of the current disply
325 | int currentDisplayIndex = SDL_GetWindowDisplayIndex(globalInstance->window);
326 | SDL_Rect currentDisplayBounds;
327 | SDL_GetDisplayBounds(currentDisplayIndex, ¤tDisplayBounds);
328 |
329 | //Set window size to size of current display
330 | SDL_SetWindowSize(globalInstance->window, currentDisplayBounds.w, currentDisplayBounds.h);
331 | //Set window position to top left of current display
332 | SDL_SetWindowPosition(globalInstance->window, currentDisplayBounds.x, currentDisplayBounds.y);
333 |
334 | SDL_SetWindowFullscreen(globalInstance->window, SDL_WINDOW_FULLSCREEN);
335 |
336 | //Scale renderer accordingly
337 | scaleRenderer();
338 |
339 | }
340 |
341 | }
342 |
343 | // Set the seed for the custom rand() function
344 | void MGF_srand(int seed)
345 | {
346 |
347 | globalInstance->randState = seed;
348 |
349 | }
350 |
351 | // Custom rand() function to have identical functionality between Windows and Linux
352 | int MGF_rand()
353 | {
354 |
355 | int const a = 1103515245;
356 | int const c = 12345;
357 | globalInstance->randState = a * globalInstance->randState + c;
358 | return (globalInstance->randState >> 16) & 0x7FFF;
359 |
360 | }
--------------------------------------------------------------------------------
/src/instance.h:
--------------------------------------------------------------------------------
1 | #ifndef INSTANCE_H_
2 | #define INSTANCE_H_
3 |
4 | #include "MGF.h"
5 |
6 | SDL_AudioDeviceID* prepareAudioDevice(SDL_AudioSpec** wavSpec);
7 | control* initControls();
8 | sound** initSounds();
9 | void scaleRenderer();
10 | void initInstance(gameInstance** instance);
11 | void updateVolume();
12 | void setWindowMode(unsigned short mode);
13 | void findDisplayBounds(gameInstance* instance);
14 | void MGF_srand(int seed);
15 | int MGF_rand();
16 |
17 | #endif
--------------------------------------------------------------------------------
/src/layout.h:
--------------------------------------------------------------------------------
1 | #ifndef LAYOUT_H_
2 | #define LAYOUT_H_
3 |
4 | #include "MGF.h"
5 |
6 | void drawPlayField(SDL_Texture* background, unsigned short size, int XOffset);
7 | void drawScoreBox(SDL_Texture* background, unsigned short size, bool inCustomMode, int XOffset, bool inMultiplayer);
8 | void drawLevelBox(SDL_Texture* background, unsigned short size, int XOffset);
9 | void drawUntilBox(SDL_Texture* background, unsigned short size, int XOffset);
10 | void drawNextBox(SDL_Texture* background, unsigned short size, int XOffset);
11 | void drawHoldBox(SDL_Texture* background, unsigned short size, int XOffset);
12 | void drawSizeBagBox(SDL_Texture* background, unsigned short size, int XOffset);
13 | int calcMapWidth();
14 | int calcMapHeight();
15 | SDL_Texture* create_Score_Text();
16 | SDL_Texture* create_Name_Text();
17 | SDL_Texture* create_Level_Text();
18 | SDL_Texture* create_Lines_Text();
19 | SDL_Texture* create_Pause_Text();
20 | SDL_Texture* create_Foreground_Text();
21 | SDL_Texture* create_Cursor_Text();
22 | SDL_Texture* create_volSlide_Text();
23 | void updateValuesText(SDL_Texture* texture);
24 | SDL_Texture* create_Values_Text();
25 | void updateControlsText(SDL_Texture* texture, int selected_control, bool editing);
26 | void updateCustomText(SDL_Texture* texture, unsigned short value);
27 | char* createSizeBagStringOne();
28 | char* createSizeBagStringTwo();
29 | SDL_Texture* create_SizeBag_Text();
30 | SDL_Texture* create_Custom_Text();
31 | SDL_Texture* create_Controls_Text();
32 | UI_list* _create_list(unsigned short color, const char* strings, ...);
33 | UI_list* create_Modes_List();
34 | UI_list* create_Numerical_List();
35 | UI_list* create_Custom_List();
36 | UI_list* create_Options_List();
37 | float getSizeBagMulti(unsigned short size);
38 | int getSizeBagX(unsigned short size, float multiplier);
39 | int getSizeBagY(unsigned short size);
40 | int getListEntryY(UI_list* list, const char* str);
41 | const char* getListSelectedString(UI_list* list);
42 | int getListSelectedEntryY(UI_list* list);
43 | void delete_UI_list(UI_list** list);
44 | piece** makeTitlePieces();
45 | SDL_Texture* create_Title_Text();
46 | piece** getMovingPieces(piece** titlePieces);
47 | bool updateTitle(SDL_Texture* texture, piece** movingPieces);
48 | int getScoreDrawX(unsigned short size);
49 | int getScoreDrawY(unsigned short size);
50 | int getForegroundX(unsigned short size);
51 | int getForegroundY(unsigned short size);
52 | int getLevelX(unsigned short size, unsigned short level);
53 | int getLevelY(unsigned short size);
54 | int getLinesX(unsigned short size, unsigned short lines);
55 | int getLinesY(unsigned short size);
56 | int getNextX(unsigned short size, int width);
57 | int getNextY(unsigned short size, int height);
58 | int getHoldX(unsigned short size, int width);
59 | int getHoldY(unsigned short size, int height);
60 | void drawBackgroundExtras(SDL_Texture* background, unsigned short size);
61 | int getGameWidth(unsigned short size, bool multiplayer);
62 | int getGameHeight(unsigned short size);
63 | int getPausedX(unsigned short size, float multi);
64 | int getPausedY(unsigned short size, float multi);
65 | void updatePausedText(SDL_Texture* texture, bool exit);
66 | UI_list* create_Connect_List();
67 | SDL_Texture* create_ConnectionValues_Text();
68 | void updateConnectionValuesText(SDL_Texture* texture, char* ip, char* port, char* name);
69 | SDL_Texture* create_ConnectionMessage_Text();
70 | void updateConnectionMessageText(SDL_Texture** texture, char* message);
71 | int getConnectionMessageX(char* message, float multi);
72 | int getConnectionMessageY(float multi);
73 | int getNameDrawX(unsigned short size);
74 | int getNameDrawY(unsigned short size);
75 | UI_list* create_Multiplayer_List();
76 | UI_list* create_Hosting_List();
77 | SDL_Texture* create_HostingValues_Text();
78 | void updateHostingValuesText(SDL_Texture* texture, char* port, char* name);
79 | UI_list* create_Pause_list();
80 | SDL_Texture* create_PausedValues_Text();
81 | void updatePausedValuesText(SDL_Texture* texture);
82 | SDL_Texture* create_Credits_Text();
83 |
84 | #endif
--------------------------------------------------------------------------------
/src/main.c:
--------------------------------------------------------------------------------
1 | #include "MGF.h"
2 |
3 | //Initialize global variables
4 | bool UPDATE_FULLSCREEN_MODE = true;
5 |
6 | bool FULLSCREEN_MODE = false;
7 | unsigned short VOLUME = 10;
8 | unsigned short MUSIC_VOLUME = 100;
9 | bool LIMIT_FPS = true;
10 | bool SHOW_FPS = true;
11 | bool CENTER_DOT = true;
12 | bool GRAPHICS = true;
13 |
14 | int PROGRESS = FIRST_NUMERICAL_SIZE;
15 |
16 | int MODE = 0;
17 | int MAP_WIDTH = 0;
18 | int MAP_HEIGHT = 0;
19 |
20 | bool CUSTOM_MODE = false;
21 |
22 | int BLOCK_SPRITE_ID = 1;
23 | int WALL_SPRITE_ID = 2;
24 |
25 | bool DEBUG = false;
26 |
27 | bool MULTIPLAYER = false;
28 |
29 | //Global Instance
30 | gameInstance *globalInstance;
31 |
32 | int main(int argc, char *argv[]) {
33 |
34 | //Added just to get rid of unused-parameter warning
35 | (void)argc;
36 | (void)argv;
37 |
38 | // Start dedicated server if "--server" launch argument is given
39 | if (argc > 1)
40 | if (SDL_strcmp(argv[1], "--server") == 0)
41 | return startDedicatedServer(argc - 1, &argv[1]);
42 |
43 | //Initialize SDL stuff
44 | if( SDL_Init( SDL_INIT_VIDEO ) < 0) {
45 |
46 | fprintf( stderr, "Could not initialize SDL video: %s\n", SDL_GetError() );
47 | exit( -1 );
48 |
49 | }
50 | if ( SDL_Init( SDL_INIT_AUDIO ) < 0) {
51 |
52 | fprintf( stderr, "Could not initialize SDL audio: %s\n", SDL_GetError() );
53 | exit( -1 );
54 |
55 | }
56 | if ( SDLNet_Init() == -1)
57 | {
58 |
59 | printf("SDLNet_Init: %s\n", SDLNet_GetError());
60 | exit( -1);
61 |
62 | }
63 | if( Mix_OpenAudio( 44100, MIX_DEFAULT_FORMAT, 2, 2048 ) < 0 )
64 | {
65 | printf( "SDL_mixer could not initialize! SDL_mixer Error: %s\n", Mix_GetError() );
66 | exit(-1);
67 | }
68 |
69 | // Force openGL on Windows because somewhere there is a bug when resizing the window on Direc3D
70 | #ifdef _WIN32
71 | SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
72 | #endif
73 |
74 | //Initialize game instance
75 | initInstance(&globalInstance);
76 |
77 | //Set MAP_WIDTH and MAP_HEIGHT
78 | MAP_WIDTH = calcMapWidth();
79 | MAP_HEIGHT = calcMapHeight();
80 |
81 | //Set seed for random number generator
82 | MGF_srand((int)time(NULL));
83 |
84 | //Used to check if this is the very first frame of the game
85 | bool firstLoop = true;
86 |
87 | //Keep track of the number of frames drawn in the last 10th of a second
88 | int frames_per_DS = 0;
89 |
90 | //Variables for estimating FPS every 100ms
91 | Uint32 prevTicks = 0;
92 | int deltaTicks = 0;
93 |
94 | //Variables for limiting FPS
95 | Uint32 ticksLastFrame = SDL_GetTicks();
96 | //targetFrameTime will be roughly double the refresh rate of the primary display
97 | double targetFrameTime = (double)1 / (double)(globalInstance->DM[0].refresh_rate * 2);
98 |
99 | //Create SAVES files if they do not exist or are broken
100 | if (!fileExists("SAVES/options.cfg") || brokenOptions())
101 | createOptions();
102 | if (!fileExists("SAVES/controls.cfg") || brokenControls())
103 | createControls();
104 | if (!fileExists("SAVES/window.cfg") || brokenWindowFile())
105 | createWindowFile();
106 | if (!fileExists("SAVES/progress.md") || brokenProgress())
107 | createProgressFile();
108 | if (!fileExists("SAVES/name.cfg") || brokenNameFile())
109 | createNameFile();
110 | if (!fileExists("SAVES/hostPort.cfg") || brokenHostPortFile())
111 | createHostPortFile();
112 | if (!fileExists("SAVES/connectIP.cfg") || brokenIPFile())
113 | createIPFile();
114 | if (!fileExists("SAVES/connectPort.cfg") || brokenConnectPort())
115 | createConnectPortFile();
116 |
117 | //Now load info from SAVES files
118 |
119 | if (fileExists("SAVES/window.cfg")) {
120 |
121 | //Get minimizedWindow size and position
122 | globalInstance->minimizedWindow_W = getFileValue("SAVES/window.cfg", "WIDTH");
123 | globalInstance->minimizedWindow_H = getFileValue("SAVES/window.cfg", "HEIGHT");
124 | globalInstance->minimizedWindow_X = getFileValue("SAVES/window.cfg", "X");
125 | globalInstance->minimizedWindow_Y = getFileValue("SAVES/window.cfg", "Y");
126 |
127 | int w = globalInstance->minimizedWindow_W;
128 | int h = globalInstance->minimizedWindow_H;
129 | int x = globalInstance->minimizedWindow_X;
130 | int y = globalInstance->minimizedWindow_Y;
131 |
132 | //Set window size
133 | SDL_SetWindowSize(globalInstance->window, w, h);
134 |
135 | //Set window position
136 | SDL_SetWindowPosition(globalInstance->window, x, y);
137 |
138 | }
139 |
140 | if (fileExists("SAVES/controls.cfg")) {
141 |
142 | globalInstance->controls[LEFT_BUTTON].button = getFileValue("SAVES/controls.cfg", "LEFT");
143 | globalInstance->controls[RIGHT_BUTTON].button = getFileValue("SAVES/controls.cfg", "RIGHT");
144 | globalInstance->controls[SOFT_DROP_BUTTON].button = getFileValue("SAVES/controls.cfg", "SOFT DROP");
145 | globalInstance->controls[HARD_DROP_BUTTON].button = getFileValue("SAVES/controls.cfg", "HARD DROP");
146 | globalInstance->controls[HOLD_BUTTON].button = getFileValue("SAVES/controls.cfg", "HOLD");
147 | globalInstance->controls[ROTATE_CCW_BUTTON].button = getFileValue("SAVES/controls.cfg", "ROTATE CCW");
148 | globalInstance->controls[ROTATE_CW_BUTTON].button = getFileValue("SAVES/controls.cfg", "ROTATE CW");
149 | globalInstance->controls[MIRROR_BUTTON].button = getFileValue("SAVES/controls.cfg", "MIRROR");
150 | globalInstance->controls[SELECT_BUTTON].button = getFileValue("SAVES/controls.cfg", "SELECT");
151 | globalInstance->controls[EXIT_BUTTON].button = getFileValue("SAVES/controls.cfg", "EXIT");
152 | globalInstance->controls[DOWN_BUTTON].button = getFileValue("SAVES/controls.cfg", "DOWN");
153 | globalInstance->controls[UP_BUTTON].button = getFileValue("SAVES/controls.cfg", "UP");
154 |
155 | }
156 |
157 | if (fileExists("SAVES/options.cfg")) {
158 |
159 | FULLSCREEN_MODE = getFileValue("SAVES/options.cfg", "FULLSCREEN");
160 | VOLUME = getFileValue("SAVES/options.cfg", "VOLUME");
161 | MUSIC_VOLUME = getFileValue("SAVES/options.cfg", "MUSIC");
162 | LIMIT_FPS = getFileValue("SAVES/options.cfg", "LIMIT FPS");
163 | SHOW_FPS = getFileValue("SAVES/options.cfg", "SHOW FPS");
164 | CENTER_DOT = getFileValue("SAVES/options.cfg", "CENTER DOT");
165 | GRAPHICS = getFileValue("SAVES/options.cfg", "GRAPHICS");
166 | DEBUG = getFileValue("SAVES/options.cfg", "DEBUG");
167 |
168 | // Change sprite IDs depending on if using new or old graphics
169 | if (GRAPHICS == false)
170 | {
171 |
172 | WALL_SPRITE_ID = 0;
173 | BLOCK_SPRITE_ID = 3;
174 |
175 | }
176 | else if (GRAPHICS == true)
177 | {
178 |
179 | WALL_SPRITE_ID = 2;
180 | BLOCK_SPRITE_ID = 1;
181 |
182 | }
183 |
184 | }
185 |
186 | if (fileExists("SAVES/progress.md"))
187 | PROGRESS = getFileValue("SAVES/progress.md", "progress");
188 |
189 | //Update VOLUME to that loaded from options file
190 | updateVolume();
191 |
192 | //Game loop
193 | while (globalInstance->running) {
194 |
195 | //Poll for events
196 | while (SDL_PollEvent(&globalInstance->event)) {
197 |
198 | //User quit
199 | if (globalInstance->event.type == SDL_QUIT)
200 | globalInstance->running = false;
201 |
202 | if (globalInstance->event.type == SDL_WINDOWEVENT) {
203 |
204 | //Scale rendere if size of window changes
205 | if (globalInstance->event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
206 | scaleRenderer();
207 |
208 | }
209 |
210 | // Show the cursor if it is currently hidden and it moves
211 | if (globalInstance->event.type == SDL_MOUSEMOTION)
212 | if (SDL_ShowCursor(SDL_QUERY) == SDL_DISABLE)
213 | SDL_ShowCursor(SDL_ENABLE);
214 |
215 | }
216 |
217 | //Update 'controls' states
218 | updateControls();
219 |
220 | // Update the state of all keys
221 | updateKeys();
222 |
223 | //Clear screen with black
224 | SDL_SetRenderDrawColor(globalInstance->renderer, 0, 0, 0, 255);
225 | SDL_RenderClear(globalInstance->renderer);
226 |
227 | //Calcualte frame_time and FPS-------------------------------------------
228 | //FPS and frame_time get updated every ~100 ms (10 times per second)
229 | if (!firstLoop) {
230 |
231 | //Increase frames_per_DS counter with each frame
232 | frames_per_DS++;
233 |
234 | //Now check how many milliseconds have passed since the last time we updated the FPS counter
235 | deltaTicks = SDL_GetTicks() - prevTicks;
236 |
237 | //If it has been more than 100ms, then we update it
238 | if (deltaTicks >= 100) {
239 |
240 | //Calculate FPS
241 | int FPS = frames_per_DS;
242 | FPS = (int)((FPS - ((float)deltaTicks - 100) * frames_per_DS / deltaTicks) * 10);
243 | globalInstance->FPS = FPS;
244 |
245 | //Reset frames_per_DS counter
246 | frames_per_DS = 0;
247 |
248 | //Set prevTicks to the current value of SDL_GetTicks so that we can begin tracking frames for the
249 | //next 100ms
250 | prevTicks = SDL_GetTicks();
251 |
252 | }
253 |
254 | }
255 | else { //In the case that this is the first frame of the game
256 |
257 | //Set prevTicks to an initial value
258 | prevTicks = SDL_GetTicks();
259 |
260 | //Scale renderer to fit window
261 | scaleRenderer();
262 |
263 | //It is no longer the first frame
264 | firstLoop = false;
265 |
266 | }
267 |
268 | //As long as the FPS is greater than 0, the frame_Time is just the inverse of the FPS
269 | if (globalInstance->FPS > 0)
270 | globalInstance->frame_time = (double)1 / (double)globalInstance->FPS;
271 |
272 | //-----------------------------------------------------------------------
273 |
274 | //Call mainLoop() at different intervals depending on if the player has the FPS limiter on or not
275 | if (LIMIT_FPS) { //FPS limiter is on
276 |
277 | //Logic for limitting the FPS
278 | //Basically just sleeps every frame if not enough time passed between frames
279 | int deltaMS = SDL_GetTicks() - ticksLastFrame;
280 | if (deltaMS < targetFrameTime * 1000)
281 | SDL_Delay(targetFrameTime * 1000 - deltaMS);
282 | ticksLastFrame = SDL_GetTicks();
283 |
284 | mainLoop();
285 |
286 | }
287 | else
288 | mainLoop();
289 |
290 | //Render the current frame
291 | SDL_RenderPresent(globalInstance->renderer);
292 |
293 | //Update window in and out of fullscreen
294 | //For some reason this has to be done at the end of the frame or else it wont
295 | //update correctly on launch
296 | if (UPDATE_FULLSCREEN_MODE) {
297 |
298 | setWindowMode(FULLSCREEN_MODE);
299 |
300 | //We are not loger updating the fullscreen mode
301 | UPDATE_FULLSCREEN_MODE = false;
302 |
303 | }
304 |
305 | }
306 |
307 | //Save window size and position to disk when the game closes. But only do this if it is not in fullscreen.
308 | //This is because we want to saved the minimized window settings, not the fullscreen window settings.
309 | if (!FULLSCREEN_MODE)
310 | saveWindowSettings();
311 |
312 | //Save option values to optionsFile when game closes
313 | saveToFile("SAVES/options.cfg", "FULLSCREEN", FULLSCREEN_MODE);
314 | saveToFile("SAVES/options.cfg", "VOLUME", VOLUME);
315 | saveToFile("SAVES/options.cfg", "MUSIC", MUSIC_VOLUME);
316 | saveToFile("SAVES/options.cfg", "LIMIT FPS", LIMIT_FPS);
317 | saveToFile("SAVES/options.cfg", "SHOW FPS", SHOW_FPS);
318 | saveToFile("SAVES/options.cfg", "CENTER DOT", CENTER_DOT);
319 | saveToFile("SAVES/options.cfg", "GRAPHICS", GRAPHICS);
320 |
321 | //Close SDL stuff
322 | SDL_DestroyRenderer(globalInstance->renderer);
323 | SDL_DestroyWindow(globalInstance->window);
324 | SDLNet_Quit();
325 | SDL_Quit();
326 |
327 | //Return 0 upon successful run
328 | return 0;
329 |
330 | }
--------------------------------------------------------------------------------
/src/main.h:
--------------------------------------------------------------------------------
1 | #ifndef MAIN_H_
2 | #define MAIN_H_
3 |
4 | #include "MGF.h"
5 |
6 | int main(int argc, char *argv[]);
7 |
8 | #endif
--------------------------------------------------------------------------------
/src/mainLoop.c:
--------------------------------------------------------------------------------
1 | #include "MGF.h"
2 |
3 | //The main game loop
4 | void mainLoop()
5 | {
6 |
7 | static SDL_Texture* Texture_Background;
8 | if (Texture_Background == NULL) {
9 |
10 | //Set renderer to correect dimensions for selected size
11 | int width = getGameWidth(MODE, MULTIPLAYER);
12 | int height = getGameHeight(MODE);
13 | SDL_RenderSetLogicalSize(globalInstance->renderer, width, height);
14 | scaleRenderer();
15 |
16 | //Create background texture
17 | Texture_Background = createTexture(width, height);
18 |
19 | //Draw all layout stuff
20 |
21 | //Draw two play fields if in multiplayer mode. Second one is offset by half the width of the game.
22 | drawPlayField(Texture_Background, MODE, 0);
23 | if (MULTIPLAYER)
24 | drawPlayField(Texture_Background, MODE, width / 2);
25 |
26 | // Draw two score boxes if in multiplayer mode. Second one is offset by half the width of the game.
27 | drawScoreBox(Texture_Background, MODE, CUSTOM_MODE, 0, MULTIPLAYER);
28 | if (MULTIPLAYER)
29 | drawScoreBox(Texture_Background, MODE, CUSTOM_MODE, width / 2, MULTIPLAYER);
30 |
31 | // Draw two level boxes if in multiplayer mode. Second one is offset by half the width of the game.
32 | drawLevelBox(Texture_Background, MODE, 0);
33 | if (MULTIPLAYER)
34 | drawLevelBox(Texture_Background, MODE, width / 2);
35 |
36 | // Draw two until boxes if in multiplayer mode. Second one is offset by half the width of the game.
37 | drawUntilBox(Texture_Background, MODE, 0);
38 | if (MULTIPLAYER)
39 | drawUntilBox(Texture_Background, MODE, width / 2);
40 |
41 | // Draw two NEXT boxes if in multiplayer mode. Second one is offset by half the width of the game.
42 | drawNextBox(Texture_Background, MODE, 0);
43 | if (MULTIPLAYER)
44 | drawNextBox(Texture_Background, MODE, width / 2);
45 |
46 | // Draw two HOLD boxes if in multiplayer mode. Second one is offset by half the width of the game.
47 | drawHoldBox(Texture_Background, MODE, 0);
48 | if (MULTIPLAYER)
49 | drawHoldBox(Texture_Background, MODE, width / 2);
50 |
51 | // Draw two SIZEBAG boxes if in multiplayer mode. Second one is offset by half the width of the game.
52 | drawSizeBagBox(Texture_Background, MODE, 0);
53 | if (MULTIPLAYER)
54 | drawSizeBagBox(Texture_Background, MODE, width / 2);
55 |
56 | drawBackgroundExtras(Texture_Background, MODE);
57 |
58 | }
59 |
60 | static bool firstFrame = true;
61 |
62 | static float fpsSizeMultiplier = 0.75;
63 | static int fpsTextureWidth;
64 |
65 | if (firstFrame)
66 | {
67 |
68 | fpsTextureWidth = getStringLength("00000", fpsSizeMultiplier);
69 |
70 | firstFrame = false;
71 |
72 | }
73 |
74 | //Create texture for FPS
75 | static SDL_Texture* Texture_FPS;
76 | if (Texture_FPS == NULL)
77 | Texture_FPS = createTexture(fpsTextureWidth, FONT_HEIGHT * fpsSizeMultiplier);
78 |
79 | //The piece that shows up in the NEXT box on the title screen will be the first piece that is placed if
80 | //playing a mode that supports the size of that piece
81 | static bool generateFirst = true;
82 | static piece* firstPiece;
83 | if (generateFirst == true)
84 | {
85 |
86 | firstPiece = generateGamePiece(MGF_rand() % MAX_PIECE_SIZE + 1);
87 | generateFirst = false;
88 |
89 | }
90 |
91 | //Stores the current game state
92 | static unsigned short game_state = TITLE_SCREEN;
93 |
94 | // Store the latest message receieved from the server in multiplayer mode
95 | // This is mainly to display the reason for disconnection when the player is
96 | // disconnected
97 | static char serverMessage[SERVERMESSAGE_BUFFER_SIZE];
98 |
99 | //Display frame rate
100 | // Get width of current frame rate in pixels
101 | int fpsWidth = getIntStringLength(globalInstance->FPS, fpsSizeMultiplier);
102 | if (globalInstance->frame_time != 0.0)
103 | {
104 |
105 | // Clear FPS texture with black
106 | clearTextureWithColor(Texture_FPS, 0, 0, 0, 255);
107 | // Print current FPS to the right side of the FPS texture
108 | intToTexture(globalInstance->FPS, Texture_FPS, fpsTextureWidth - fpsWidth, 0, fpsSizeMultiplier, YELLOW);
109 |
110 | }
111 |
112 | // RENDERING --------------------------------------------------------------
113 |
114 | drawTexture(Texture_Background, 0, 0, 1.0);
115 | // Draw FPS texture so that any unusued space is outside of the drawable area
116 | if (SHOW_FPS)
117 | drawTexture(Texture_FPS, 0 - (fpsTextureWidth - fpsWidth), 0, 1.0);
118 |
119 | if (game_state == TITLE_SCREEN) {
120 |
121 | game_state = drawTitle(&firstPiece);
122 |
123 | //If MODE != 0, then NUMERICAL mode was selected. So we need to redraw the background layout.
124 | //If Texture_Background is set to NULL, it will be re-drawn on the next frame
125 | if (game_state == PLAY_SCREEN && MODE != 0) {
126 |
127 | //Clear and delete background texture
128 | clearTexture(Texture_Background);
129 | SDL_DestroyTexture(Texture_Background);
130 | Texture_Background = NULL;
131 |
132 | }
133 |
134 | }
135 | else if (game_state == PLAY_SCREEN) {
136 |
137 | //The first loop into playMode
138 | //We can tell it's the first loop because firstPiece hasn't yet been set back to NULL
139 | if (firstPiece != NULL) {
140 |
141 | //Save the map width and height into global variables so that it doesn't have to be re-calculated
142 | //every time we need it
143 | MAP_WIDTH = calcMapWidth();
144 | MAP_HEIGHT = calcMapHeight();
145 |
146 | //If the firstPiece is not a valid piece for the selected game mode, delete it and generate one
147 | //that is valid
148 | if (MODE != 0 && MODE != firstPiece->numOfBlocks) {
149 |
150 | delPiece(&firstPiece);
151 | firstPiece = generateGamePiece(MODE);
152 |
153 | }
154 | game_state = playMode(firstPiece, &serverMessage[0]);
155 |
156 | //We can delete the firstPiece after passing it to playMode
157 | delPiece(&firstPiece);
158 |
159 | }
160 | else //The firstPiece has been deleted since it is no longer needed
161 | game_state = playMode(NULL, &serverMessage[0]); //So we just pass NULL in its place
162 |
163 | // This happens when the player just got disconnected from a multiplayer game and goes back to the lobby
164 | if (game_state == MULTIPLAYERLOBBY_SCREEN)
165 | {
166 |
167 | // Reset MODE back to zero
168 | MODE = 0;
169 |
170 | // Delete the background texture so that we don't have the two play fields anymore
171 | clearTexture(Texture_Background);
172 | SDL_DestroyTexture(Texture_Background);
173 | Texture_Background = NULL;
174 |
175 | // Re-generate the firstPiece
176 | generateFirst = true;
177 |
178 | }
179 |
180 | }
181 | else if (game_state == CONTROLS_SCREEN)
182 | game_state = controlsScreen(&firstPiece);
183 | else if (game_state == CREDITS_SCREEN)
184 | game_state = creditsScreen(&firstPiece);
185 | else if (game_state == MULTIPLAYERLOBBY_SCREEN)
186 | {
187 |
188 | game_state = multiplayerLobby(&firstPiece, &serverMessage[0]);
189 |
190 | // If entering a multiplayer game, clear the background so that it can be redrawn with two play fields.
191 | if (game_state == PLAY_SCREEN && MULTIPLAYER == true)
192 | {
193 |
194 | clearTexture(Texture_Background);
195 | SDL_DestroyTexture(Texture_Background);
196 | Texture_Background = NULL;
197 |
198 | }
199 |
200 | }
201 | else if (game_state == RESET) //Reset the game to the main menu
202 | {
203 |
204 | //Reset MODE back to 0
205 | MODE = 0;
206 |
207 | //Lets also reset the background again
208 | clearTexture(Texture_Background);
209 | SDL_DestroyTexture(Texture_Background);
210 | Texture_Background = NULL;
211 |
212 | //Return to the TITLE_SCREEN when the game is reset
213 | game_state = TITLE_SCREEN;
214 |
215 | // We need to re-generate the firstPiece when we RESET
216 | generateFirst = true;
217 |
218 | // If we got returned to the main menu, reset global hosting value. This is just in case it for some reason didn't get reset already.
219 | globalInstance->hosting = false;
220 |
221 | }
222 |
223 | }
--------------------------------------------------------------------------------
/src/mainLoop.h:
--------------------------------------------------------------------------------
1 | #ifndef MAINLOOP_H_
2 | #define MAINLOOP_H_
3 |
4 | #include "MGF.h"
5 |
6 | void mainLoop();
7 |
8 | #endif
--------------------------------------------------------------------------------
/src/map.c:
--------------------------------------------------------------------------------
1 | #include "MGF.h"
2 |
3 | unsigned short drawTitle(piece** firstPiece)
4 | {
5 |
6 | //Variables
7 | DECLARE_VARIABLE(bool, titleUpdating, false);
8 | DECLARE_VARIABLE(unsigned short, mode, 0);
9 | DECLARE_VARIABLE(double, Y, (double)15);
10 | DECLARE_VARIABLE(char, inputLock, true);
11 | DECLARE_VARIABLE(int, nextText_Width, 0);
12 | DECLARE_VARIABLE(int, nextText_Height, 0);
13 | DECLARE_VARIABLE(int, titleText_Height, 0);
14 | DECLARE_VARIABLE(bool, firstLoop, true);
15 | DECLARE_VARIABLE(bool, editingVolume, false);
16 | DECLARE_VARIABLE(bool, editingMusic, false);
17 | DECLARE_VARIABLE(unsigned int, moveStart, 0);
18 | DECLARE_VARIABLE(bool, moveStartBool, false);
19 | DECLARE_VARIABLE(unsigned short, customModeSize, MIN_CUSTOM_SIZE);
20 |
21 | //Textures
22 | static SDL_Texture* Texture_Next; declare_Piece_Text(&Texture_Next, *firstPiece, false);
23 | static SDL_Texture* Texture_Score; declare_HUD_Text(&Texture_Score, SCORE_TEXT);
24 | static SDL_Texture* Texture_Level; declare_HUD_Text(&Texture_Level, LEVEL_TEXT);
25 | static SDL_Texture* Texture_Lines; declare_HUD_Text(&Texture_Lines, LINES_TEXT);
26 | static SDL_Texture* Texture_Title; declare_HUD_Text(&Texture_Title, TITLE_TEXT);
27 | static SDL_Texture* Texture_Cursor; declare_HUD_Text(&Texture_Cursor, CURSOR_TEXT);
28 | static SDL_Texture* Texture_volSlide; declare_HUD_Text(&Texture_volSlide, VOLSLIDE_TEXT);
29 | static SDL_Texture* Texture_Values; declare_HUD_Text(&Texture_Values, VALUES_TEXT);
30 | static SDL_Texture* Texture_Custom; declare_HUD_Text(&Texture_Custom, CUSTOM_TEXT);
31 |
32 | //Arrays
33 | static piece** movingPieces; declare_moving_title_pieces(&movingPieces);
34 |
35 | //UI elements
36 | static UI_list* modes; declare_UI_list(&modes, MODES_LIST);
37 | static UI_list* numerical; declare_UI_list(&numerical, NUMERICAL_LIST);
38 | static UI_list* custom; declare_UI_list(&custom, CUSTOM_LIST);
39 | static UI_list* options; declare_UI_list(&options, OPTIONS_LIST);
40 |
41 | //Some stuff to do on the firstLoop
42 | if (*firstLoop == true)
43 | {
44 |
45 | //Get the width and height for the nextPiece texture
46 | SDL_QueryTexture(Texture_Next, NULL, NULL, nextText_Width, nextText_Height);
47 | //Get the height of the title texture
48 | SDL_QueryTexture(Texture_Title, NULL, NULL, NULL, titleText_Height);
49 |
50 | //Start the title at a Y value that is just below whatever the lowest-reaching menu is
51 | int modesHeight, numericalHeight, customHeight, optionsHeight;
52 | SDL_QueryTexture(modes->ui->texture, NULL, NULL, NULL, &modesHeight);
53 | SDL_QueryTexture(numerical->ui->texture, NULL, NULL, NULL, &numericalHeight);
54 | SDL_QueryTexture(custom->ui->texture, NULL, NULL, NULL, &customHeight);
55 | SDL_QueryTexture(options->ui->texture, NULL, NULL, NULL, &optionsHeight);
56 | //This section basically finds the bottom Y value of the lowest-reaching menu
57 | *Y = SDL_ceil(SDL_max(modes->ui->y + modesHeight, numerical->ui->y + numericalHeight));
58 | *Y = SDL_ceil(SDL_max(*Y, custom->ui->y + customHeight));
59 | *Y = SDL_ceil(SDL_max(*Y, options->ui->y + optionsHeight));
60 | *Y = *Y / (double)(SPRITE_HEIGHT) + 1;
61 |
62 | //Default MODE is 0
63 | MODE = 0;
64 |
65 | CUSTOM_MODE = false;
66 |
67 | *firstLoop = false;
68 |
69 | }
70 |
71 | // Control Logic -------------------------------------------------------------
72 |
73 | //Keep track of the current selected mode and option
74 | const char* selected_mode = getListSelectedString(modes);
75 | const char* selected_option = getListSelectedString(options);
76 |
77 | //If you press DOWN
78 | if (onPress(DOWN_BUTTON))
79 | {
80 |
81 | //Move down only in the menu that is currently being interacted with
82 | if(modes->ui->currentlyInteracting)
83 | {
84 |
85 | //Do some bounds checking to make sure you dont move down off the menu
86 | if (modes->selected_entry < modes->num_entries - 1)
87 | {
88 |
89 | playSound(MOVE_SOUND);
90 | modes->selected_entry++;
91 |
92 | }
93 |
94 | }
95 | else if(numerical->ui->currentlyInteracting)
96 | {
97 |
98 | if (numerical->selected_entry < PROGRESS - FIRST_NUMERICAL_SIZE)
99 | {
100 |
101 | playSound(MOVE_SOUND);
102 | numerical->selected_entry++;
103 |
104 | }
105 | else //Play LAND_SOUND if player trying to select a size they dont have unlocked
106 | playSound(LAND_SOUND);
107 |
108 | }
109 | else if(options->ui->currentlyInteracting && !*editingVolume && !*editingMusic)
110 | {
111 |
112 | if (options->selected_entry < options->num_entries - 1)
113 | {
114 |
115 | playSound(MOVE_SOUND);
116 | options->selected_entry++;
117 |
118 | }
119 |
120 | }
121 |
122 | }
123 |
124 | //If you press UP
125 | //Basically same logic as pressing DOWN
126 | if (onPress(UP_BUTTON))
127 | {
128 |
129 | if (modes->ui->currentlyInteracting)
130 | {
131 |
132 | if(modes->selected_entry > 0)
133 | {
134 |
135 | playSound(MOVE_SOUND);
136 | modes->selected_entry--;
137 |
138 | }
139 |
140 | }
141 | else if (numerical->ui->currentlyInteracting)
142 | {
143 |
144 | if(numerical->selected_entry > 0)
145 | {
146 |
147 | playSound(MOVE_SOUND);
148 | numerical->selected_entry--;
149 |
150 | }
151 |
152 | }
153 | else if (options->ui->currentlyInteracting && !*editingVolume && !*editingMusic)
154 | {
155 |
156 | if(options->selected_entry > 0)
157 | {
158 |
159 | playSound(MOVE_SOUND);
160 | options->selected_entry--;
161 |
162 | }
163 |
164 | }
165 |
166 | }
167 |
168 | //Holding LEFT or RIGHT is only used for changing the volume
169 | if (onHold(LEFT_BUTTON) || onHold(RIGHT_BUTTON))
170 | {
171 |
172 | Uint32 currTicks = SDL_GetTicks();
173 |
174 | //Start the counter for holding the LEFT or RIGHT buttons
175 | if (*moveStart == 0)
176 | {
177 |
178 | *moveStart = currTicks;
179 | *moveStartBool = true;
180 |
181 | }
182 |
183 | //Logic for volume or custom value changing rapidly if you hold the button
184 | if (*moveStartBool || (currTicks - *moveStart) >= (MOVEMENT_WAIT + MOVEMENT_TIME))
185 | {
186 |
187 | if (onHold(LEFT_BUTTON))
188 | {
189 |
190 | if (*editingVolume && VOLUME > 0)
191 | {
192 |
193 | VOLUME--;
194 | updateValuesText(Texture_Values);
195 |
196 | updateVolume();
197 | playSound(MOVE_SOUND);
198 |
199 | }
200 | else if (*editingMusic && MUSIC_VOLUME > 0)
201 | {
202 |
203 | MUSIC_VOLUME--;
204 | updateValuesText(Texture_Values);
205 |
206 | playSound(MOVE_SOUND);
207 |
208 | }
209 | else if (custom->ui->currentlyInteracting && *customModeSize > MIN_CUSTOM_SIZE)
210 | {
211 |
212 | *customModeSize -= 1;
213 | updateCustomText(Texture_Custom, *customModeSize);
214 |
215 | playSound(MOVE_SOUND);
216 |
217 | }
218 |
219 | }
220 |
221 | if (onHold(RIGHT_BUTTON))
222 | {
223 |
224 | if (*editingVolume && VOLUME < 100)
225 | {
226 |
227 | VOLUME++;
228 | updateValuesText(Texture_Values);
229 |
230 | updateVolume();
231 | playSound(MOVE_SOUND);
232 |
233 | }
234 | else if (*editingMusic && MUSIC_VOLUME < 100)
235 | {
236 |
237 | MUSIC_VOLUME++;
238 | updateValuesText(Texture_Values);
239 |
240 | playSound(MOVE_SOUND);
241 |
242 | }
243 | else if (custom->ui->currentlyInteracting && *customModeSize < MAX_CUSTOM_SIZE)
244 | {
245 |
246 | *customModeSize += 1;
247 | updateCustomText(Texture_Custom, *customModeSize);
248 |
249 | playSound(MOVE_SOUND);
250 |
251 | }
252 |
253 | }
254 |
255 | *moveStartBool = false;
256 |
257 | if ((currTicks - *moveStart) >= (MOVEMENT_WAIT + MOVEMENT_TIME))
258 | *moveStart = currTicks - MOVEMENT_WAIT;
259 |
260 | }
261 |
262 | }
263 | else
264 | *moveStart = 0;
265 |
266 | //When you press the SELECT_BUTTON
267 | if (onPress(SELECT_BUTTON))
268 | {
269 |
270 | //Play ROTATE_SOUND only if pressed when not editing volume
271 | if (*editingVolume == false && *editingMusic == false)
272 | playSound(ROTATE_SOUND);
273 |
274 | //If pressed SELECT when in the modes menu
275 | if (modes->ui->currentlyInteracting)
276 | {
277 |
278 | if (SDL_strcmp(selected_mode, "MULTRIS") == 0)
279 | {
280 |
281 | //Enter MULTRIS gamemode
282 | MODE = 0;
283 | freeVars();
284 | return PLAY_SCREEN;
285 |
286 | }
287 | else if (SDL_strcmp(selected_mode, "NUMERICAL") == 0)
288 | {
289 |
290 | //Go into NUMERICAL menu
291 | modes->ui->currentlyInteracting = false;
292 | numerical->ui->currentlyInteracting = true;
293 |
294 | }
295 | else if (SDL_strcmp(selected_mode, "MULTIPLAYER") == 0)
296 | {
297 |
298 | // Go to the Multiplayer lobby screen where the player connects to a server
299 | freeVars();
300 | return MULTIPLAYERLOBBY_SCREEN;
301 |
302 | }
303 | else if (SDL_strcmp(selected_mode, "CUSTOM") == 0)
304 | {
305 |
306 | // Go into CUSTOM menu
307 | modes->ui->currentlyInteracting = false;
308 | custom->ui->currentlyInteracting = true;
309 |
310 | }
311 | else if (SDL_strcmp(selected_mode, "OPTIONS") == 0)
312 | {
313 |
314 | //Go into OPTIONS menu
315 | modes->ui->currentlyInteracting = false;
316 | options->ui->currentlyInteracting = true;
317 |
318 | }
319 | else if (SDL_strcmp(selected_mode, "CREDITS") == 0)
320 | {
321 |
322 | // Go to CREDITS screen
323 | freeVars();
324 | return CREDITS_SCREEN;
325 |
326 | }
327 | else if (SDL_strcmp(selected_mode, "EXIT") == 0)
328 | //Exit the game
329 | globalInstance->running = false;
330 |
331 | }
332 | //If pressed SELECT when in the NUMERICAL menu
333 | else if (numerical->ui->currentlyInteracting)
334 | {
335 |
336 | //Enter NUMERICAL gamemode with selected size
337 | MODE = SDL_atoi(getListSelectedString(numerical));
338 | freeVars();
339 | return PLAY_SCREEN;
340 |
341 | }
342 | else if (custom->ui->currentlyInteracting)
343 | {
344 |
345 | CUSTOM_MODE = true;
346 | MODE = *customModeSize;
347 | freeVars();
348 | return PLAY_SCREEN;
349 |
350 | }
351 | //If pressed SELECT when in the OPTIONS menu
352 | else if (options->ui->currentlyInteracting)
353 | {
354 |
355 | //Logic for what option you pressed SELECT on
356 | if (SDL_strcmp(selected_option, "LIMIT FPS") == 0)
357 | {
358 |
359 | LIMIT_FPS = !LIMIT_FPS;
360 |
361 | updateValuesText(Texture_Values);
362 |
363 | }
364 | else if (SDL_strcmp(selected_option, "FULLSCREEN") == 0)
365 | {
366 |
367 | FULLSCREEN_MODE = !FULLSCREEN_MODE;
368 | UPDATE_FULLSCREEN_MODE = true;
369 |
370 | updateValuesText(Texture_Values);
371 |
372 | }
373 | else if (SDL_strcmp(selected_option, "SFX VOL") == 0)
374 | *editingVolume = true;
375 | else if (SDL_strcmp(selected_option, "MUSIC VOL") == 0)
376 | *editingMusic = true;
377 | else if (SDL_strcmp(selected_option, "CONTROLS") == 0)
378 | {
379 |
380 | //Go to the controls screen
381 | freeVars();
382 | return CONTROLS_SCREEN;
383 |
384 | }
385 | else if (SDL_strcmp(selected_option, "SHOW FPS") == 0)
386 | {
387 |
388 | SHOW_FPS = !SHOW_FPS;
389 |
390 | updateValuesText(Texture_Values);
391 |
392 | }
393 | else if (SDL_strcmp(selected_option, "CENTER DOT") == 0)
394 | {
395 |
396 | CENTER_DOT = !CENTER_DOT;
397 |
398 | updateValuesText(Texture_Values);
399 |
400 | }
401 | else if (SDL_strcmp(selected_option, "GRAPHICS") == 0)
402 | {
403 |
404 | GRAPHICS = !GRAPHICS;
405 |
406 | updateValuesText(Texture_Values);
407 |
408 | // Change sprite IDs depending on if using new or old graphics
409 | if (GRAPHICS == false)
410 | {
411 |
412 | WALL_SPRITE_ID = 0;
413 | BLOCK_SPRITE_ID = 3;
414 |
415 | }
416 | else if (GRAPHICS == true)
417 | {
418 |
419 | WALL_SPRITE_ID = 2;
420 | BLOCK_SPRITE_ID = 1;
421 |
422 | }
423 |
424 | freeVars();
425 | return RESET;
426 |
427 | }
428 |
429 | }
430 |
431 | }
432 |
433 | //Exit out of the current sub-menu if the EXIT_BUTTON is pressed
434 | if (onPress(EXIT_BUTTON))
435 | {
436 |
437 | //We're not exiting out of any menu if were in the modes menu when we press EXIT.
438 | //So only play this sound if we're not in the modes menu
439 | if (modes->ui->currentlyInteracting == false)
440 | playSound(LAND_SOUND);
441 |
442 | if (numerical->ui->currentlyInteracting)
443 | {
444 |
445 | numerical->ui->currentlyInteracting = false;
446 | modes->ui->currentlyInteracting = true;
447 |
448 | }
449 | else if (custom->ui->currentlyInteracting)
450 | {
451 |
452 | custom->ui->currentlyInteracting = false;
453 | modes->ui->currentlyInteracting = true;
454 |
455 | }
456 | else if (options->ui->currentlyInteracting)
457 | {
458 |
459 | if (*editingVolume == true)
460 | *editingVolume = false;
461 | else if (*editingMusic == true)
462 | *editingMusic = false;
463 | else
464 | {
465 |
466 | options->ui->currentlyInteracting = false;
467 | modes->ui->currentlyInteracting = true;
468 |
469 | //Save option values to optionsFile when player leaves options menu
470 | saveToFile("SAVES/options.cfg", "FULLSCREEN", FULLSCREEN_MODE);
471 | saveToFile("SAVES/options.cfg", "VOLUME", VOLUME);
472 | saveToFile("SAVES/options.cfg", "MUSIC", MUSIC_VOLUME);
473 | saveToFile("SAVES/options.cfg", "LIMIT FPS", LIMIT_FPS);
474 | saveToFile("SAVES/options.cfg", "SHOW FPS", SHOW_FPS);
475 | saveToFile("SAVES/options.cfg", "CENTER DOT", CENTER_DOT);
476 | saveToFile("SAVES/options.cfg", "GRAPHICS", GRAPHICS);
477 |
478 | }
479 |
480 | }
481 |
482 | }
483 |
484 | // ---------------------------------------------------------------------------
485 |
486 | //Title dropping
487 | if ((int)(*Y + *titleText_Height / SPRITE_HEIGHT) <= BASE_PLAYFIELD_HEIGHT * MAX_PIECE_SIZE)
488 | {
489 |
490 | //Don't start dropping the title until the game has been running for at least 1000ms
491 | //This is because FPS and frame_times can be erratic the first few frames after launch
492 | if (SDL_GetTicks() > 1000)
493 | *Y += INITIAL_SPEED * globalInstance->frame_time;
494 |
495 | }
496 | else
497 | {
498 |
499 | //Once the title is done dropping, there are a some pieces in the title that should
500 | //continue dropping
501 | //These pieces are called movingPieces
502 | //We call updateTitle until all movingPieces finished dropping as well
503 | if (*titleUpdating == false)
504 | *titleUpdating = updateTitle(Texture_Title, movingPieces);
505 |
506 | }
507 |
508 | //Rendering -------------------------------------------
509 |
510 | drawTexture(Texture_Title, FONT_WIDTH, FONT_HEIGHT * (int)*Y, 1.0);
511 | drawTexture(Texture_Next, 318 - (*nextText_Width / 2), 282 - (*nextText_Height / 2), 1.0);
512 | drawTexture(Texture_Score, getScoreDrawX(MODE), getScoreDrawY(MODE), 1.0);
513 | drawTexture(Texture_Level, 312, 115, 1.0);
514 | drawTexture(Texture_Lines, 312, 189, 1.0);
515 |
516 | //Draw UI elements
517 |
518 | //Modes
519 | drawTexture(modes->ui->texture, modes->ui->x, modes->ui->y, 1.0);
520 | drawTexture(Texture_Cursor, modes->ui->x - 14, getListSelectedEntryY(modes), 1.0);
521 |
522 | //Only draw currently active sub-menu
523 | //MODES menu is always active
524 | if (numerical->ui->currentlyInteracting)
525 | {
526 |
527 | drawTexture(numerical->ui->texture, numerical->ui->x, numerical->ui->y, 1.0);
528 | drawTexture(Texture_Cursor, numerical->ui->x - 14, getListSelectedEntryY(numerical), 1);
529 |
530 | }
531 | else if (custom->ui->currentlyInteracting)
532 | {
533 |
534 | // Draw CUSTOM menu texture
535 | drawTexture(custom->ui->texture, custom->ui->x, custom->ui->y, 1.0);
536 |
537 | // Draw CUSTOM value
538 | int customValueX = custom->ui->x + (int)(2.5 * (double)FONT_WIDTH) + 2 * STRING_GAP - getIntStringLength(*customModeSize, 1.0) / 2;
539 | drawTexture(Texture_Custom, customValueX, custom->ui->y, 1.0);
540 |
541 | }
542 | else if (options->ui->currentlyInteracting)
543 | {
544 |
545 | //Draw options menu
546 | drawTexture(options->ui->texture, options->ui->x, options->ui->y, 1.0);
547 | drawTexture(Texture_Cursor, options->ui->x - 14, getListSelectedEntryY(options), 1);
548 |
549 | //Draw options values
550 | int optionsWidth;
551 | SDL_QueryTexture(options->ui->texture, NULL, NULL, &optionsWidth, NULL);
552 | drawTexture(Texture_Values, options->ui->x + optionsWidth, options->ui->y, 1.0);
553 |
554 | //If editing the volume, draw cursors to left and right of volume
555 | if (*editingVolume || *editingMusic)
556 | drawTexture(Texture_volSlide, options->ui->x + optionsWidth - FONT_WIDTH, getListSelectedEntryY(options), 1.0);
557 |
558 | }
559 |
560 | //-----------------------------------------------------
561 |
562 | return TITLE_SCREEN;
563 |
564 | }
--------------------------------------------------------------------------------
/src/map.h:
--------------------------------------------------------------------------------
1 | #ifndef MAP_H_
2 | #define MAP_H_
3 |
4 | #include "MGF.h"
5 |
6 | unsigned short drawTitle(piece** firstPiece);
7 |
8 | #endif
--------------------------------------------------------------------------------
/src/memory.c:
--------------------------------------------------------------------------------
1 | #include "MGF.h"
2 |
3 | //Function for pushing a variable or object onto a varVector structure
4 | //designed to make freeing variables at the end of a game_state easier
5 | varVector** pushAddress(void** ptr, unsigned short type)
6 | {
7 |
8 | //If this is the first address being pushed
9 | static varVector* vector;
10 | if (vector == NULL) {
11 |
12 | //Allocate memory for the varVector
13 | vector = SDL_calloc(1, sizeof(*vector));
14 | vector->count = 0;
15 |
16 | }
17 |
18 | //If we passed a NULL ptr, return the address of the varVector
19 | if(ptr == NULL)
20 | return &vector;
21 | else //Otherwise, we want to push the pointer onto the varVector
22 | {
23 |
24 | //If this is the first pointer being pushed since the last time they were all freed
25 | if (vector->ptrs == NULL)
26 | {
27 |
28 | //Allocate memory for the ptrs array and the types array
29 | vector->ptrs = SDL_calloc(1, sizeof(void*));
30 | vector->types = SDL_calloc(1, sizeof(unsigned short));
31 |
32 | }
33 | else //Otherwise, we need to reallocate ptrs and types to increase their size by 1
34 | {
35 |
36 | vector->ptrs = SDL_realloc(vector->ptrs, (vector->count + 1) * sizeof(void*));
37 | vector->types = SDL_realloc(vector->types, (vector->count + 1) * sizeof(unsigned short));
38 |
39 | }
40 |
41 | //Store the pointer to the variable and the type of variable into the arrays
42 | vector->ptrs[vector->count] = ptr;
43 | vector->types[vector->count] = type;
44 |
45 | //Increase the count of the varVector
46 | vector->count++;
47 |
48 | return NULL;
49 |
50 | }
51 |
52 | }
53 |
54 | //Function for checking if a ptr is already stored in the varVector
55 | bool inVector(void** ptr)
56 | {
57 |
58 | varVector** vector = pushAddress(NULL, TYPE_NA);
59 |
60 | for (int i = 0; i < (*vector)->count; i++)
61 | if (ptr == (*vector)->ptrs[i])
62 | return true;
63 | return false;
64 |
65 | }
66 |
67 | //Function for declaring a short on the varVector array
68 | void declare_short(void** ptr, short value)
69 | {
70 |
71 | if (*ptr == NULL)
72 | {
73 |
74 | *ptr = SDL_calloc(1, sizeof(short));
75 | **(short**)(ptr) = value;
76 |
77 | if (!inVector(ptr))
78 | pushAddress(ptr, VARIABLE);
79 |
80 | }
81 |
82 | }
83 |
84 | //Function for declaring an unsigned short on the varVector array
85 | void declare_unsigned_short(void** ptr, unsigned short value)
86 | {
87 |
88 | if (*ptr == NULL)
89 | {
90 |
91 | *ptr = SDL_calloc(1, sizeof(unsigned short));
92 | **(unsigned short**)(ptr) = value;
93 |
94 | if (!inVector(ptr))
95 | pushAddress(ptr, VARIABLE);
96 |
97 | }
98 |
99 | }
100 |
101 | // Function for declaring an unsigned long on the varVector array
102 | void declare_unsigned_long(void** ptr, unsigned long value)
103 | {
104 |
105 | if (*ptr == NULL)
106 | {
107 |
108 | *ptr = SDL_calloc(1, sizeof(unsigned long));
109 | **(unsigned long**)(ptr) = value;
110 |
111 | if (!inVector(ptr))
112 | pushAddress(ptr, VARIABLE);
113 |
114 | }
115 |
116 | }
117 |
118 | //Function for declaring a double on the varVector array
119 | void declare_double(void** ptr, double value)
120 | {
121 |
122 | if (*ptr == NULL)
123 | {
124 |
125 | *ptr = SDL_calloc(1, sizeof(double));
126 | **(double**)(ptr) = value;
127 |
128 | if (!inVector(ptr))
129 | pushAddress(ptr, VARIABLE);
130 |
131 | }
132 |
133 | }
134 |
135 | //Function for declaring an int on the varVector array
136 | void declare_int(void** ptr, int value)
137 | {
138 |
139 | if (*ptr == NULL)
140 | {
141 |
142 | *ptr = SDL_calloc(1, sizeof(int));
143 | **(int**)(ptr) = value;
144 |
145 | if (!inVector(ptr))
146 | pushAddress(ptr, VARIABLE);
147 |
148 | }
149 |
150 | }
151 |
152 | //Function for declaring a char on the varVector array
153 | void declare_char(void** ptr, char value)
154 | {
155 |
156 | if (*ptr == NULL)
157 | {
158 |
159 | *ptr = SDL_calloc(1, sizeof(char));
160 | **(char**)(ptr) = value;
161 |
162 | if (!inVector(ptr))
163 | pushAddress(ptr, VARIABLE);
164 |
165 | }
166 |
167 | }
168 |
169 | //Function for declaring a bool on the varVector array
170 | void declare_bool(void** ptr, bool value)
171 | {
172 |
173 | if (*ptr == NULL)
174 | {
175 |
176 | *ptr = SDL_calloc(1, sizeof(bool));
177 | **(bool**)(ptr) = value;
178 |
179 | if (!inVector(ptr))
180 | pushAddress(ptr, VARIABLE);
181 |
182 | }
183 |
184 | }
185 |
186 | //Function for declaring an unsigned int on the varVector array
187 | void declare_unsigned_int(void** ptr, unsigned int value)
188 | {
189 |
190 | if (*ptr == NULL)
191 | {
192 |
193 | *ptr = SDL_calloc(1, sizeof(unsigned int));
194 | **(unsigned int**)(ptr) = value;
195 |
196 | if (!inVector(ptr))
197 | pushAddress(ptr, VARIABLE);
198 |
199 | }
200 |
201 | }
202 |
203 | //Function for declaring a Piece on the varVector array
204 | void declare_Piece(piece** ptr, piece* Piece)
205 | {
206 |
207 | if (*ptr == NULL)
208 | {
209 |
210 | if (Piece != NULL)
211 | {
212 |
213 | *ptr = SDL_calloc(1, sizeof(piece));
214 | (*ptr)->blocks = SDL_calloc(Piece->numOfBlocks, sizeof(*(*ptr)->blocks));
215 | (*ptr)->centerBlock = SDL_calloc(1, sizeof(block));
216 | copyPiece(Piece, *ptr);
217 |
218 | }
219 |
220 | if (!inVector((void**)ptr))
221 | pushAddress((void**)ptr, PIECE);
222 |
223 | }
224 |
225 | }
226 |
227 | //Function for declaring a Piece Texture on the varVector array
228 | void declare_Piece_Text(SDL_Texture** ptr, piece* Piece, bool drawCenterDot)
229 | {
230 |
231 | if (*ptr == NULL)
232 | {
233 |
234 | if (Piece != NULL)
235 | *ptr = createPieceTexture(*Piece, drawCenterDot);
236 |
237 | if (!inVector((void**)ptr))
238 | pushAddress((void**)ptr, TEXTURE);
239 |
240 | }
241 |
242 | }
243 |
244 | //Function for declaring a HUD Texture on the varVector array
245 | void declare_HUD_Text(SDL_Texture** ptr, int type)
246 | {
247 |
248 | if (*ptr == NULL)
249 | {
250 |
251 | //Call a different function depending on the the type of HUD texture we are trying to make
252 | if (type == SCORE_TEXT)
253 | *ptr = create_Score_Text();
254 | else if (type == LEVEL_TEXT)
255 | *ptr = create_Level_Text();
256 | else if (type == LINES_TEXT)
257 | *ptr = create_Lines_Text();
258 | else if (type == PAUSED_TEXT)
259 | *ptr = create_Pause_Text();
260 | else if (type == FOREGROUND_TEXT)
261 | *ptr = create_Foreground_Text();
262 | else if (type == TITLE_TEXT)
263 | *ptr = create_Title_Text();
264 | else if (type == CURSOR_TEXT)
265 | *ptr = create_Cursor_Text();
266 | else if (type == VALUES_TEXT)
267 | *ptr = create_Values_Text();
268 | else if (type == VOLSLIDE_TEXT)
269 | *ptr = create_volSlide_Text();
270 | else if (type == CONTROLS_TEXT)
271 | *ptr = create_Controls_Text();
272 | else if (type == CUSTOM_TEXT)
273 | *ptr = create_Custom_Text();
274 | else if (type == SIZEBAG_TEXT)
275 | *ptr = create_SizeBag_Text();
276 | else if (type == CONNECTIONVALUES_TEXT)
277 | *ptr = create_ConnectionValues_Text();
278 | else if (type == CONNECTIONMESSAGE_TEXT)
279 | *ptr = create_ConnectionMessage_Text();
280 | else if (type == NAME_TEXT)
281 | *ptr = create_Name_Text();
282 | else if (type == HOSTINGVALUES_TEXT)
283 | *ptr = create_HostingValues_Text();
284 | else if (type == PAUSEDVALUES_TEXT)
285 | *ptr = create_PausedValues_Text();
286 | else if (type == CREDITS_TEXT)
287 | *ptr = create_Credits_Text();
288 |
289 | if(!inVector((void**)ptr))
290 | pushAddress((void**)ptr, TEXTURE);
291 |
292 | }
293 |
294 | }
295 |
296 | //Declare the map data matrix
297 | void declare_map_matrix(int** ptr)
298 | {
299 |
300 | if (*ptr == NULL)
301 | {
302 |
303 | *ptr = (int*)(SDL_malloc(MAP_HEIGHT * MAP_WIDTH * sizeof(**ptr)));
304 | if (*ptr != NULL)
305 | for (unsigned short i = 0; i < MAP_HEIGHT; i++)
306 | for (unsigned short j = 0; j < MAP_WIDTH; j++)
307 | *(*ptr + i * MAP_WIDTH + j) = 0;
308 |
309 | if (!inVector((void**)ptr))
310 | pushAddress((void**)ptr, VARIABLE);
311 |
312 | }
313 |
314 | }
315 |
316 | // Declare the size bag for bag shuffling the sizes
317 | void declare_size_bag(SizeBag** ptr, unsigned short mode, bool customMode)
318 | {
319 |
320 | if (*ptr == NULL)
321 | {
322 |
323 | *ptr = (SizeBag*)(SDL_calloc(1, sizeof(SizeBag)));
324 |
325 | if (mode == 0)
326 | {
327 |
328 | if (*ptr != NULL)
329 | {
330 |
331 | // In MULTRIS mode, sizes 1 - MAX_PIECE_SIZE are in the bag
332 | (*ptr)->sizesInBag = SDL_calloc(MAX_PIECE_SIZE, sizeof(unsigned short));
333 | (*ptr)->size = MAX_PIECE_SIZE;
334 |
335 | for (unsigned short i = 1; i <= MAX_PIECE_SIZE; i++)
336 | (*ptr)->sizesInBag[i - 1] = i;
337 |
338 | }
339 |
340 | }
341 | else if (mode != 0 && customMode == true)
342 | {
343 |
344 | if (*ptr != NULL)
345 | {
346 |
347 | // In CUSTOM mode, sizes 1 - MODE are in the bag
348 | (*ptr)->sizesInBag = SDL_calloc(mode, sizeof(unsigned short));
349 | (*ptr)->size = mode;
350 |
351 | for (unsigned short i = 1; i <= mode; i++)
352 | (*ptr)->sizesInBag[i - 1] = i;
353 |
354 | }
355 |
356 | }
357 | else if (mode != 0 && customMode == false)
358 | {
359 |
360 | // In NUMERICAL mode, only size MODE is in the bag
361 | (*ptr)->sizesInBag = SDL_calloc(1, sizeof(unsigned short));
362 | (*ptr)->size = 1;
363 |
364 | (*ptr)->sizesInBag[0] = mode;
365 |
366 | }
367 |
368 | if (!inVector((void**)ptr))
369 | pushAddress((void**)ptr, SIZE_BAG);
370 |
371 | }
372 |
373 | }
374 |
375 | //Declare the array of moving pieces in the title
376 | void declare_moving_title_pieces(piece*** ptr)
377 | {
378 |
379 | if (*ptr == NULL)
380 | {
381 |
382 | *ptr = getMovingPieces(makeTitlePieces());
383 |
384 | if (!inVector((void**)ptr))
385 | pushAddress((void**)ptr, MOVING_TITLE_PIECES);
386 |
387 | }
388 |
389 | }
390 |
391 | //Declare a UI_list
392 | void declare_UI_list(UI_list** ptr, int type)
393 | {
394 |
395 | if (*ptr == NULL)
396 | {
397 |
398 | //Call a different function depending on which list we are trying to make
399 | if (type == MODES_LIST)
400 | *ptr = create_Modes_List();
401 | else if (type == NUMERICAL_LIST)
402 | *ptr = create_Numerical_List();
403 | else if (type == CUSTOM_LIST)
404 | *ptr = create_Custom_List();
405 | else if (type == OPTIONS_LIST)
406 | *ptr = create_Options_List();
407 | else if (type == CONNECTION_LIST)
408 | *ptr = create_Connect_List();
409 | else if (type == MULTIPLAYER_LIST)
410 | *ptr = create_Multiplayer_List();
411 | else if (type == HOSTING_LIST)
412 | *ptr = create_Hosting_List();
413 | else if (type == PAUSE_LIST)
414 | *ptr = create_Pause_list();
415 |
416 | if (!inVector((void**)ptr))
417 | pushAddress((void**)ptr, UI_LIST);
418 |
419 | }
420 |
421 | }
422 |
423 | //Function for freeing memory of all variables declared during the current game_state
424 | void freeVars()
425 | {
426 |
427 | //By passing NULL to pushAddress(), we get the address of the varVector
428 | varVector** vector = pushAddress(NULL, TYPE_NA);
429 |
430 | for (int i = 0; i < (*vector)->count; i++)
431 | {
432 |
433 | //Check if memory is actually allocated at *ptr[i]
434 | if (*(void**)((*vector)->ptrs[i]) != NULL)
435 | {
436 |
437 | //If it is, then free it
438 | //We call different freeing methods depending on the type of variable or
439 | //object
440 | if ((*vector)->types[i] == VARIABLE)
441 | SDL_free(*(void**)((*vector)->ptrs[i]));
442 | else if ((*vector)->types[i] == PIECE)
443 | delPiece((piece**)&*(void**)((*vector)->ptrs[i]));
444 | else if ((*vector)->types[i] == TEXTURE)
445 | SDL_DestroyTexture(*(SDL_Texture**)((*vector)->ptrs[i]));
446 | else if ((*vector)->types[i] == MOVING_TITLE_PIECES)
447 | {
448 |
449 | for (unsigned short j = 0; j < NUM_MOVING_TITLE_PIECES; j++)
450 | delPiece(&((piece**)*(void**)(*vector)->ptrs[i])[j]);
451 | SDL_free((piece**)*(void**)(*vector)->ptrs[i]);
452 |
453 | }
454 | else if ((*vector)->types[i] == UI_LIST)
455 | delete_UI_list((UI_list**)&*(void**)((*vector)->ptrs[i]));
456 | else if ((*vector)->types[i] == SIZE_BAG)
457 | {
458 |
459 | SDL_free(((SizeBag*)*(void**)(*vector)->ptrs[i])->sizesInBag);
460 | SDL_free(((SizeBag*)*(void**)(*vector)->ptrs[i]));
461 |
462 | }
463 |
464 | }
465 |
466 | //Reset the ptr to NULL
467 | *(void**)((*vector)->ptrs[i]) = NULL;
468 |
469 | }
470 |
471 | //Free ptrs and types arrays
472 | SDL_free((*vector)->ptrs);
473 | SDL_free((*vector)->types);
474 |
475 | //Free the memory taken by the varVector
476 | SDL_free(*vector);
477 | *vector = NULL;
478 |
479 | }
480 |
481 | // Function for extracting all of the values from a string of bytes separated by a delimiter character
482 | // The function returns a dynamically allocated 2D array that should be freed later with SDL_free().
483 | // Ensure to free each element as well as the array itself.
484 | char** extractStringsFromDelimitedBytes(char* data, int dataLength, int* numValues, char delim)
485 | {
486 |
487 | // values is an array of strings of bytes
488 | char** values = NULL;
489 | *numValues = 0;
490 | char* currentStringValue = NULL;
491 |
492 | // Go through the entire string of bytes
493 | for (int dataIndex = 0; dataIndex < dataLength; dataIndex++)
494 | {
495 |
496 | // If the current byte is not a delimter.
497 | if (data[dataIndex] != delim)
498 | {
499 |
500 | // Then we are currently reading a value. So build the current value by appending the bytes 1-by-1 to
501 | // currentStringValue
502 | if (currentStringValue == NULL)
503 | {
504 |
505 | currentStringValue = SDL_calloc(2, sizeof(char));
506 | currentStringValue[0] = data[dataIndex];
507 | currentStringValue[1] = '\0';
508 |
509 | }
510 | else
511 | {
512 |
513 | int newLen = SDL_strlen(currentStringValue) + 1 + 1;
514 | currentStringValue = SDL_realloc(currentStringValue, newLen * sizeof(char));
515 | SDL_strlcat(currentStringValue, &(data[dataIndex]), newLen);
516 |
517 | }
518 |
519 | } // If the current byte is a delimiter, we have finished reading the current value.
520 | else if (currentStringValue != NULL) // Skip if current value is of length zero
521 | {
522 |
523 | // So append the current value to the list of values
524 | if (*numValues == 0)
525 | {
526 |
527 | values = SDL_calloc(1, sizeof(char*));
528 | *numValues = *numValues + 1;
529 |
530 | }
531 | else
532 | {
533 |
534 | values = SDL_realloc(values, (*numValues + 1) * sizeof(char*));
535 | *numValues = *numValues + 1;
536 |
537 | }
538 |
539 | values[*numValues - 1] = SDL_calloc(SDL_strlen(currentStringValue) + 1, sizeof(char));
540 | SDL_strlcpy(values[*numValues - 1], currentStringValue, SDL_strlen(currentStringValue) + 1);
541 |
542 | // Free the currentStringValue to avoid memory leaks
543 | SDL_free(currentStringValue);
544 | currentStringValue = NULL;
545 |
546 | }
547 |
548 | }
549 |
550 | // Ensure to free currentStringValue before leaving the function.
551 | if (currentStringValue != NULL)
552 | {
553 |
554 | SDL_free(currentStringValue);
555 | currentStringValue = NULL;
556 |
557 | }
558 |
559 | return values;
560 |
561 | }
--------------------------------------------------------------------------------
/src/memory.h:
--------------------------------------------------------------------------------
1 | #ifndef MEMORY_H_
2 | #define MEMORY_H_
3 |
4 | #include "MGF.h"
5 |
6 | varVector** pushAddress(void** ptr, unsigned short type);
7 | bool inVector(void** ptr);
8 | void declare_short(void** ptr, short value);
9 | void declare_unsigned_short(void** ptr, unsigned short value);
10 | void declare_unsigned_long(void** ptr, unsigned long value);
11 | void declare_double(void** ptr, double value);
12 | void declare_int(void** ptr, int value);
13 | void declare_char(void** ptr, char value);
14 | void declare_bool(void** ptr, bool value);
15 | void declare_unsigned_int(void** ptr, unsigned int value);
16 | void declare_Piece(piece** ptr, piece* Piece);
17 | void declare_Piece_Text(SDL_Texture** ptr, piece* Piece, bool drawCenterDot);
18 | void declare_HUD_Text(SDL_Texture** ptr, int type);
19 | void declare_map_matrix(int** ptr);
20 | void declare_size_bag(SizeBag** ptr, unsigned short mode, bool customMode);
21 | void declare_moving_title_pieces(piece*** ptr);
22 | void declare_UI_list(UI_list** ptr, int type);
23 | void freeVars();
24 | char** extractStringsFromDelimitedBytes(char* data, int dataLength, int* numValues, char delim);
25 |
26 | #endif
--------------------------------------------------------------------------------
/src/multiplayerLobby.h:
--------------------------------------------------------------------------------
1 | #ifndef MULTIPLAYERLOBBY_H_
2 | #define MULTIPLAYERLOBBY_H_
3 |
4 | #include "MGF.h"
5 |
6 | unsigned short multiplayerLobby(piece** Piece, char* serverMessage);
7 | void loadIPandPortForConnectScreen(char* ipString, char* portString);
8 | void loadPortForHostScreen(char* portString);
9 |
10 | #endif
--------------------------------------------------------------------------------
/src/network.c:
--------------------------------------------------------------------------------
1 | #include "MGF.h"
2 |
3 | // Function for disconnecting from a server
4 | void disconnectFromServer()
5 | {
6 |
7 | // Close the socket
8 | SDLNet_TCP_Close(globalInstance->serverSocket);
9 | // Free the socket set
10 | SDLNet_FreeSocketSet(globalInstance->serverSocketSet);
11 | // And set MULTIPLAYER to false
12 | MULTIPLAYER = false;
13 |
14 | }
15 |
16 | // Function for sending garbage data to the server
17 | void sendGarbageToServer(int amount, int* lastPulseTime)
18 | {
19 |
20 | // Convert the amount into a string
21 | char* amountString = SDL_calloc(getIntLength(amount) + 1, sizeof(char));
22 | SDL_itoa(amount, amountString, 10);
23 | amountString[getIntLength(amount)] = '\0';
24 |
25 | // Prepend the "GARBAGE=" header to the data string
26 | int len = SDL_strlen("GARBAGE=") + SDL_strlen(amountString) + 1;
27 | char* data = SDL_calloc(len, sizeof(char));
28 | SDL_strlcpy(data, "GARBAGE=", len);
29 | SDL_strlcat(data, amountString, len);
30 |
31 | // Send data to server
32 | SDLNet_TCP_Send(globalInstance->serverSocket, data, len);
33 |
34 | // Keep track of when last communication with server was
35 | *lastPulseTime = SDL_GetTicks();
36 |
37 | // Free strings to avoid memory leaks
38 | SDL_free(amountString);
39 | SDL_free(data);
40 |
41 | }
42 |
43 | // Function for sending a list of rows to remove to the server
44 | // Data is sent to server in the format "REMOVE=[row #]|[row #]|...|[row #]|"
45 | void sendRemovalToServer(int* rows, int numRows, int *lastPulseTime)
46 | {
47 |
48 | // Add header to packet
49 | int len = SDL_strlen("REMOVE=") + 1;
50 | char* data = SDL_calloc(len, sizeof(char));
51 | SDL_strlcpy(data, "REMOVE=", len);
52 |
53 | // Go through each row in the array of rows and append them to our data string separated by "|"
54 | for (unsigned short i = 0; i < numRows; i++)
55 | {
56 |
57 | char* rowString = SDL_calloc(getIntLength(rows[i]), sizeof(char));
58 | SDL_itoa(rows[i], rowString, 10);
59 | rowString[getIntLength(rows[i])] = '\0';
60 |
61 | len = SDL_strlen(data) + SDL_strlen(rowString) + SDL_strlen("|") + 1;
62 | data = SDL_realloc(data, len * sizeof(char));
63 | SDL_strlcat(data, rowString, len);
64 | SDL_strlcat(data, "|", len);
65 |
66 | SDL_free(rowString);
67 |
68 | }
69 |
70 | // Send the data to the server
71 | SDLNet_TCP_Send(globalInstance->serverSocket, data, len);
72 |
73 | // Keep track of the last time we communicated with the server
74 | *lastPulseTime = SDL_GetTicks();
75 |
76 | // Free the data to avoid memory leaks
77 | SDL_free(data);
78 |
79 | }
80 |
81 | // Function for sending current sizeBag to the server
82 | // Sent in the format "SIZEBAG=|size_1|size_2|size_3|...|"
83 | void sendSizeBagToServer(SizeBag* sizeBag, int* lastPuleTime)
84 | {
85 |
86 | // Add header to packet
87 | int len = SDL_strlen("SIZEBAG=") + 1;
88 | char* data = SDL_calloc(len, sizeof(char));
89 | SDL_strlcpy(data, "SIZEBAG=", len);
90 |
91 | // Go through each size in the bag and append it as a string to our data
92 | for (unsigned short i = 0; i < sizeBag->size; i++)
93 | {
94 |
95 | char* sizeString = SDL_calloc(getIntLength(sizeBag->sizesInBag[i]), sizeof(char));
96 | SDL_itoa(sizeBag->sizesInBag[i], sizeString, 10);
97 | sizeString[getIntLength(sizeBag->sizesInBag[i])] = '\0';
98 |
99 | len = SDL_strlen(data) + SDL_strlen(sizeString) + SDL_strlen("|") + 1;
100 | data = SDL_realloc(data, len * sizeof(char));
101 | SDL_strlcat(data, sizeString, len);
102 | SDL_strlcat(data, "|", len);
103 |
104 | // Free the sizeString to avoid memory leaks
105 | SDL_free(sizeString);
106 |
107 | }
108 |
109 | // Send the data to the server
110 | SDLNet_TCP_Send(globalInstance->serverSocket, data, len);
111 |
112 | // Keep track of the last time we communicated with the server
113 | *lastPuleTime = SDL_GetTicks();
114 |
115 | // Free the data to avoid memory leaks
116 | SDL_free(data);
117 |
118 | }
119 |
120 | // Function for sending current position to the server
121 | void sendPositionToServer(int X, int Y, int* lastPulseTime)
122 | {
123 |
124 | // Convert X value into a string
125 | char* xString = SDL_calloc(getIntLength(X) + 1, sizeof(char));
126 | SDL_itoa(X, xString, 10);
127 | xString[getIntLength(X)] = '\0';
128 |
129 | // Conver Y value into a string
130 | char* yString = SDL_calloc(getIntLength(Y) + 1, sizeof(char));
131 | SDL_itoa(Y, yString, 10);
132 | yString[getIntLength(Y)] = '\0';
133 |
134 | // Send the position to the server in the format "POSITION=X|Y|"
135 | int len = SDL_strlen("POSITION=") + SDL_strlen(xString) + SDL_strlen("|") + SDL_strlen(yString) + SDL_strlen("|") + 1;
136 | char* data = SDL_calloc(len, sizeof(char));
137 | SDL_strlcpy(data, "POSITION=", len);
138 | SDL_strlcat(data, xString, len);
139 | SDL_strlcat(data, "|", len);
140 | SDL_strlcat(data, yString, len);
141 | SDL_strlcat(data, "|", len);
142 |
143 | SDL_free(xString);
144 | SDL_free(yString);
145 |
146 | SDLNet_TCP_Send(globalInstance->serverSocket, data, len);
147 |
148 | *lastPulseTime = SDL_GetTicks();
149 |
150 | SDL_free(data);
151 |
152 | }
153 |
154 | // Function for sending level data to the server
155 | void sendLevelToServer(int level, int* lastPulseTime)
156 | {
157 |
158 | // Convert the level into a string
159 | char* levelString = SDL_calloc(getIntLength(level) + 1, sizeof(char));
160 | SDL_itoa(level, levelString, 10);
161 | levelString[getIntLength(level)] = '\0';
162 |
163 | // Prepend the "LEVEL=" header to the data string
164 | int len = SDL_strlen("LEVEL=") + SDL_strlen(levelString) + 1;
165 | char* data = SDL_calloc(len, sizeof(char));
166 | SDL_strlcpy(data, "LEVEL=", len);
167 | SDL_strlcat(data, levelString, len);
168 |
169 | // Ensure data ends in null-byte
170 | data[len] = '\0';
171 |
172 | // Send data to server
173 | SDLNet_TCP_Send(globalInstance->serverSocket, data, len);
174 |
175 | // Keep track of when last communication with server was
176 | *lastPulseTime = SDL_GetTicks();
177 |
178 | // Free the levelString to avoid memory leaks
179 | SDL_free(levelString);
180 |
181 | SDL_free(data);
182 |
183 | }
184 |
185 | // Function for sending lines data to the server
186 | void sendLinesToServer(int lines, int* lastPulseTime)
187 | {
188 |
189 | // Convert the lines value into a string
190 | char* linesString = SDL_calloc(getIntLength(lines) + 1, sizeof(char));
191 | SDL_itoa(lines, linesString, 10);
192 | linesString[getIntLength(lines)] = '\0';
193 |
194 | // Prepend the "LINES=" header to the data string
195 | int len = SDL_strlen("LINES=") + SDL_strlen(linesString) + 1;
196 | char* data = SDL_calloc(len, sizeof(char));
197 | SDL_strlcpy(data, "LINES=", len);
198 | SDL_strlcat(data, linesString, len);
199 |
200 | // Ensure data ends in null-byte
201 | data[len] = '\0';
202 |
203 | // Send data to server
204 | SDLNet_TCP_Send(globalInstance->serverSocket, data, len);
205 |
206 | // Keep track of when last communication with server was
207 | *lastPulseTime = SDL_GetTicks();
208 |
209 | // Free the linesString to avoid memory leaks
210 | SDL_free(linesString);
211 |
212 | SDL_free(data);
213 |
214 | }
215 |
216 | // Send the current CURRENT piece to the server
217 | void sendCurrentPieceToServer(piece* currentPiece, int *lastPulseTime)
218 | {
219 |
220 | // Convert the piece to a string
221 | char* currentPieceAsString = convertPieceToString(currentPiece);
222 |
223 | // Pre-pend "CURRENT=" to the string
224 | int len = SDL_strlen("CURRENT=") + SDL_strlen(currentPieceAsString) + 1;
225 | char* data = SDL_calloc(len, sizeof(char));
226 | SDL_strlcpy(data, "CURRENT=", len);
227 | SDL_strlcat(data, currentPieceAsString, len);
228 |
229 | // Send the string to the server
230 | SDLNet_TCP_Send(globalInstance->serverSocket, data, len);
231 |
232 | // Keep track of when last communication with server was
233 | *lastPulseTime = SDL_GetTicks();
234 |
235 | // Free the strings to avoid memory leaks
236 | SDL_free(currentPieceAsString);
237 | SDL_free(data);
238 |
239 | }
240 |
241 | // Function for sending a NEXT piece to the server
242 | void sendNextPieceToServer(piece* nextPiece, int* lastPulseTime)
243 | {
244 |
245 | // Conver the piece to a string
246 | char* nextPieceAsString = convertPieceToString(nextPiece);
247 |
248 | // Pre-pend "NEXT=" to the string
249 | int len = SDL_strlen("NEXT=") + SDL_strlen(nextPieceAsString) + 1;
250 | char* data = SDL_calloc(len, sizeof(char));
251 | SDL_strlcpy(data, "NEXT=", len);
252 | SDL_strlcat(data, nextPieceAsString, len);
253 |
254 | // Send the string to the server
255 | SDLNet_TCP_Send(globalInstance->serverSocket, data, len);
256 |
257 | // Keep track of the last time data was sent to the server
258 | *lastPulseTime = SDL_GetTicks();
259 |
260 | // Free the strings to avoid memory leaks
261 | SDL_free(nextPieceAsString);
262 | SDL_free(data);
263 |
264 | }
265 |
266 | // Function for sending a HOLD piece to the server
267 | void sendHoldPieceToServer(piece* holdPiece, int* lastPulseTime)
268 | {
269 |
270 | // Conver the piece to a string
271 | char* holdPieceAsString = convertPieceToString(holdPiece);
272 |
273 | // Pre-pend "HOLD=" to the string
274 | int len = SDL_strlen("HOLD=") + SDL_strlen(holdPieceAsString) + 1;
275 | char* data = SDL_calloc(len, sizeof(char));
276 | SDL_strlcpy(data, "HOLD=", len);
277 | SDL_strlcat(data, holdPieceAsString, len);
278 |
279 | // Send the string to the server
280 | SDLNet_TCP_Send(globalInstance->serverSocket, data, len);
281 |
282 | // Keep track of the last time data was sent to the server
283 | *lastPulseTime = SDL_GetTicks();
284 |
285 | // Free the strings to avoid memory leaks
286 | SDL_free(holdPieceAsString);
287 | SDL_free(data);
288 |
289 | }
290 |
291 | // Function for sending score data to the server
292 | void sendScoretoServer(int score, int* lastPulseTime)
293 | {
294 |
295 | // Convert the score into a string
296 | char* scoreString = SDL_calloc(getIntLength(score) + 1, sizeof(char));
297 | SDL_itoa(score, scoreString, 10);
298 | scoreString[getIntLength(score)] = '\0';
299 |
300 | // Prepend the "SCORE=" header to the data string
301 | int len = SDL_strlen("SCORE=") + SDL_strlen(scoreString) + 1;
302 | char* data = SDL_calloc(len, sizeof(char));
303 | SDL_strlcpy(data, "SCORE=", len);
304 | SDL_strlcat(data, scoreString, len);
305 |
306 | // Ensure data ends in null-byte
307 | data[len] = '\0';
308 |
309 | // Send data to server
310 | SDLNet_TCP_Send(globalInstance->serverSocket, data, len);
311 |
312 | // Keep track of when last communication with server was
313 | *lastPulseTime = SDL_GetTicks();
314 |
315 | // Free the scoreString to avoid memory leaks
316 | SDL_free(scoreString);
317 |
318 | SDL_free(data);
319 |
320 | }
321 |
322 | // Function for sending mapData to the server
323 | void sendMapToServer(int* mapData, int* lastPuleTime)
324 | {
325 |
326 | int len = MAP_WIDTH * MAP_HEIGHT + SDL_strlen("MAP=") + 1;
327 | char* data = SDL_calloc(len, sizeof(char));
328 | SDL_strlcpy(data, "MAP=", len);
329 |
330 | int index = SDL_strlen("MAP=");
331 |
332 | for (unsigned short i = 0; i < MAP_HEIGHT; i++)
333 | {
334 |
335 | for (unsigned short j = 0; j < MAP_WIDTH; j++)
336 | {
337 |
338 | // MAP data is sent as a string of digits
339 | data[index] = '0' + (char)*(mapData + i * MAP_WIDTH + j);
340 |
341 | index++;
342 |
343 | }
344 |
345 | }
346 |
347 | // MAP data ends with a null-terminator
348 | data[len] = '\0';
349 |
350 | // Send the data
351 | SDLNet_TCP_Send(globalInstance->serverSocket, data, len);
352 |
353 | // Keep track of the last time we sent data to the server.
354 | *lastPuleTime = SDL_GetTicks();
355 |
356 | // Then free the data to avoid memory leaks
357 | SDL_free(data);
358 |
359 | }
360 |
361 | // Function for connecting to a server in a separate thread
362 | int openConnection(void* data)
363 | {
364 |
365 | // Keep track of the ID for this tread so it can be canceled if it timesout
366 | globalInstance->connectionThreadID = SDL_GetThreadID(NULL);
367 |
368 | // Attempt the connection
369 | globalInstance->serverSocket = SDLNet_TCP_Open(&(globalInstance->serverIP));
370 |
371 | // If failed to open connection, return the error message to the main thread via the data variable
372 | if (globalInstance->serverSocket == NULL)
373 | {
374 |
375 | const char* temp = SDLNet_GetError();
376 | SDL_strlcpy(data, temp, HOST_GAME_DATA_MAX_LENGTH);
377 |
378 | return 0;
379 |
380 | }
381 |
382 | // Keep track of if the SDLNet_TCP_Open function returned
383 | SDL_strlcpy(data, "true", HOST_GAME_DATA_MAX_LENGTH);
384 |
385 | return 0;
386 |
387 | }
388 |
389 | // Function for starting a server in a separate thread. The data variable is used to transmit information between the server thread and the
390 | // game thread
391 | int hostGame(void* data)
392 | {
393 |
394 | IPaddress localIP;
395 |
396 | // If we can't resolve the host
397 | if (SDLNet_ResolveHost(&localIP, NULL, SDL_atoi((char*)data)) == -1)
398 | {
399 |
400 | // Send the error back to the game thread via the data variable
401 | const char* temp = SDLNet_GetError();
402 | SDL_strlcpy(data, temp, HOST_GAME_DATA_MAX_LENGTH);
403 |
404 | return 0;
405 |
406 | }
407 |
408 | // If the server returns with a value other than 0
409 | if (startServer(localIP, SERVER_TICK_RATE) != 0)
410 | {
411 |
412 | // Send the error back to the game thread via the data variable
413 | const char* temp = SDLNet_GetError();
414 | SDL_strlcpy(data, temp, HOST_GAME_DATA_MAX_LENGTH);
415 |
416 | }
417 |
418 | return 0;
419 |
420 | }
--------------------------------------------------------------------------------
/src/network.h:
--------------------------------------------------------------------------------
1 | #ifndef NETWORK_H_
2 | #define NETWORK_H_
3 |
4 | #include "MGF.h"
5 |
6 | void disconnectFromServer();
7 | void sendMapToServer(int* mapData, int* lastPuleTime);
8 | void sendScoretoServer(int score, int* lastPulseTime);
9 | void sendNextPieceToServer(piece* nextPiece, int* lastPulseTime);
10 | void sendLevelToServer(int level, int* lastPulseTime);
11 | void sendCurrentPieceToServer(piece* currentPiece, int *lastPulseTime);
12 | void sendPositionToServer(int X, int Y, int* lastPulseTime);
13 | void sendLinesToServer(int lines, int* lastPulseTime);
14 | void sendHoldPieceToServer(piece* holdPiece, int* lastPulseTime);
15 | void sendSizeBagToServer(SizeBag* sizeBag, int* lastPuleTime);
16 | void sendGarbageToServer(int amount, int* lastPulseTime);
17 | void sendRemovalToServer(int* rows, int numRows, int *lastPulseTime);
18 | int openConnection(void* data);
19 | int hostGame(void* data);
20 |
21 | #endif
--------------------------------------------------------------------------------
/src/playMode.h:
--------------------------------------------------------------------------------
1 | #ifndef PLAYMODE_H_
2 | #define PLAYMODE_H_
3 |
4 | #include "MGF.h"
5 |
6 | unsigned short playMode(piece* firstPiece, char* serverMessage);
7 | void removeSizeFromBag(SizeBag* sizeBag, unsigned short size, unsigned short mode, bool customMode, SDL_Texture* sizeBagTexture);
8 | void removeSizeFromBagTexture(SDL_Texture* sizeBagTexture, unsigned short size);
9 | void resetSizeBagTexture(SDL_Texture* sizeBagTexture);
10 | double calcSpeed(unsigned short level);
11 | unsigned short calcLinesUntilLevelup(unsigned short linesAtCurrentLevel, unsigned short currentLevel);
12 | unsigned short calcGhostY(piece* Piece, int X, int startY, int* mapData, int mapWidth, int mapHeight);
13 | bool playOverAnimation(SDL_Texture* foreground, SDL_Texture* opponentForeground, unsigned short mapWidth, unsigned short mapHeight);
14 | void updateLines(unsigned short lines, SDL_Texture** linesTexture);
15 | void updateLevel(unsigned short level, SDL_Texture* levelTexture);
16 | void updateScore(unsigned int score, SDL_Texture* scoreTexture);
17 | bool playLineAnimation(SDL_Texture* foreground, unsigned short row, bool *clearingLine, int *mapData, unsigned short* numCompleted);
18 | void removeLine(unsigned short row, int* mapData, SDL_Texture* foreground, unsigned short mapWidth);
19 | bool lineIsComplete(int* mapData, unsigned short row, unsigned short mapWidth);
20 | unsigned short completedLine(int* mapData, int Y, piece Piece, int** returnRows, int mapWidth, int mapHeight);
21 | void adjustNewPiece(piece* Piece, signed short* X, unsigned short mapWidth);
22 | bool isColliding(piece Piece, int X, double* Y, int direction, int* mapData, int mapWidth, int mapHeight);
23 | void move(char keyPress, signed short *X, piece Piece, unsigned short mapWidth);
24 | void addGarbageLines(unsigned short numOfLines, int* mapData, SDL_Texture* foreground, unsigned short mapWidth, unsigned short mapHeight);
25 | void playOpponentLineAnimation(SDL_Texture* foreground, unsigned short row, bool *clearingLine, unsigned short* numCompleted);
26 | void updateOpponentForeground(SDL_Texture* foreground, char* mapString);
27 | piece* generateSeededPiece(SizeBag* sizeBag, int seed);
28 |
29 | #endif
--------------------------------------------------------------------------------
/src/rotate.c:
--------------------------------------------------------------------------------
1 | #include "MGF.h"
2 |
3 | //Rotate a piece
4 | void rotatePiece(piece *Piece, unsigned short direction) {
5 |
6 | //If rotating clockwise, we inverse the piece and then mirror it
7 | if (direction == CW) {
8 |
9 | inversePiece(Piece);
10 | mirrorPieceOverY(Piece);
11 |
12 | }
13 | else if (direction == CCW) { //If rotating counter-clockwise, we mirror and then inverse
14 |
15 | mirrorPieceOverY(Piece);
16 | inversePiece(Piece);
17 |
18 | }
19 |
20 | }
21 |
22 | //Inverse the x and y values of every block in a piece
23 | void inversePiece(piece* Piece) {
24 |
25 | //Swap all X and Y values
26 | for (unsigned short i = 0; i < Piece->numOfBlocks; i++)
27 | swap(Piece->blocks[i].X, Piece->blocks[i].Y, signed short);
28 | swap(Piece->centerBlock->X, Piece->centerBlock->Y, signed short);
29 |
30 | //Swap width & height, minX & minY
31 | swap(Piece->width, Piece->height, unsigned short);
32 | swap(Piece->minX, Piece->minY, unsigned short);
33 |
34 | }
35 |
36 | //Mirror the piece over a vertical center line
37 | void mirrorPieceOverY(piece* Piece) {
38 |
39 | signed short center = Piece->minX + Piece->width / 2;
40 |
41 | for (unsigned short i = 0; i < Piece->numOfBlocks; i++) {
42 |
43 | Piece->blocks[i].X = center - Piece->blocks[i].X;
44 | if (i == 0 || Piece->blocks[i].X < Piece->minX)
45 | Piece->minX = Piece->blocks[i].X;
46 |
47 | }
48 |
49 | Piece->centerBlock->X = center - Piece->centerBlock->X;
50 |
51 | }
--------------------------------------------------------------------------------
/src/rotate.h:
--------------------------------------------------------------------------------
1 | #ifndef ROTATE_H_
2 | #define ROTATE_H_
3 |
4 | #include "MGF.h"
5 |
6 | void rotatePiece(piece *Piece, unsigned short direction);
7 | void inversePiece(piece* Piece);
8 | void mirrorPieceOverY(piece* Piece);
9 |
10 | #endif
--------------------------------------------------------------------------------
/src/server.c:
--------------------------------------------------------------------------------
1 | #include "MGF.h"
2 |
3 | // Function for starting a dedicated server
4 | int startDedicatedServer(int argc, char* argv[])
5 | {
6 |
7 | // Added just to get rid of unuused-parameter warning
8 | (void)argc;
9 | (void)argv;
10 |
11 | printf("Starting server...\n");
12 |
13 | // Initialize SDL Stuff
14 | if (SDL_Init(0) == -1)
15 | {
16 |
17 | printf("SDL_Init: %s\n", SDL_GetError());
18 | exit(1);
19 |
20 | }
21 | if (SDLNet_Init() == -1)
22 | {
23 |
24 | printf("SDLNet_Init: %s\n", SDLNet_GetError());
25 | exit(2);
26 |
27 | }
28 |
29 | IPaddress ip;
30 | bool portSpecified = false;
31 |
32 | bool restartAfterClose = false;
33 |
34 | // Check for launch arguments
35 | for (unsigned short i = 1; i < argc; i++)
36 | {
37 |
38 | // -p launch argument takes the next argument as the port
39 | if (SDL_strcmp(argv[i], "-p") == 0)
40 | {
41 |
42 | if (i < argc - 1)
43 | {
44 |
45 | portSpecified = true;
46 |
47 | if (SDLNet_ResolveHost(&ip, NULL, SDL_atoi(argv[i + 1])) == -1)
48 | {
49 |
50 | printf("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
51 | exit(1);
52 |
53 | }
54 |
55 | }
56 |
57 | } // -r launch argument sets the server to restart after the game ends
58 | else if (SDL_strcmp(argv[i], "-r") == 0)
59 | restartAfterClose = true;
60 |
61 | }
62 |
63 | // Close server if no port was specified
64 | if (portSpecified == false)
65 | {
66 |
67 | printf("No port specified.\n");
68 | exit(1);
69 |
70 | }
71 |
72 | do
73 | {
74 |
75 | // Print error if startServer returns a value other than 0
76 | if (startServer(ip, SERVER_TICK_RATE) != 0)
77 | printf("Error starting server: %s\n", SDLNet_GetError());
78 |
79 | if (restartAfterClose)
80 | printf("Restarting server...\n");
81 |
82 | }
83 | while (restartAfterClose);
84 |
85 | //Close SDL Stuff
86 | SDLNet_Quit();
87 | SDL_Quit();
88 |
89 | return 0;
90 |
91 | }
92 |
93 | // Function for starting a server (dedicated or from in game)
94 | int startServer(IPaddress address, int tickRate)
95 | {
96 |
97 | const double TARGET_FRAME_TIME = (double)1 / (double)tickRate;
98 | Uint32 ticksLastFrame = SDL_GetTicks();
99 |
100 | // Open TCP socket
101 | TCPsocket socket;
102 | socket = SDLNet_TCP_Open(&address);
103 |
104 | // Add server socket to socket set
105 | SDLNet_SocketSet serverSocketSet = SDLNet_AllocSocketSet(1);
106 | SDLNet_TCP_AddSocket(serverSocketSet, socket);
107 |
108 | // Return 2 if failed to open socket
109 | if (!socket)
110 | return 2;
111 | else
112 | printf("Opened server on port %d.\n", SDL_SwapBE16(address.port));
113 |
114 | bool running = true;
115 |
116 | int maxPlayers = 2;
117 | int numConnectedPlayers = 0;
118 |
119 | // Array of connected clients
120 | TCPsocket* clients = SDL_calloc(maxPlayers, sizeof(TCPsocket));
121 |
122 | // Array of socket sets. Each connected client has its own socket set.
123 | SDLNet_SocketSet* socketSets = SDL_calloc(maxPlayers, sizeof(SDLNet_SocketSet));
124 |
125 | printf("Waiting for player %d to join...\n", numConnectedPlayers + 1);
126 |
127 | // Variables used for checking for timedout players
128 | int *lastPulse = NULL;
129 | Uint32 TIMEOUT_SECONDS = 10;
130 |
131 | // Store the names of the players
132 | char** names = NULL;
133 | int numNames = 0;
134 | bool sentNames = false;
135 |
136 | // Variables for the countdown
137 | int playersReady = 0;
138 | bool countdownGoing = false;
139 | int currentCountdownValue = 4;
140 | Uint32 timeAllPlayersConnected = 0;
141 | bool gameStarted = false;
142 |
143 | // Keep track of when we have received the first packet from a particular player
144 | bool* receivedFirstPacket = SDL_calloc(maxPlayers, sizeof(bool));
145 |
146 | LoopStart:;
147 | while (running == true)
148 | {
149 |
150 | // Accept a connection if there is incoming data on the server socket
151 | TCPsocket newSocket = NULL;
152 | if (SDLNet_CheckSockets(serverSocketSet, 0))
153 | newSocket = SDLNet_TCP_Accept(socket);
154 |
155 | // If a client connected
156 | if (newSocket != NULL)
157 | {
158 |
159 | // Allow the connection as long as the server is not full
160 | if (numConnectedPlayers < maxPlayers)
161 | {
162 |
163 | clients[numConnectedPlayers] = newSocket;
164 |
165 | // Get their IP address
166 | IPaddress* remoteip;
167 | remoteip = SDLNet_TCP_GetPeerAddress(clients[numConnectedPlayers]);
168 |
169 | // Print error if error getting the IP address of the connected client
170 | if (!remoteip)
171 | {
172 |
173 | printf("SDLNet_TCP_GetPeerAddress: %s\n", SDLNet_GetError());
174 | continue;
175 |
176 | }
177 |
178 | // Add socket for current player to their own socket set
179 | socketSets[numConnectedPlayers] = SDLNet_AllocSocketSet(1);
180 | SDLNet_TCP_AddSocket(socketSets[numConnectedPlayers], clients[numConnectedPlayers]);
181 |
182 | // Keep track of number of connected players
183 | int id = numConnectedPlayers + 1;
184 |
185 | // Print their IP to the console
186 | Uint32 ipaddr = SDL_SwapBE32(remoteip->host);
187 | int ipParts[] = { ipaddr >> 24, (ipaddr >> 16) & 0xff, (ipaddr >> 8) & 0xff, ipaddr & 0xff };
188 | Uint16 port = SDL_SwapBE16(remoteip->port);
189 | printf("Player %d connected from %d.%d.%d.%d:%hu.\n", id, ipParts[0], ipParts[1], ipParts[2], ipParts[3], port);
190 |
191 | numConnectedPlayers++;
192 |
193 | // Keep track of the last time we received data from the client
194 | if (numConnectedPlayers == 1)
195 | {
196 |
197 | lastPulse = SDL_calloc(1, sizeof(int));
198 | lastPulse[0] = SDL_GetTicks();
199 |
200 | }
201 | else if (lastPulse != NULL && numConnectedPlayers > 1)
202 | {
203 |
204 | lastPulse = SDL_realloc(lastPulse, numConnectedPlayers * sizeof(int));
205 | lastPulse[numConnectedPlayers - 1] = SDL_GetTicks();
206 |
207 | }
208 |
209 | // If still waiting for more players to join
210 | if (numConnectedPlayers < maxPlayers)
211 | {
212 |
213 | // Let user know we are still waiting on additional players
214 | printf("Waiting for player %d to join...\n", numConnectedPlayers + 1);
215 |
216 | // Also let the connected player know that we are waiting for players to join
217 | char message[] = "Waiting for players to join";
218 | SDLNet_TCP_Send(clients[numConnectedPlayers - 1], message, SDL_strlen(message) + 1);
219 |
220 | }
221 | else
222 | {
223 |
224 | // Once all players connect, let all players know to press SELECT when ready
225 | for (int i = 0; i < maxPlayers; i++)
226 | {
227 |
228 | printf("Telling player %d to ready up...\n", i + 1);
229 |
230 | char message[] = "Press SELECT when ready";
231 | SDLNet_TCP_Send(clients[i], message, SDL_strlen(message) + 1);
232 |
233 | }
234 |
235 | }
236 |
237 | }
238 | else // If the server is full, disconnect the client and let them know the server is full
239 | {
240 |
241 | char message[] = "Server is full";
242 | SDLNet_TCP_Send(newSocket, message, SDL_strlen(message) + 1);
243 |
244 | SDLNet_TCP_Close(newSocket);
245 |
246 | }
247 |
248 | }
249 |
250 | // If at least one player has connected
251 | if (numConnectedPlayers > 0)
252 | {
253 |
254 | // If all players are ready and we have synced all the names and we haven't started the countdown yet
255 | if (playersReady == maxPlayers && sentNames == true && countdownGoing == false)
256 | {
257 |
258 | // Send the RNG seed to all players
259 | // Seed is just the current time
260 | int startSeed = (int)time(NULL);
261 | for (int playerIndex = 0; playerIndex < maxPlayers; playerIndex++)
262 | {
263 |
264 | char* seedAsString = SDL_calloc(getIntLength(startSeed), sizeof(char));
265 | SDL_itoa(startSeed, seedAsString, 10);
266 |
267 | int len = SDL_strlen("SEED=") + getIntLength(startSeed) + 1;
268 | char* seedPacket = SDL_calloc(len, sizeof(char));
269 | SDL_strlcpy(seedPacket, "SEED=", len);
270 | SDL_strlcat(seedPacket, seedAsString, len);
271 |
272 | SDLNet_TCP_Send(clients[playerIndex], seedPacket, len);
273 |
274 | SDL_free(seedAsString);
275 | SDL_free(seedPacket);
276 |
277 | }
278 |
279 | // Start the countdown at 3 seconds
280 | countdownGoing = true;
281 |
282 | timeAllPlayersConnected = SDL_GetTicks();
283 | printf("All players ready.\n");
284 | printf("Starting game in 3...\n");
285 |
286 | for (int playerIndex = 0; playerIndex < maxPlayers; playerIndex++)
287 | {
288 |
289 | char message[] = "Game starting in 3";
290 | SDLNet_TCP_Send(clients[playerIndex], message, SDL_strlen(message) + 1);
291 |
292 | }
293 |
294 | } // Countdown until the game has started
295 | else if (countdownGoing == true && gameStarted == false)
296 | {
297 |
298 | if ((SDL_GetTicks() - timeAllPlayersConnected) / 1000 >= 3)
299 | {
300 |
301 | // Send "START" message to all players once countdown reaches zero
302 | printf("Starting game!\n");
303 |
304 | for (int playerIndex = 0; playerIndex < maxPlayers; playerIndex++)
305 | {
306 |
307 | char message[] = "START";
308 | SDLNet_TCP_Send(clients[playerIndex], message, SDL_strlen(message) + 1);
309 |
310 | }
311 |
312 | gameStarted = true;
313 |
314 | }
315 | else if ((SDL_GetTicks() - timeAllPlayersConnected) / 1000 >= 2 && currentCountdownValue > 1)
316 | {
317 |
318 | printf("Starting game in 1...\n");
319 |
320 | for (int playerIndex = 0; playerIndex < maxPlayers; playerIndex++)
321 | {
322 |
323 | char message[] = "Game starting in 1";
324 | SDLNet_TCP_Send(clients[playerIndex], message, SDL_strlen(message) + 1);
325 |
326 | }
327 |
328 | currentCountdownValue = 1;
329 |
330 | }
331 | else if ((SDL_GetTicks() - timeAllPlayersConnected) / 1000 >= 1 && currentCountdownValue > 2)
332 | {
333 |
334 | printf("Starting game in 2...\n");
335 |
336 | for (int playerIndex = 0; playerIndex < maxPlayers; playerIndex++)
337 | {
338 |
339 | char message[] = "Game starting in 2";
340 | SDLNet_TCP_Send(clients[playerIndex], message, SDL_strlen(message) + 1);
341 |
342 | }
343 |
344 | currentCountdownValue = 2;
345 |
346 | }
347 |
348 | }
349 |
350 | // Check all connected players
351 | for (int currentPlayerIndex = 0; currentPlayerIndex < numConnectedPlayers; currentPlayerIndex++)
352 | {
353 |
354 | // Check to see if the player has sent any data
355 | if (SDLNet_CheckSockets(socketSets[currentPlayerIndex], 0))
356 | {
357 |
358 | int playerID = currentPlayerIndex + 1;
359 |
360 | // Keep track of the last time we have receieved data from that client
361 | lastPulse[currentPlayerIndex] = SDL_GetTicks();
362 |
363 | // Capture data from player in 1024-byte chunks and combine them together into data pointer
364 | char* data = NULL;
365 | int dataLen = 0;
366 | while (SDLNet_CheckSockets(socketSets[currentPlayerIndex], 0))
367 | {
368 |
369 | char currentData[1024];
370 | int currentLen = SDLNet_TCP_Recv(clients[currentPlayerIndex], currentData, 1024);
371 |
372 | // If the data is of length <= 0, that means the client closed the connection
373 | if (currentLen <= 0)
374 | {
375 |
376 | // If a player disconnected, close the server.
377 | printf("Player %d disconnected. Closing server.\n", playerID);
378 |
379 | // Tell all other clients a player disconnected
380 | char message[] = "Player disconnected.";
381 | for (int otherPlayerIndex = 0; otherPlayerIndex < numConnectedPlayers; otherPlayerIndex++)
382 | if (otherPlayerIndex != currentPlayerIndex)
383 | SDLNet_TCP_Send(clients[otherPlayerIndex], message, SDL_strlen(message) + 1);
384 |
385 | running = false;
386 | goto LoopStart;
387 |
388 | }
389 |
390 | if (data == NULL)
391 | {
392 |
393 | data = SDL_calloc(currentLen, sizeof(char));
394 | SDL_memcpy(data, currentData, currentLen);
395 | dataLen = currentLen;
396 |
397 | }
398 | else
399 | {
400 |
401 | data = SDL_realloc(data, dataLen + currentLen);
402 | SDL_memcpy(&data[dataLen], currentData, currentLen);
403 | dataLen += currentLen;
404 |
405 | }
406 |
407 | // If our received data does not end in a null-byte,
408 | //add a null-byte to the end of it
409 | // This is to avoid out-of-bounds errors since we will treat this
410 | // data as a string, which expects a null-byte at the end.
411 | if (data[dataLen - 1] != '\0')
412 | {
413 |
414 | data = SDL_realloc(data, dataLen + 1);
415 | data[dataLen] = '\0';
416 | dataLen += 1;
417 |
418 | }
419 |
420 | }
421 |
422 | // Split data into "packets". A packet is basically a set of data for a specific variable.
423 | // For example,"MAP=...NEXT=...SCORE=..." could all be received at the same time, and be stored
424 | // in the data pointer at the same time, and here we would split it into three separate packets.
425 | char** packets = NULL;
426 | int numPackets = 0;
427 | char* currentStringValue = NULL;
428 | for (int dataIndex = 0; dataIndex < dataLen; dataIndex++)
429 | {
430 |
431 | if (data[dataIndex] != '\0')
432 | {
433 |
434 | if (currentStringValue == NULL)
435 | {
436 |
437 | currentStringValue = SDL_calloc(2, sizeof(char));
438 | currentStringValue[0] = data[dataIndex];
439 | currentStringValue[1] = '\0';
440 |
441 | }
442 | else
443 | {
444 |
445 | int newLen = SDL_strlen(currentStringValue) + 1 + 1;
446 | currentStringValue = SDL_realloc(currentStringValue, newLen * sizeof(char));
447 | SDL_strlcat(currentStringValue, &(data[dataIndex]), newLen);
448 |
449 | }
450 |
451 | }
452 | else
453 | {
454 |
455 | // Only append a new packet if the currentStringValue is not null.
456 | // This is to avoid crashes where data is received with two null-bytes
457 | // in a row.
458 | if (currentStringValue != NULL)
459 | {
460 |
461 | if (numPackets == 0)
462 | {
463 |
464 | packets = SDL_calloc(1, sizeof(char*));
465 | numPackets++;
466 |
467 | }
468 | else
469 | {
470 |
471 | packets = SDL_realloc(packets, (numPackets + 1) * sizeof(char*));
472 | numPackets++;
473 |
474 | }
475 |
476 | packets[numPackets - 1] = SDL_calloc(SDL_strlen(currentStringValue) + 1, sizeof(char));
477 | SDL_strlcpy(packets[numPackets - 1], currentStringValue, SDL_strlen(currentStringValue) + 1);
478 |
479 | SDL_free(currentStringValue);
480 | currentStringValue = NULL;
481 |
482 | }
483 |
484 | }
485 |
486 | }
487 |
488 | if (currentStringValue != NULL)
489 | {
490 |
491 | SDL_free(currentStringValue);
492 | currentStringValue = NULL;
493 |
494 | }
495 |
496 | // Now go through each packet and process it
497 | for (unsigned short packetIndex = 0; packetIndex < numPackets; packetIndex++)
498 | {
499 |
500 | // Get the packet header (everything before the '=')
501 | int endHeaderIndex = 0;
502 | for (unsigned short i = 0; i < SDL_strlen(packets[packetIndex]); i++)
503 | if (packets[packetIndex][endHeaderIndex] != '=')
504 | endHeaderIndex++;
505 | else
506 | break;
507 | char* packetHeader = SDL_calloc(endHeaderIndex + 1, sizeof(char));
508 | SDL_strlcpy(packetHeader, packets[packetIndex], endHeaderIndex + 1);
509 |
510 | // If this is the first packet received from this player
511 | if (receivedFirstPacket[currentPlayerIndex] == false)
512 | {
513 |
514 | // The first packet MUST be a NAME packet. Otherwise, it is likely not
515 | // a Multris client that connected to the server. And in that case, we
516 | // should close the server.
517 | if (SDL_strstr(packets[packetIndex], "NAME=") == NULL)
518 | {
519 |
520 | printf("Unexpected packet from player %d. Closing server.\n", playerID);
521 |
522 | char message[] = "Unexpected packet from player. Server closed.";
523 | for (int otherPlayerIndex = 0; otherPlayerIndex < numConnectedPlayers; otherPlayerIndex++)
524 | if (otherPlayerIndex != currentPlayerIndex)
525 | SDLNet_TCP_Send(clients[otherPlayerIndex], message, SDL_strlen(message) + 1);
526 |
527 | // Since we are closing the server, we need to free our packets
528 | for (int i = 0; i < numPackets; i++)
529 | SDL_free(packets[i]);
530 | SDL_free(packets);
531 | SDL_free(data);
532 |
533 | // Close the server
534 | running = false;
535 | goto LoopStart;
536 |
537 | }
538 |
539 | receivedFirstPacket[currentPlayerIndex] = true;
540 |
541 | }
542 |
543 | // If the received packet is a NAME
544 | if (SDL_strstr(packets[packetIndex], "NAME") != NULL)
545 | {
546 |
547 | // Find the start of the name value
548 | int nameStartIndex = SDL_strlen("NAME=");
549 | int nameLength = SDL_strlen(&(packets[packetIndex][nameStartIndex])) + 1;
550 |
551 | // Allocate space for the name
552 | if (numNames == 0)
553 | names = SDL_calloc(1, sizeof(char*));
554 | else
555 | names = SDL_realloc(names, sizeof(char*) * (numNames + 1));
556 | names[currentPlayerIndex] = SDL_calloc(nameLength, sizeof(char));
557 | numNames++;
558 |
559 | // And store the name
560 | SDL_strlcpy(names[currentPlayerIndex], &(packets[packetIndex][nameStartIndex]), nameLength);
561 |
562 | printf("Received name %s from player %d.\n", names[currentPlayerIndex], playerID);
563 |
564 | // If the NAME we just recieved is the last name, send names to all
565 | // players
566 | if (numNames == maxPlayers)
567 | {
568 |
569 | for (unsigned short i = 0; i < numConnectedPlayers; i++)
570 | {
571 |
572 | char* currentName = names[i];
573 |
574 | for (unsigned short j = 0; j < numConnectedPlayers; j++)
575 | {
576 |
577 | if (j != i)
578 | {
579 |
580 | // Send the players names to each other
581 | printf("Sending name %s from player %d to player %d.\n", currentName, i + 1, j + 1);
582 | int len = SDL_strlen("NAME=") + SDL_strlen(currentName) + 1;
583 | char* namePacket = SDL_calloc(len, sizeof(char));
584 | SDL_strlcpy(namePacket, "NAME=", len);
585 | SDL_strlcat(namePacket, currentName, len);
586 | SDLNet_TCP_Send(clients[j], namePacket, len);
587 | SDL_free(namePacket);
588 |
589 | }
590 |
591 | }
592 |
593 | }
594 |
595 | sentNames = true;
596 |
597 | }
598 |
599 | } // Keep track of when each player is ready
600 | else if (SDL_strstr(packets[packetIndex], "READY") != NULL)
601 | {
602 |
603 | printf("Player %d is ready.\n", playerID);
604 |
605 | playersReady++;
606 |
607 | }
608 | // If the packet is not a NAME or PULSE
609 | else if (SDL_strstr(packets[packetIndex], "PULSE") == NULL)
610 | {
611 |
612 | printf("Received %s from player %d.\n", packetHeader, playerID);
613 |
614 | // Forward the packet to all other connected players
615 | for (int otherPlayerIndex = 0; otherPlayerIndex < maxPlayers; otherPlayerIndex++)
616 | {
617 |
618 | if (otherPlayerIndex != currentPlayerIndex)
619 | {
620 |
621 | printf("Sending %s from player %d to player %d.\n", packetHeader, playerID, otherPlayerIndex + 1);
622 | int len = SDL_strlen(packets[packetIndex]) + 1;
623 | SDLNet_TCP_Send(clients[otherPlayerIndex], packets[packetIndex], len);
624 |
625 | }
626 |
627 | }
628 |
629 |
630 | }
631 | else
632 | printf("Received PULSE from player %d.\n", playerID);
633 |
634 | }
635 |
636 | // Free our data and packets to avoid memory leaks
637 | for (int i = 0; i < numPackets; i++)
638 | SDL_free(packets[i]);
639 | SDL_free(packets);
640 | SDL_free(data);
641 |
642 | }
643 | else
644 | {
645 |
646 | // If we haven't received any data in TIMEOUT_SECONDS seconds, assume a timeout
647 | if ((SDL_GetTicks() - lastPulse[currentPlayerIndex]) / 1000 > TIMEOUT_SECONDS)
648 | {
649 |
650 | // Since the client disconnected, close the server.
651 | printf("Received no packets from player %d in %d seconds. Closing server.\n", currentPlayerIndex + 1, TIMEOUT_SECONDS);
652 |
653 | // Tell all other clients a player disconnected
654 | char message[] = "Player disconnected.";
655 | for (int otherPlayerIndex = 0; otherPlayerIndex < numConnectedPlayers; otherPlayerIndex++)
656 | if (otherPlayerIndex != currentPlayerIndex)
657 | SDLNet_TCP_Send(clients[otherPlayerIndex], message, SDL_strlen(message) + 1);
658 |
659 | running = false;
660 | goto LoopStart;
661 |
662 | }
663 |
664 | }
665 |
666 | }
667 |
668 | }
669 |
670 | //Logic for limitting the FPS
671 | //Basically just sleeps every frame if not enough time passed between frames
672 | int deltaMS = SDL_GetTicks() - ticksLastFrame;
673 | if (deltaMS < TARGET_FRAME_TIME * 1000)
674 | SDL_Delay(TARGET_FRAME_TIME * 1000 - deltaMS);
675 | ticksLastFrame = SDL_GetTicks();
676 |
677 | }
678 |
679 | // Free memory and close connection to each client
680 | for (unsigned short i = 0; i < numConnectedPlayers; i++)
681 | {
682 |
683 | if (names != NULL)
684 | SDL_free(names[i]);
685 |
686 | SDLNet_TCP_Close(clients[i]);
687 | SDLNet_FreeSocketSet(socketSets[i]);
688 |
689 | }
690 | if (names != NULL)
691 | SDL_free(names);
692 | SDL_free(socketSets);
693 | SDLNet_TCP_Close(socket);
694 | SDL_free(clients);
695 |
696 | printf("Server closed.\n");
697 |
698 | return 0;
699 |
700 | }
--------------------------------------------------------------------------------
/src/server.h:
--------------------------------------------------------------------------------
1 | #ifndef SERVER_H_
2 | #define SERVER_H_
3 |
4 | #include "MGF.h"
5 |
6 | int startDedicatedServer(int argc, char* argv[]);
7 | int startServer(IPaddress address, int tickRate);
8 |
9 | #endif
--------------------------------------------------------------------------------