game_player.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. #include "game_player.h"
  2. #include "SDL2/SDL.h"
  3. #include "sdl_helper.h"
  4. #include "game_map.h"
  5. #include "game_math.h"
  6. #define PLAYER_MULTIPLIER(p) (float) (p->level *0.2)
  7. #define PLAYER_JUMP(p) (-1 -p->level *0.2)
  8. #define PLAYER_SPEED_RUNNING(p) (0.1 +p->level *0.05)
  9. #define PLAYER_SPEED_MAX(p) (1 +p->level *0.1)
  10. #define PLAYER_BUMP_POWER(p) (2 +p->level *0.2)
  11. #define PLAYER_ATTACK_POWER(p) (5 +p->level *0.8)
  12. #define PLAYER_CAST_COOLDOWN(p) (30 -p->level *1)
  13. /* animation states - used to change frames on the player's texture
  14. * the texture is divined on the x axis by animation frames,
  15. * and on the y axis by animations
  16. */
  17. const int ANIMATION_IDLE = 0;
  18. const int ANIMATION_RUN = 1;
  19. const int ANIMATION_JUMP = 2;
  20. const int ANIMATION_FALL = 3;
  21. const int ANIMATION_CAST = 4;
  22. const int ANIMATION_ATTACK = 4;
  23. /* secret functions - not meant for the public's eyes
  24. */
  25. // cast is the action of the skill itself
  26. void game_player_cast(struct game_player *p) {
  27. /* currently the only skill is to increase the height the platform the player stands on
  28. * and the two surrrounding platforms
  29. */
  30. int platform_id = game_map_platform_id(p->map, p->rect.x);
  31. game_map_platform_rise(p->map, platform_id, -15);
  32. game_map_platform_rise(p->map, platform_id-1, -10);
  33. game_map_platform_rise(p->map, platform_id+1, -10);
  34. // wait ~1 second before casting again
  35. p->casting_cooldown = PLAYER_CAST_COOLDOWN(p);
  36. }
  37. // animate
  38. int game_player_animate(struct game_player *p) {
  39. /* advance animation frames
  40. * currently it does so every 10 frames, but this might change in the future
  41. */
  42. p->anim_count++;
  43. if (p->anim_count > 10) {
  44. // animate player's texture to next frame
  45. p->tex_rect.x += p->tex_size;
  46. // reset animation counter
  47. p->anim_count = 0;
  48. // end of animation - repeat
  49. if (p->tex_rect.x > p->tex_size *3) {
  50. // reset animation frame
  51. p->tex_rect.x = 0;
  52. // animation ended
  53. return 1;
  54. }
  55. } // player frame animation
  56. // animation keeps going
  57. return 0;
  58. } // animate
  59. // collision player with water
  60. int game_player_water_collide(struct game_player *p) {
  61. return p->rect.y +p->rect.h > game_map_water_y(p->map);
  62. }
  63. // collision player with water - deep enough to drown
  64. int game_player_water_drown(struct game_player *p) {
  65. return p->rect.y > game_map_water_y(p->map);
  66. }
  67. void game_player_movement(struct game_player *p) {
  68. /* handle movement
  69. * if user is moving left or right, move the velocity_x towards -1 or 1
  70. * the final speed of the character is its own speed times speec_counter
  71. * if user is not moving, slowly decelerate
  72. */
  73. switch (p->move) {
  74. // not moving - rest
  75. case MOVE_NONE:
  76. p->velocity_x *= 0.8;
  77. if (p->velocity_x < 0.1
  78. && p->velocity_x > -0.1) p->velocity_x = 0;
  79. break;
  80. // moving rightwards
  81. case MOVE_RIGHT:
  82. p->velocity_x += PLAYER_SPEED_RUNNING(p);
  83. if (p->velocity_x > PLAYER_SPEED_MAX(p)) p->velocity_x = PLAYER_SPEED_MAX(p);
  84. break;
  85. // moving leftwards
  86. case MOVE_LEFT:
  87. p->velocity_x -= PLAYER_SPEED_RUNNING(p);
  88. if (p->velocity_x < -PLAYER_SPEED_MAX(p)) p->velocity_x = -PLAYER_SPEED_MAX(p);
  89. break;
  90. }
  91. // temporary way of assigning the running animation based on speed
  92. if (p->velocity_x > 0 && p->state == IDLE) {
  93. p->tex_rect.y = p->tex_size *ANIMATION_RUN;
  94. }
  95. else if (p->velocity_x < 0 && p->state == IDLE) {
  96. p->tex_rect.y = p->tex_size *ANIMATION_RUN;
  97. }
  98. else if (p->state == IDLE) {
  99. p->tex_rect.y = p->tex_size *ANIMATION_IDLE;
  100. }
  101. /* movement & collision
  102. */
  103. if (p->velocity_x != 0) {
  104. // player is in the water, delay speed
  105. if (game_player_water_collide(p)) {
  106. p->velocity_x *= 0.8;
  107. }
  108. // movement
  109. p->rect.x += p->velocity_x *p->speed_max;
  110. // check collision against each player
  111. for (int i = 0; i < 2; i++) {
  112. // not on self
  113. if (&p->players[i] == p) continue;
  114. // actual collision
  115. if (game_math_collide(&p->players[i].rect, &p->rect)) {
  116. p->rect.x = p->players[i].rect.x +(p->velocity_x > 0 ? -p->rect.w : +p->rect.w);
  117. break;
  118. }
  119. }
  120. } // movement
  121. // moved to a new platform
  122. if (game_map_platform_id(p->map, p->rect.x) != p->platform_id) {
  123. // get new platform id and height
  124. int new_platform_id = game_map_platform_id(p->map, p->rect.x);
  125. int new_platform_y = game_map_platform_y(p->map, new_platform_id);
  126. // platform is above player, don't let them move there!
  127. if (new_platform_y < p->rect.y +p->rect.h) {
  128. // move player back
  129. if (new_platform_id > p->platform_id) {
  130. p->rect.x = (p->platform_id+1) *p->map->grid_width -1 +p->map->offset_x;
  131. }
  132. else {
  133. p->rect.x = p->platform_id *p->map->grid_width +p->map->offset_x;
  134. }
  135. // cancel player's speed
  136. p->velocity_x = 0;
  137. }
  138. // platform is below (or same level as) player, assign new platform to player
  139. else {
  140. p->platform_id = new_platform_id;
  141. p->state = FALL;
  142. }
  143. }
  144. }
  145. /* initialise player
  146. * for now initialise him on the top left part of the screen
  147. * with 48x48 size
  148. * not moving, not standing on a surface
  149. * initialise some sample speed and falling/jumping data
  150. */
  151. void game_player_init(struct game_player *p, struct game_map *m) {
  152. // player's size (width and height)
  153. p->tex_size = 48;
  154. // player's texture and rect
  155. p->tex = load_image("images/character_0.png");
  156. p->rect.x = 0;
  157. p->rect.y = 0;
  158. p->rect.w = p->tex_size;
  159. p->rect.h = p->tex_size;
  160. /* player's texture rect, should always have the same width and height,
  161. * the x and y should change to point to the specific animation/frame
  162. */
  163. p->tex_rect.x = 0;
  164. p->tex_rect.y = 0;
  165. p->tex_rect.w = p->tex_size;
  166. p->tex_rect.h = p->tex_size;
  167. // animation
  168. p->state = IDLE;
  169. p->anim_count = 0;
  170. p->looking_side = LOOKING_RIGHT;
  171. // movement
  172. p->move = MOVE_NONE;
  173. p->speed_max = 5;
  174. p->velocity_x = 0;
  175. p->velocity_y = 0;
  176. /* get the map and mark where the player is on the map
  177. * based on starting position
  178. */
  179. p->map = m;
  180. p->platform_id = game_map_platform_id(p->map, p->rect.x);
  181. // ai
  182. p->map_target = 0;
  183. // casting skill variables
  184. p->casting_cooldown = 0;
  185. // player's starting level
  186. p->level = 0;
  187. } // init
  188. /* the player when updates:
  189. * moves left/right
  190. * moves up/down with gravity
  191. */
  192. void game_player_update(struct game_player *p) {
  193. // casting is on cooldown, decrease it
  194. if (p->casting_cooldown > 0) p->casting_cooldown--;
  195. /* ideally the whole update function should use this switch
  196. * do something depending on its state
  197. */
  198. int platform_y = game_map_platform_y(p->map, p->platform_id);
  199. switch (p->state) {
  200. /* when attacking, move to the looking direction non-stop,
  201. * until animation runs out, or player collides with another player
  202. * or the map
  203. */
  204. case ATTACK:
  205. // when animation is over, fall to the platform
  206. if (game_player_animate(p)) {
  207. p->state = FALL;
  208. // a small velocity makes a nice after-attack bouncing effect
  209. p->velocity_y = -0.5;
  210. p->velocity_x = -1 *p->looking_side;
  211. }
  212. // actual movement towards looking direction
  213. p->rect.x += PLAYER_ATTACK_POWER(p) *p->looking_side;
  214. // check collision with other players (for now with magic numbers)
  215. for (int i = 1; i < 2; i++) {
  216. // check collision on x and y
  217. if (game_math_collide(&p->players[i].rect, &p->rect)) {
  218. p->rect.x = p->players[i].rect.x +(p->looking_side == LOOKING_RIGHT ? -p->rect.w -1 : p->rect.w +5);
  219. // if collision happens, stop attacking, move current player with a "bump" jump away
  220. p->state = FALL;
  221. p->velocity_y = -0.5;
  222. p->velocity_x = -1 *p->looking_side;
  223. // ideally, the targeted player is bounced away here
  224. game_player_bump(&p->players[i], p);
  225. // only first player matters
  226. break;
  227. }
  228. } // collision with players
  229. // moved to new platform
  230. int new_id;
  231. if ((new_id = game_map_platform_id(p->map, p->rect.x)) != p->platform_id) {
  232. // not colliding with it, use it as current platform
  233. if (p->rect.y +p->rect.h < game_map_platform_y(p->map, new_id)) {
  234. p->platform_id = new_id;
  235. p->state = FALL;
  236. }
  237. // colliding with it, stop, and attack it
  238. else {
  239. p->rect.x = game_map_platform_x(p->map, p->platform_id)
  240. +(new_id > p->platform_id?
  241. game_map_platform_width(p->map) : 0);
  242. p->state = FALL;
  243. p->velocity_y = -0.5;
  244. p->velocity_x = -1 *p->looking_side;
  245. // get attacking platform (either left or right)
  246. int target_platform = p->platform_id
  247. +(p->looking_side == LOOKING_RIGHT ? 1: -1);
  248. // attack platform
  249. game_map_platform_rise(p->map, target_platform, 10);
  250. }
  251. }
  252. // for now ignore the rest of update
  253. return;
  254. // player is sitting on the ground
  255. case IDLE:
  256. game_player_animate(p);
  257. // follow standing platform
  258. p->rect.y = platform_y -p->rect.h;
  259. // nullify speed for now
  260. p->velocity_y = 0;
  261. // movement
  262. game_player_movement(p);
  263. break;
  264. // player is jumping
  265. case JUMP:
  266. game_player_animate(p);
  267. /* gravity
  268. * as long as the player stands on a surface, nothing happens
  269. * if player is jumping, advance the jump counter, which goes
  270. * from 0 to -1, once its done, player stops jumping, and is now falling
  271. * because of gravity
  272. */
  273. p->velocity_y -= 0.4;
  274. if (p->velocity_y <= PLAYER_JUMP(p)) {
  275. p->state = FALL;
  276. }
  277. // animation is jumping
  278. p->tex_rect.y = p->tex_size *ANIMATION_JUMP;
  279. // move player up/down, if player goes below platform, they are now standing on it
  280. p->rect.y += p->velocity_y * 10;
  281. if (game_player_water_drown(p)) {
  282. p->state = DROWN;
  283. }
  284. else
  285. if (p->rect.y +p->rect.h > platform_y) {
  286. p->rect.y = platform_y -p->rect.h;
  287. p->velocity_y = 0;
  288. p->state = IDLE;
  289. }
  290. // collision on players
  291. for (int i = 0; i < 2; i++) {
  292. // not on self
  293. if (&p->players[i] == p) continue;
  294. // actual collision
  295. if (game_math_collide(&p->players[i].rect, &p->rect)) {
  296. p->rect.y = p->players[i].rect.y +(p->velocity_y > 0 ? -p->rect.h : p->rect.h);
  297. p->state = FALL;
  298. p->velocity_y = 0;
  299. break;
  300. }
  301. }
  302. // when jumping, the character still moves left/right
  303. game_player_movement(p);
  304. break;
  305. // player is falling
  306. case FALL:
  307. game_player_animate(p);
  308. // player keeps falling at increasing capped speed
  309. p->velocity_y += 0.2;
  310. if (p->velocity_y > 1) p->velocity_y = 1;
  311. if (p->velocity_y > 0) {
  312. // animation is falling
  313. p->tex_rect.y = p->tex_size *ANIMATION_FALL;
  314. }
  315. else {
  316. // animation is jumping
  317. p->tex_rect.y = p->tex_size *ANIMATION_JUMP;
  318. }
  319. // move player up/down, if player goes below platform, they are now standing on it
  320. p->rect.y += p->velocity_y * 10;
  321. if (game_player_water_drown(p)) {
  322. p->state = DROWN;
  323. }
  324. else
  325. if (p->rect.y +p->rect.h > platform_y) {
  326. p->rect.y = platform_y -p->rect.h;
  327. p->velocity_y = 0;
  328. p->state = IDLE;
  329. }
  330. // if player touches another player, step on them
  331. else {
  332. for (int i = 0; i < 2; i++) {
  333. // not on self
  334. if (&p->players[i] == p) continue;
  335. // actual collision
  336. if (game_math_collide(&p->players[i].rect, &p->rect)) {
  337. p->rect.y = p->players[i].rect.y +(p->velocity_y > 0 ? -p->rect.h : p->rect.h);
  338. p->velocity_y = 0;
  339. break;
  340. }
  341. }
  342. }
  343. // when falling, the character still moves left/right
  344. game_player_movement(p);
  345. break;
  346. // player is casting skill
  347. case CAST:
  348. // casting is done, apply effect and fall
  349. if (game_player_animate(p)) {
  350. game_player_cast(p);
  351. p->state = FALL;
  352. }
  353. // don't move during casting
  354. p->move = MOVE_NONE;
  355. // follow standing platform
  356. p->rect.y = platform_y -p->rect.h;
  357. // nullify speed for now
  358. p->velocity_y = 0;
  359. break;
  360. /* players drown when falling deep enough in the water
  361. * they remain there, floating, forever
  362. */
  363. case DROWN:
  364. // for now do nothing
  365. return;
  366. default:
  367. break;
  368. }
  369. } // update
  370. /* draw player using its rect
  371. */
  372. void game_player_draw(struct game_player *p) {
  373. p->rect.x -= p->tex_size/2;
  374. if (p->looking_side == LOOKING_RIGHT) {
  375. p->tex_rect.x += 192; // 4 frames * 48 pixels = 192
  376. }
  377. SDL_RenderCopy(ren, p->tex, &p->tex_rect, &p->rect);
  378. if (p->looking_side == LOOKING_RIGHT) {
  379. p->tex_rect.x -= 192;
  380. }
  381. p->rect.x += p->tex_size/2;
  382. }
  383. /* movement
  384. */
  385. void game_player_moveleft(struct game_player *p) {
  386. // some states take control away
  387. if (p->state == CAST || p->state == DROWN || p->state == ATTACK) return;
  388. p->move = MOVE_LEFT;
  389. p->looking_side = LOOKING_LEFT;
  390. }
  391. void game_player_moveright(struct game_player *p) {
  392. // some states take control away
  393. if (p->state == CAST || p->state == DROWN || p->state == ATTACK) return;
  394. p->move = MOVE_RIGHT;
  395. p->looking_side = LOOKING_RIGHT;
  396. }
  397. void game_player_movestop(struct game_player *p) {
  398. p->move = MOVE_NONE;
  399. }
  400. /* jump
  401. */
  402. void game_player_jump(struct game_player *p) {
  403. if (p->state == IDLE) {
  404. p->state = JUMP;
  405. }
  406. }
  407. // player's special skill
  408. void game_player_useskill(struct game_player *p) {
  409. // skills usage:
  410. // only on platform
  411. // can't use while in water
  412. // can't use when in cooldown
  413. if (p->state != IDLE
  414. || game_player_water_collide(p)
  415. || p->casting_cooldown > 0) return;
  416. p->state = CAST;
  417. p->anim_count = 0;
  418. p->tex_rect.x = 0;
  419. p->tex_rect.y = p->tex_size *ANIMATION_CAST;
  420. } // use skill
  421. /* player's attack
  422. * change state (for behaviour) and texture rect (to animate properly)
  423. */
  424. void game_player_attack(struct game_player *p) {
  425. // attack is only allowed while player is on the platform (not doing something else)
  426. if (p->state != IDLE) {
  427. return;
  428. }
  429. p->state = ATTACK;
  430. p->anim_count = 0;
  431. p->tex_rect.x = 0;
  432. p->tex_rect.y = p->tex_size *ANIMATION_ATTACK;
  433. } // attack
  434. // ai behaviour
  435. void game_player_ai(struct game_player *p) {
  436. /* find the tallest map platform
  437. * is iterated platform taller?
  438. * then player's target is that platform now
  439. */
  440. for (int i = 0; i < 10; i++) {
  441. if (game_map_platform_y(p->map, i) < game_map_platform_y(p->map, p->map_target)) {
  442. p->map_target = i;
  443. }
  444. }
  445. // find target platform's x
  446. int target_x = game_map_platform_x(p->map, p->map_target) +p->map->grid_width/2;
  447. // approach it
  448. if (p->rect.x > target_x) {
  449. game_player_moveleft(p);
  450. }
  451. else {
  452. game_player_moveright(p);
  453. }
  454. // if too close to target, don't do anything else
  455. if (p->rect.x -target_x > -p->map->grid_width/3
  456. && p->rect.x -target_x < p->map->grid_width/3) {
  457. game_player_movestop(p);
  458. game_player_useskill(p);
  459. return;
  460. }
  461. /* if standing on platform
  462. * and target platform is taller
  463. * jump
  464. */
  465. if (p->state == IDLE
  466. && game_map_platform_y(p->map, p->map_target) < game_map_platform_y(p->map, p->platform_id)) {
  467. game_player_jump(p);
  468. }
  469. } // ai
  470. void game_player_bump(struct game_player *p, struct game_player *p2) {
  471. int from_direction = p2->looking_side;
  472. p->velocity_x = PLAYER_BUMP_POWER(p2) *from_direction;
  473. p->velocity_y = -1;
  474. p->state = FALL;
  475. p->looking_side = from_direction == LOOKING_LEFT ? LOOKING_RIGHT : LOOKING_LEFT;
  476. }
  477. // collision data
  478. int game_player_left(struct game_player *p) { return p->rect.x; }
  479. int game_player_top (struct game_player *p) { return p->rect.y; }
  480. int game_player_right (struct game_player *p) { return p->rect.x +p->rect.w; }
  481. int game_player_bottom(struct game_player *p) { return p->rect.y +p->rect.h; }
  482. int game_player_centerx(struct game_player *p) { return p->rect.x +p->rect.w/2; }
  483. int game_player_centery(struct game_player *p) { return p->rect.y +p->rect.h/2; }
  484. void game_player_offset(struct game_player *s, int x, int y) {
  485. s->rect.x += x;
  486. s->rect.y += y;
  487. }
  488. int game_player_get_state(struct game_player *s) { return s->state; }