NEWS: Welcome to my new homepage! <3

player.c - freezo - A retro platform game

freezo

A retro platform game
git clone git://192.168.2.2/freezo
Log | Files | Refs | README

player.c (12853B)


      1 #include <stdlib.h>
      2 #include <math.h>
      3 
      4 #include "player.h"
      5 #include "const.h"
      6 #include "tile.h"
      7 #include "util.h"
      8 #include "entity.h"
      9 #include "effect.h"
     10 
     11 player_t *player_create(pos_t pos) {
     12   player_t *player = malloc(sizeof(player_t));
     13   player->pos = pos;
     14   player->health = 6;
     15   player->on_ground = false;
     16   player->velocity = 0.0;
     17   player->gravity = 0.0;
     18   player->dir = 1;
     19   player->sneaking = false;
     20   player->shooting = false;
     21   player->damage_timer = 0;
     22   player->fall_height = 0.0;
     23   player->held_enemy = NULL;
     24   player->moving = false;
     25   player->timer_walking = timer_create(9, 4);
     26   player->timer_sneaking = timer_create(9, 8);
     27   player->target_entity = NULL;
     28   player->shooting_distance = PLAYER_SHOOTING_RANGE;
     29   return player;
     30 }
     31 
     32 void player_update(player_t *player, game_t *game) {
     33   float move = 0.0;
     34   bool go_down = false;
     35   bool sneaking = false;
     36   bool holding = false;
     37   player->shooting = false;
     38   player->moving = false;
     39   tile_t *left_ground_tile = tile_get(game, player->pos.x + 0.2 * TILE_WIDTH, player->pos.y + PLAYER_HEIGHT + 1);
     40   tile_t *right_ground_tile = tile_get(game, player->pos.x + PLAYER_WIDTH - 0.2 * TILE_WIDTH, player->pos.y + PLAYER_HEIGHT + 1);
     41   // game over
     42   if (player->health <= 0) {
     43     game->defeat = true;
     44   }
     45   if (IsKeyDown(KEY_A) && !IsKeyDown(KEY_D)) {
     46     move = -3.0;
     47     player->dir = -1;
     48     player->moving = true;
     49   }
     50   if (IsKeyDown(KEY_D) && !IsKeyDown(KEY_A)) {
     51     move = 3.0;
     52     player->dir = 1;
     53     player->moving = true;
     54   }
     55   if (IsKeyDown(KEY_S)) {
     56     sneaking = true;
     57   }
     58   if (IsKeyDown(KEY_S) && IsKeyDown(KEY_SPACE)) {
     59     if (
     60       (left_ground_tile == NULL || !tile_solid(left_ground_tile)) &&
     61       (right_ground_tile == NULL || !tile_solid(right_ground_tile))
     62     ) {
     63       go_down = true;
     64     }
     65   }
     66   if (IsKeyPressed(KEY_SPACE)) {
     67     if (player->on_ground && !player->sneaking && !go_down) {
     68       player->velocity = 13.0;
     69     }
     70   }
     71   if (IsKeyDown(KEY_ENTER)) {
     72     player->shooting = true;
     73   }
     74   if (IsKeyPressed(KEY_W)) {
     75     holding = true;
     76   }
     77   // detect on ground
     78   bool on_ground = false;
     79   if (!(player->velocity > 0.0)) {
     80     if (!go_down) {
     81       for (int i = 0; i < game->tiles_len; i++) {
     82         tile_t *tile = game->tiles[i];
     83         if (tile_ground(tile)) {
     84           if (
     85             player->pos.x + PLAYER_WIDTH > tile->pos.x &&
     86             player->pos.x < tile->pos.x + TILE_WIDTH
     87           ) {
     88             float tolerance = 4.0;
     89             if (player->gravity > tolerance) {
     90               tolerance = player->gravity;
     91             }
     92             if (fabs(player->pos.y + PLAYER_HEIGHT - tile->pos.y) < tolerance) {
     93               player->pos.y = tile->pos.y - PLAYER_HEIGHT;
     94               on_ground = true;
     95               if (player->fall_height >= 1.5 * TILE_HEIGHT) {
     96                 effect_play(player->pos, EFFECT_FALL, game);
     97               }
     98               // fall damage
     99               if (player->fall_height >= 6 * TILE_HEIGHT) {
    100                 if (player->damage_timer == 0) {
    101                   if (player->health > 0) {
    102                     player->health--;
    103                     player->damage_timer = 80;
    104                     player->velocity = 6.0;
    105                     if (player->held_enemy != NULL) {
    106                       player->held_enemy = NULL;
    107                     }
    108                   }
    109                 }
    110               }
    111             }
    112           }
    113         }
    114       }
    115     }
    116     for (int i = 0; i < game->entities_len; i++) {
    117       if (game->entities[i].type == ENTITY_ENEMY) {
    118         enemy_t *enemy = game->entities[i].enemy;
    119         if (enemy->frozen) {
    120           if (
    121             player->pos.x + PLAYER_WIDTH > enemy->pos.x &&
    122             player->pos.x < enemy->pos.x + ENEMY_WIDTH
    123           ) {
    124             if (fabs(player->pos.y + PLAYER_HEIGHT - enemy->pos.y) < 4.0) {
    125               player->pos.y = enemy->pos.y - PLAYER_HEIGHT;
    126               on_ground = true;
    127               if (player->sneaking && player->fall_height >= 2 * TILE_HEIGHT) {
    128                 effect_play(enemy->pos, EFFECT_BREAK, game);
    129                 effect_play(enemy->pos, EFFECT_PARTICLE, game);
    130                 enemy_kill(enemy, game);
    131                 i--;
    132                 player->velocity = 6.0;
    133               }
    134             }
    135           }
    136         }
    137       }
    138     }
    139   }
    140   // detect ceiling
    141   if (player->velocity > 0.0) {
    142     for (int i = 0; i < game->tiles_len; i++) {
    143       tile_t *tile = game->tiles[i];
    144       if (tile_solid(tile)) {
    145         if (
    146           player->pos.x + PLAYER_WIDTH - 0.2 * TILE_WIDTH > tile->pos.x &&
    147           player->pos.x + 0.2 * TILE_WIDTH < tile->pos.x + TILE_WIDTH
    148         ) {
    149           float tolerance = 4.0;
    150           if (player->velocity > tolerance) {
    151             tolerance = player->velocity;
    152           }
    153           if (fabs(player->pos.y - (tile->pos.y + 0.2 * TILE_HEIGHT)) < tolerance) {
    154             player->pos.y = tile->pos.y + 0.2 * TILE_HEIGHT;
    155             player->velocity = 0.0;
    156           }
    157         }
    158       }
    159     }
    160   }
    161   // detect wall
    162   if (fabs(move) > 0.0) {
    163     for (int i = 0; i < game->tiles_len; i++) {
    164       tile_t *tile = game->tiles[i];
    165       if (tile_wall(tile)) {
    166         if (
    167           player->pos.y + PLAYER_HEIGHT > tile->pos.y &&
    168           player->pos.y < tile->pos.y + TILE_HEIGHT
    169         ) {
    170           float tolerance = 4.0;
    171           if (move > tolerance) {
    172             tolerance = player->velocity;
    173           }
    174           if (player->dir > 0) {
    175             if (fabs((player->pos.x + PLAYER_WIDTH - 0.2 * TILE_WIDTH) - tile->pos.x) < tolerance) {
    176               player->pos.x = tile->pos.x - (PLAYER_WIDTH - 0.2 * TILE_WIDTH);
    177             }
    178           }
    179           else {
    180             if (fabs((player->pos.x + 0.2 * TILE_WIDTH) - (tile->pos.x + TILE_WIDTH)) < tolerance) {
    181               player->pos.x = tile->pos.x + TILE_WIDTH - 0.2 * TILE_WIDTH;
    182             }
    183           }
    184         }
    185       }
    186     }
    187   }
    188   player->on_ground = on_ground;
    189   player->sneaking = sneaking;
    190   // handle move
    191   if (player->sneaking || player->shooting) {
    192     player->pos.x += 0.5 * move;
    193   }
    194   else {
    195     player->pos.x += move;
    196   }
    197   // handle map border
    198   if (player->pos.x < 0.0) {
    199     player->pos.x = 0.0;
    200   }
    201   if (player->pos.x + PLAYER_WIDTH > TILE_WIDTH * game->level->width) {
    202     player->pos.x = TILE_WIDTH * game->level->width - PLAYER_WIDTH;
    203   }
    204   // handle jump
    205   if (player->velocity > 0.0) {
    206     player->pos.y -= player->velocity;
    207     player->velocity -= 1.1;
    208   }
    209   if (player->on_ground) {
    210     player->gravity = 0.0;
    211     player->fall_height = 0.0;
    212   }
    213   else {
    214     player->pos.y += player->gravity;
    215     player->fall_height += player->gravity;
    216     if (player->gravity < PLAYER_GRAVITY) {
    217       player->gravity += 0.2;
    218     }
    219     else {
    220       player->gravity = PLAYER_GRAVITY;
    221     }
    222   }
    223   if (player->damage_timer > 0) {
    224     player->damage_timer--;
    225   }
    226   // detect shooting
    227   if (player->shooting) {
    228     // determine target
    229     float shooting_range = 2 * TILE_WIDTH;
    230     rect_t shooting_rect = {
    231       .x = player->dir > 0 ? player->pos.x + PLAYER_WIDTH : player->pos.x - shooting_range,
    232       .y = player->sneaking ? player->pos.y + 8 * SCALE : player->pos.y + 7 * SCALE,
    233       .width = shooting_range,
    234       .height = SCALE,
    235     };
    236     float closest_distance = shooting_range;
    237     entity_t *closest_entity = NULL;
    238     bool target_in_range = false;
    239     for (int i = 0; i < game->entities_len; i++) {
    240       entity_t *entity = &game->entities[i];
    241       // ignore held enemies
    242       if (entity->type == ENTITY_ENEMY && player->held_enemy == entity->enemy) {
    243         continue;
    244       }
    245       // ignore frozen gates
    246       if (entity->type == ENTITY_GATE && entity->gate->frozen) {
    247         continue;
    248       }
    249       rect_t entity_rect;
    250       switch (entity->type) {
    251         case ENTITY_ENEMY:
    252           entity_rect = rect_from_pos(entity->enemy->pos, ENEMY_WIDTH, ENEMY_HEIGHT);
    253           break;
    254         case ENTITY_GATE:
    255           entity_rect = rect_from_pos((pos_t) { entity->gate->pos.x, entity->gate->pos.y - TILE_HEIGHT }, ENEMY_WIDTH, ENEMY_HEIGHT);
    256           break;
    257       }
    258       if (rect_collide(shooting_rect, entity_rect)) {
    259         float entity_x = entity_rect.x + entity_rect.width / 2.0;
    260         float entity_distance = fabs(entity_x - (player->dir > 0 ? player->pos.x + PLAYER_WIDTH : player->pos.x));
    261         if (player->target_entity != NULL && player->target_entity == entity) {
    262           target_in_range = true;
    263         }
    264         if (entity_distance < closest_distance) {
    265           closest_distance = entity_distance;
    266           closest_entity = entity;
    267         }
    268       }
    269     }
    270     if (closest_entity != NULL) {
    271       player->shooting_distance = closest_distance;
    272       if (!target_in_range) {
    273         player->target_entity = closest_entity;
    274       }
    275     }
    276     else {
    277       player->target_entity = NULL;
    278       player->shooting_distance = PLAYER_SHOOTING_RANGE;
    279     }
    280   }
    281   else {
    282     player->target_entity = NULL;
    283     player->shooting_distance = PLAYER_SHOOTING_RANGE;
    284   }
    285   // detect holding
    286   if (holding) {
    287     bool picked_up = false;
    288     for (int i = 0; i < game->entities_len; i++) {
    289       if (game->entities[i].type == ENTITY_ENEMY) {
    290         enemy_t *enemy = game->entities[i].enemy;
    291         if (enemy->frozen) {
    292           float tolerance = 0.0;
    293           if (
    294             player->pos.x <= enemy->pos.x + ENEMY_WIDTH - tolerance &&
    295             player->pos.x + PLAYER_WIDTH >= enemy->pos.x + tolerance  &&
    296             player->pos.y <= enemy->pos.y + ENEMY_HEIGHT - tolerance  &&
    297             player->pos.y + PLAYER_HEIGHT >= enemy->pos.y + tolerance 
    298           ) {
    299             if (player->held_enemy == NULL) {
    300               player->held_enemy = enemy;
    301               picked_up = true;
    302             }
    303           }
    304         }
    305       }
    306     }
    307     if (!picked_up && player->held_enemy != NULL) {
    308       player->held_enemy = NULL;
    309     }
    310   }
    311   if (player->held_enemy != NULL) {
    312     player->held_enemy->pos = (pos_t) {
    313       .x = player->pos.x,
    314       .y = player->pos.y - (player->sneaking ? 0.3 : 0.5) * TILE_HEIGHT,
    315     };
    316   }
    317   // detect damage
    318   for (int i = 0; i < game->entities_len; i++) {
    319     if (game->entities[i].type == ENTITY_ENEMY) {
    320       enemy_t *enemy = game->entities[i].enemy;
    321       if (!enemy->frozen) {
    322         float tolerance = TILE_WIDTH / 5.0;
    323         if (
    324           player->pos.x <= enemy->pos.x + ENEMY_WIDTH - tolerance &&
    325           player->pos.x + PLAYER_WIDTH >= enemy->pos.x + tolerance  &&
    326           player->pos.y <= enemy->pos.y + ENEMY_HEIGHT - tolerance  &&
    327           player->pos.y + PLAYER_HEIGHT >= enemy->pos.y + tolerance 
    328         ) {
    329           if (player->damage_timer == 0) {
    330             if (player->health > 0) {
    331               player->health--;
    332               player->damage_timer = 80;
    333               player->velocity = 8.0;
    334               if (player->pos.x + PLAYER_WIDTH / 2.0 > enemy->pos.x + ENEMY_WIDTH / 2.0) {
    335                 player->pos.x += 20.0;
    336               }
    337               else {
    338                 player->pos.x -= 20.0;
    339               }
    340               if (player->held_enemy != NULL) {
    341                 player->held_enemy = NULL;
    342               }
    343             }
    344           }
    345         }
    346       }
    347     }
    348   }
    349   // fall out of map
    350   if (player->pos.y > TILE_HEIGHT * game->level->height) {
    351     if (player->health > 0) {
    352       player->health = 0;
    353     }
    354   }
    355   // update timer
    356   if (player->on_ground && player->moving) {
    357     timer_update(player->timer_walking);
    358     timer_update(player->timer_sneaking);
    359   }
    360   else {
    361     timer_reset(player->timer_walking);
    362     timer_reset(player->timer_sneaking);
    363   }
    364 }
    365 
    366 void player_draw(player_t *player, game_t *game) {
    367   pos_t pos = pos_snap(player->pos);
    368   int frame = (player->sneaking || player->shooting)
    369     ? timer_get(player->timer_sneaking)
    370     : timer_get(player->timer_walking);
    371   if (player->damage_timer / 4 % 2 == 0) {
    372     if (player->sneaking) {
    373       if (player->dir > 0) {
    374         DrawTextureRec(game->assets.entities, texture_rect(frame, 6, PLAYER_WIDTH, PLAYER_HEIGHT), pos, WHITE);
    375       }
    376       else {
    377         DrawTextureRec(game->assets.entities, texture_rect(frame, 7, PLAYER_WIDTH, PLAYER_HEIGHT), pos, WHITE);
    378       }
    379     }
    380     else {
    381       if (player->dir > 0) {
    382         DrawTextureRec(game->assets.entities, texture_rect(frame, 4, PLAYER_WIDTH, PLAYER_HEIGHT), pos, WHITE);
    383       }
    384       else {
    385         DrawTextureRec(game->assets.entities, texture_rect(frame, 5, PLAYER_WIDTH, PLAYER_HEIGHT), pos, WHITE);
    386       }
    387     }
    388     if (player->shooting) {
    389       float y = player->sneaking ? pos.y + 8 * SCALE : pos.y + 7 * SCALE;
    390       if (player->dir > 0) {
    391         DrawRectangle(pos.x + PLAYER_WIDTH, y, player->shooting_distance, 1 * SCALE, BLUE);
    392       }
    393       else {
    394         DrawRectangle(pos.x - player->shooting_distance, y, player->shooting_distance, 1 * SCALE, BLUE);
    395       }
    396     }
    397   }
    398 }
    399 
    400 void player_free(player_t *player) {
    401   timer_free(player->timer_walking);
    402   timer_free(player->timer_sneaking);
    403   free(player);
    404 }