/* * player.c -- Implementa el personaje controlado por el jugador. * Copyright (C) 2018 Juan MarĂ­n Noguera * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include "collision_type.h" #include "block.h" #include "error.h" #include "player.h" #include "screen.h" #define PLAYER_GRAVITY 0.15 #define PLAYER_ACCEL 0.2 #define PLAYER_ACCEL_FLYING 0.02 #define PLAYER_VELOCITY_JUMP 4.5 #define PLAYER_MAX_VELOCITY 1.8 #define PLAYER_ANIM_TICKS 5 #define PLAYER_ANIM_NO 3 #define PLAYER_PICT_FILE "50365.bmp" #define PLAYER_SMALL_Y 34 #define PLAYER_SMALL_H 16 #define PLAYER_BIG_Y 1 #define PLAYER_BIG_H 32 #define PLAYER_FLOWER_Y 129 #define PLAYER_SHIFT_X 17 #define PLAYER_DEAD_X 182 #define PLAYER_LOOK_X 80 #define PLAYER_JUMP_X 165 #define PLAYER_GROUND_X 182 #define PLAYER_WALK_X 97 #define PLAYER_VULNERABLE_TICKS 60 struct Player { double x; double y; double vx; double vy; PlayerState state; PlayerPos pos; int left; int counter; int coins; int lives; int vuln; }; Picture player_pict = NULL; Player player_create(int x, int y, int lives) { Player p = malloc(sizeof(struct Player)); if (p == NULL) error_libc_exit(); p->x = x; p->y = y; p->vx = 0; p->vy = 0; p->state = PLAYER_ST_SMALL; p->pos = PLAYER_POS_UPRIGHT; p->counter = 0; p->left = 0; p->coins = 0; p->lives = lives; p->vuln = PLAYER_VULNERABLE_TICKS; return p; } void player_free(Player p) { if(p == NULL) error_exit(ERR_NULL_PARAM); free(p); } void player_end() { if (player_pict != NULL) { screen_free_picture(player_pict); player_pict = NULL; } } double player_x(Player p) { return p->x; } void player_set_x(Player p, double x) { p->x = x; } double player_y(Player p) { return p->y; } void player_set_y(Player p, double y) { p->y = y; } double player_vx(Player p) { return p->vx; } double player_vy(Player p) { return p->vy; } PlayerState player_state(Player p) { return p->state; } PlayerPos player_pos(Player p) { return p->pos; } void player_set_pos(Player p, PlayerPos pos) { if (pos == PLAYER_POS_JUMP && p->pos != PLAYER_POS_JUMP) p->vy = PLAYER_VELOCITY_JUMP; p->pos = pos; } int player_coins(Player p) { return p->coins; } int player_lives(Player p) { return p->lives; } void player_update(Player p) { p->counter++; p->vuln++; p->vy -= PLAYER_GRAVITY; p->x += p->vx; p->y += p->vy; } int player_vulnerable(Player p) { return p->vuln >= PLAYER_VULNERABLE_TICKS; } void player_correct_on_collision(Player p, int dir) { if (dir & COLLISION_BOTTOM) { p->vy = 0; p->y = ((int)(p->y + BLOCK_SIZE) / BLOCK_SIZE) * BLOCK_SIZE; if (dir & COLLISION_TOP) p->pos = PLAYER_POS_GROUND; else p->pos = PLAYER_POS_UPRIGHT; } else if (dir & COLLISION_TOP) { p->vy = 0; p->y = p->y - 2 * (((int)p->y) % BLOCK_SIZE); } if (dir & COLLISION_LEFT) { p->vx = 0; p->x = (((int)(p->x + BLOCK_SIZE)) / BLOCK_SIZE) * BLOCK_SIZE; } if (dir & COLLISION_RIGHT) { p->vx = 0; p->x = (((int)(p->x)) / BLOCK_SIZE) * BLOCK_SIZE; } } void player_accel(Player p, int left) { double amount = p->pos == PLAYER_POS_JUMP && p->vy < 0 ? PLAYER_ACCEL_FLYING : PLAYER_ACCEL; if (left) p->vx = (p->vx > 0 ? -amount : p->vx - amount); else p->vx = (p->vx < 0 ? amount : p->vx + amount); if (p->vx > PLAYER_MAX_VELOCITY) p->vx = PLAYER_MAX_VELOCITY; else if (p->vx < -PLAYER_MAX_VELOCITY) p->vx = -PLAYER_MAX_VELOCITY; } void player_jump(Player p) { if (p->pos != PLAYER_POS_JUMP) { p->vy = PLAYER_VELOCITY_JUMP; p->pos = PLAYER_POS_JUMP; } } void player_stop(Player p) { if (p->pos != PLAYER_POS_JUMP) p->vx = 0; } int player_pict_y(Player p) { switch (p->state) { case PLAYER_ST_SMALL: case PLAYER_ST_DEAD: return PLAYER_SMALL_Y; case PLAYER_ST_BIG: return PLAYER_BIG_Y; case PLAYER_ST_FLOWER: return PLAYER_FLOWER_Y; default: error_exit(ERR_OUT_OF_RANGE); return 0; // Execution will never reach this point. } } int player_height(Player p) { return p->state == PLAYER_ST_SMALL || p->state == PLAYER_ST_DEAD ? PLAYER_SMALL_H : PLAYER_BIG_H; } int player_pict_x(Player p) { if (p->state == PLAYER_ST_DEAD) return PLAYER_DEAD_X; else if (p->pos == PLAYER_POS_JUMP) return PLAYER_JUMP_X; else if (p->pos == PLAYER_POS_GROUND && p->state != PLAYER_ST_SMALL) return PLAYER_GROUND_X; else if (p->vx == 0) return PLAYER_LOOK_X; else // Walking return PLAYER_WALK_X + ((p->counter / PLAYER_ANIM_TICKS) % PLAYER_ANIM_NO) * PLAYER_SHIFT_X; } void player_render(Screen scr, Player p, int scroll) { int h = player_height(p); if (p->vx < 0) p->left = 1; else if (p->vx > 0) p->left = 0; if (player_pict == NULL) player_pict = screen_get_picture(PLAYER_PICT_FILE); screen_place( scr, player_pict, p->x - scroll, screen_height(scr) - p->y - h, player_pict_x(p), player_pict_y(p), PLAYER_WIDTH, h, p->left); } void player_downgrade(Player p) { if (p->state != PLAYER_ST_DEAD) p->state--; p->vuln = 0; } void player_upgrade_to(Player p, int state) { if (p->state > PLAYER_ST_FLOWER) error_exit(ERR_OUT_OF_RANGE); if (p->state < state) p->state = state; } void player_die(Player p) { p->state = PLAYER_ST_DEAD; } void player_add_coin(Player p) { if (++p->coins % 100 == 0) ++p->lives; } int player_restart(Player p) { p->vx = 0; p->vy = 0; p->pos = PLAYER_POS_UPRIGHT; p->left = 0; p->counter = 0; p->vuln = PLAYER_VULNERABLE_TICKS; if (p->state == PLAYER_ST_DEAD) { player_upgrade_to(p, PLAYER_ST_SMALL); p->coins = 0; } return --p->lives > 0; }