so://phie

freezo

A retro platform game
git clone git://soophie.de/freezo

blob: 52cadebc4444724cb6c1c1c052412e5ea5f46eda (11545 bytes)[raw]

1#include <stdio.h>

2#include <stdlib.h>

3#include <string.h>

4

5#include "../lib/raylib.h"

6#include "../include/entity.h"

7#include "../include/game.h"

8#include "../include/tile.h"

9#include "../include/util.h"

10#include "../include/player.h"

11#include "../include/const.h"

12#include "../include/level.h"

13

14game_t *game_create(void) {

15 game_t *game = malloc(sizeof(game_t));

16 *game = (game_t) {

17 .state = STATE_GAME,

18 .level = NULL,

19 .quit = false,

20 .defeat = false,

21 .victory = false,

22 .menu = menu_create(),

23 .player = NULL,

24 .tiles = NULL,

25 .tiles_len = 0,

26 .entities = NULL,

27 .entities_len = 0,

28 .effects = NULL,

29 .effects_len = 0,

30 .assets.tiles = texture_load("assets/tiles.png", SCALE),

31 .assets.entities = texture_load("assets/entities.png", SCALE),

32 .assets.font = texture_load("assets/font.png", SCALE - 1),

33 .assets.images = texture_load("assets/images.png", SCALE),

34 .assets.background = texture_load("assets/background.png", SCALE),

35 .assets.track = LoadSound("assets/song.wav"),

36 .assets.win = LoadSound("assets/win.wav"),

37 .assets.lose = LoadSound("assets/lose.wav"),

38 .assets.locked = LoadSound("assets/locked.wav"),

39 .camera = (Camera2D) {

40 .offset = (pos_t) { 0.0, 0.0 },

41 .zoom = 1,

42 },

43 .xp = 0,

44 .sceen_timer = 0,

45 .door = NULL,

46 .overlay = LoadRenderTexture(WINDOW_WIDTH, WINDOW_HEIGHT),

47 .is_muted = false,

48 };

49 level_load(game, LEVEL_1);

50 SetSoundVolume(game->assets.track, GAME_MUSIC_VOLUME);

51 SetSoundVolume(game->assets.win, GAME_SOUND_VOLUME);

52 SetSoundVolume(game->assets.lose, GAME_SOUND_VOLUME);

53 SetSoundVolume(game->assets.locked, GAME_SOUND_VOLUME);

54 return game;

55}

56

57void game_update(game_t *game) {

58 bool do_toggle_mute = false;

59 if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) {

60 pos_t mouse_pos = GetMousePosition();

61 rect_t mouse_rect = {

62 .x = mouse_pos.x,

63 .y = mouse_pos.y,

64 .width = 1,

65 .height = 1,

66 };

67 rect_t mute_button_rect = {

68 .x = TILE_WIDTH / 2.0,

69 .y = WINDOW_HEIGHT - TILE_HEIGHT - TILE_HEIGHT / 2.0,

70 .width = TILE_WIDTH,

71 .height = TILE_HEIGHT,

72 };

73 rect_t pause_button_rect = {

74 .x = WINDOW_WIDTH - TILE_WIDTH - TILE_WIDTH / 2.0,

75 .y = WINDOW_HEIGHT - TILE_HEIGHT - TILE_HEIGHT / 2.0,

76 .width = TILE_WIDTH,

77 .height = TILE_HEIGHT,

78 };

79 if (rect_collide(mouse_rect, mute_button_rect)) {

80 do_toggle_mute = true;

81 }

82 if (!game->victory && !game->defeat) {

83 if (rect_collide(mouse_rect, pause_button_rect)) {

84 switch (game->state) {

85 case STATE_GAME:

86 game->state = STATE_MENU;

87 break;

88 case STATE_MENU:

89 game->state = STATE_GAME;

90 break;

91 }

92 }

93 }

94 }

95 if (IsKeyPressed(KEY_M)) {

96 do_toggle_mute = true;

97 }

98 if (do_toggle_mute) {

99 game->is_muted = !game->is_muted;

100 if (game->is_muted) {

101 SetSoundVolume(game->assets.track, 0.0f);

102 SetSoundVolume(game->assets.win, 0.0f);

103 SetSoundVolume(game->assets.lose, 0.0f);

104 SetSoundVolume(game->assets.locked, 0.0f);

105 }

106 else {

107 SetSoundVolume(game->assets.track, GAME_MUSIC_VOLUME);

108 SetSoundVolume(game->assets.win, GAME_SOUND_VOLUME);

109 SetSoundVolume(game->assets.lose, GAME_SOUND_VOLUME);

110 SetSoundVolume(game->assets.locked, GAME_SOUND_VOLUME);

111 }

112 }

113 if (!IsSoundPlaying(game->assets.track) && !game->victory && !game->defeat) {

114 PlaySound(game->assets.track);

115 }

116 switch (game->state) {

117 case STATE_MENU: {

118 menu_update(game->menu, game);

119 break;

120 }

121 case STATE_GAME: {

122 if (!game->victory && !game->defeat) {

123 if (IsKeyPressed(KEY_ESCAPE)) {

124 game->state = STATE_MENU;

125 game->menu->idx = 0;

126 }

127 }

128 if (game->level != NULL) {

129 // defeat

130 if (game->defeat) {

131 if (game->sceen_timer == 0) {

132 StopSound(game->assets.track);

133 }

134 if (game->sceen_timer == 20) {

135 PlaySound(game->assets.lose);

136 }

137 if (game->sceen_timer == GAME_SCEEN_TIMER) {

138 level_e level = game->level->type;

139 int max_health = game->player->max_health;

140 max_health -= 2;

141 if (max_health <= 0) {

142 level = LEVEL_1;

143 max_health = PLAYER_MAX_HEALTH;

144 }

145 level_unload(game);

146 level_load(game, level);

147 game->player->max_health = max_health;

148 game->player->health = max_health;

149 game->defeat = false;

150 game->xp = 0;

151 game->sceen_timer = 0;

152 }

153 else {

154 game->sceen_timer++;

155 }

156 }

157 // victory

158 if (game->victory) {

159 if (game->sceen_timer == 0) {

160 StopSound(game->assets.track);

161 }

162 if (game->sceen_timer == 20) {

163 PlaySound(game->assets.win);

164 }

165 if (game->sceen_timer == GAME_SCEEN_TIMER) {

166 if (game->level->type < LEVELS) {

167 level_e level = game->level->type;

168 int max_health = game->player->max_health;

169 level++;

170 level_unload(game);

171 level_load(game, level);

172 game->player->max_health = max_health;

173 game->player->health = max_health;

174 }

175 game->victory = false;

176 game->sceen_timer = 0;

177 }

178 else {

179 if (game->sceen_timer == 125) {

180 game->door->is_locked = true;

181 }

182 game->sceen_timer++;

183 }

184 }

185 if (!game->victory && !game->defeat) {

186 player_update(game->player, game);

187 }

188 for (int i = 0; i < game->tiles_len; i++) {

189 tile_update(game->tiles[i], game);

190 }

191 for (int i = 0; i < game->entities_len; i++) {

192 int len = game->entities_len;

193 entity_update(&game->entities[i], game);

194 // check if entity was removed

195 if (game->entities_len < len) {

196 i--;

197 }

198 }

199 for (int i = 0; i < game->effects_len; i++) {

200 int len = game->effects_len;

201 effect_update(game->effects[i], game);

202 // check if enemy was removed

203 if (game->effects_len < len) {

204 i--;

205 }

206 }

207 pos_t camera_pos = (pos_t) {

208 .x = WINDOW_WIDTH / 2.0 - game->player->pos.x - PLAYER_WIDTH / 2.0,

209 .y = WINDOW_HEIGHT / 2.0 - game->player->pos.y,

210 };

211 if (camera_pos.x > 0.0) {

212 camera_pos.x = 0.0;

213 }

214 if (camera_pos.x + TILE_WIDTH * game->level->width < WINDOW_WIDTH) {

215 camera_pos.x = WINDOW_WIDTH - TILE_WIDTH * game->level->width;

216 }

217 if (camera_pos.y > 0.0) {

218 camera_pos.y = 0.0;

219 }

220 if (camera_pos.y + TILE_HEIGHT * game->level->height < WINDOW_HEIGHT) {

221 camera_pos.y = WINDOW_HEIGHT - TILE_HEIGHT * game->level->height;

222 }

223 game->camera.offset = pos_snap(camera_pos);

224 }

225 break;

226 }

227 }

228}

229

230void game_draw(game_t *game) {

231 // render overlay

232 BeginTextureMode(game->overlay);

233 ClearBackground(BLACK);

234 DrawRectangle(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, BLACK);

235 BeginBlendMode(BLEND_SUBTRACT_COLORS);

236 int radius = WINDOW_WIDTH;

237 radius -= radius / 80.0 * game->sceen_timer;

238 if (radius < PLAYER_HEIGHT) {

239 radius = PLAYER_HEIGHT;

240 }

241 DrawCircle(game->player->pos.x + PLAYER_WIDTH / 2.0, WINDOW_HEIGHT - (game->player->pos.y + PLAYER_HEIGHT / 2.0), radius, WHITE);

242 EndBlendMode();

243 EndTextureMode();

244 // render sceen

245 BeginDrawing();

246 ClearBackground((Color) { 5, 5, 5, 255 });

247 if (game->level != LEVEL_NULL) {

248 BeginMode2D(game->camera);

249 for (int i = 0; i < game->tiles_len; i++) {

250 tile_draw(game->tiles[i], game);

251 }

252 for (int i = 0; i < game->entities_len; i++) {

253 entity_t *entity = &game->entities[i];

254 if (entity->type == ENTITY_GATE || entity->type == ENTITY_DOOR) {

255 entity_draw(entity, game);

256 }

257 }

258 for (int i = 0; i < game->entities_len; i++) {

259 entity_t *entity = &game->entities[i];

260 if (entity->type != ENTITY_GATE && entity->type != ENTITY_DOOR) {

261 entity_draw(entity, game);

262 }

263 }

264 if (game->sceen_timer < 100) {

265 player_draw(game->player, game);

266 }

267 for (int i = 0; i < game->effects_len; i++) {

268 effect_draw(game->effects[i], game);

269 }

270 // draw overlay

271 DrawTexture(game->overlay.texture, 0, 0, WHITE);

272 EndMode2D();

273 // draw interface

274 int i = 0;

275 for (i = 0; i < game->player->health / 2; i++) {

276 DrawTextureRec(game->assets.tiles, texture_rect(0, 1, TILE_WIDTH, TILE_HEIGHT), (pos_t) { TILE_WIDTH / 2.0 + i * (TILE_WIDTH + TILE_WIDTH / 4.0), TILE_WIDTH / 2.0 }, WHITE);

277 }

278 if (game->player->health % 2 != 0) {

279 DrawTextureRec(game->assets.tiles, texture_rect(1, 1, TILE_WIDTH, TILE_HEIGHT), (pos_t) { TILE_WIDTH / 2.0 + i * (TILE_WIDTH + TILE_WIDTH / 4.0), TILE_WIDTH / 2.0 }, WHITE);

280 i++;

281 }

282 for (; i < game->player->max_health / 2; i++) {

283 DrawTextureRec(game->assets.tiles, texture_rect(2, 1, TILE_WIDTH, TILE_HEIGHT), (pos_t) { TILE_WIDTH / 2.0 + i * (TILE_WIDTH + TILE_WIDTH / 4.0), TILE_WIDTH / 2.0 }, WHITE);

284 }

285 char xp_text[10];

286 snprintf(xp_text, 10, "%dXP", game->xp);

287 pos_t xp_pos = (pos_t) {

288 .x = WINDOW_WIDTH - TILE_WIDTH / 2.0,

289 .y = TILE_WIDTH / 1.5,

290 };

291 text_draw(xp_pos, xp_text, TEXT_ALIGNMENT_RIGHT, game);

292 char level_text[10];

293 snprintf(level_text, 10, "L%d", game->level->type);

294 pos_t level_pos = (pos_t) {

295 .x = WINDOW_WIDTH - TILE_WIDTH / 2.0,

296 .y = TILE_WIDTH / 1.5 + 25,

297 };

298 text_draw(level_pos, level_text, TEXT_ALIGNMENT_RIGHT, game);

299 }

300 if (game->state == STATE_MENU) {

301 menu_draw(game->menu, game);

302 }

303 if (game->is_muted) {

304 DrawTextureRec(game->assets.tiles, texture_rect(5, 1, TILE_WIDTH, TILE_HEIGHT), (pos_t) { TILE_WIDTH / 2.0, WINDOW_HEIGHT - TILE_HEIGHT - TILE_HEIGHT / 2.0 }, WHITE);

305 }

306 else {

307 DrawTextureRec(game->assets.tiles, texture_rect(4, 1, TILE_WIDTH, TILE_HEIGHT), (pos_t) { TILE_WIDTH / 2.0, WINDOW_HEIGHT - TILE_HEIGHT - TILE_HEIGHT / 2.0 }, WHITE);

308 }

309 if (game->state == STATE_GAME) {

310 if (game->victory || game->defeat) {

311 DrawTextureRec(game->assets.tiles, texture_rect(8, 1, TILE_WIDTH, TILE_HEIGHT), (pos_t) { WINDOW_WIDTH - TILE_WIDTH - TILE_WIDTH / 2.0, WINDOW_HEIGHT - TILE_HEIGHT - TILE_HEIGHT / 2.0 }, WHITE);

312 }

313 else {

314 DrawTextureRec(game->assets.tiles, texture_rect(6, 1, TILE_WIDTH, TILE_HEIGHT), (pos_t) { WINDOW_WIDTH - TILE_WIDTH - TILE_WIDTH / 2.0, WINDOW_HEIGHT - TILE_HEIGHT - TILE_HEIGHT / 2.0 }, WHITE);

315 }

316 }

317 else {

318 DrawTextureRec(game->assets.tiles, texture_rect(7, 1, TILE_WIDTH, TILE_HEIGHT), (pos_t) { WINDOW_WIDTH - TILE_WIDTH - TILE_WIDTH / 2.0, WINDOW_HEIGHT - TILE_HEIGHT - TILE_HEIGHT / 2.0 }, WHITE);

319 }

320 EndDrawing();

321}

322

323void game_free(game_t *game) {

324 menu_free(game->menu);

325 if (game->level != LEVEL_NULL) {

326 level_unload(game);

327 }

328 UnloadTexture(game->assets.tiles);

329 UnloadTexture(game->assets.entities);

330 UnloadTexture(game->assets.font);

331 UnloadTexture(game->assets.images);

332 UnloadTexture(game->assets.background);

333 UnloadSound(game->assets.track);

334 UnloadSound(game->assets.win);

335 UnloadSound(game->assets.lose);

336 UnloadSound(game->assets.locked);

337 UnloadRenderTexture(game->overlay);

338 free(game);

339}