Collab_RPG/src/player.c

237 lines
7.2 KiB
C

#include "player.h"
#include "config.h"
#include "dialogs.h"
#include "game.h"
#include "map.h"
#include "npc.h"
#include <gint/display.h>
extern bopti_image_t player_male_img;
extern bopti_image_t player_female_img;
extern bopti_image_t npc_male;
extern bopti_image_t npc_female;
extern bopti_image_t npc_milkman;
extern bopti_image_t npc_police;
extern bopti_image_t SGN_Icon_img;
extern bopti_image_t INFO_Icon_img;
const bopti_image_t *faces[FACES] = {&npc_male, &npc_female, &npc_milkman,
&npc_police};
const char one_px_mov[8] = {
0, -1, /* Up */
0, 1, /* Down */
-1, 0, /* Left */
1, 0 /* Right */
};
/* TODO: Search for all hard tiles in the tileset. hard_tiles is a list of their
* IDs */
/* The speed of the player on the diffrent tiles in the walkable layer. */
#define WALKABLE_TILE_MAX 4
const short int walkable_speed[WALKABLE_TILE_MAX] = {SPEED, 0, PXSIZE, PXSIZE};
/* How much damage the player takes on the diffrent tiles in the walkable
* layer. */
const char damage_taken_walkable[WALKABLE_TILE_MAX] = {0, 0, 5, 0};
extern bopti_image_t demo_player_img;
extern NPC *npcRPG;
extern uint32_t nbNPC;
void player_draw(Game *game) {
Player *player = &game->player;
dimage(player->px - P_WIDTH / 2, player->py - P_HEIGHT / 2,
player->is_male ? &player_male_img : &player_female_img);
}
void player_move(Game *game, Direction direction) {
Player *player = &game->player;
/* How this player movement will modify the player x and y. */
char dx, dy;
/* If the player will collide with a hard tile or if the will go outside of
* the map. */
if(player_collision(game, direction, P_CENTER)) {
/* If the will collide with the center of the player. */
dx = one_px_mov[direction * 2] * player->speed;
dy = one_px_mov[direction * 2 + 1] * player->speed;
player_fix_position(game, dx, dy);
} else {
if(player_collision(game, direction, P_RIGHTDOWN) ||
player_collision(game, direction, P_LEFTUP)) {
/* If the will collide with the edges of the player. */
/* I fix his position so he won't be partially in the tile. */
/* I invert dx and dy to fix the axis where he is not moving on. */
/* Do not replace dx==0 with !dx or dy==0 with !dy, it won't work!
*/
dx = one_px_mov[direction * 2] * player->speed;
dy = one_px_mov[direction * 2 + 1] * player->speed;
player_fix_position(game, dx == 0, dy == 0);
}
/* If he won't collide with the center, so I just move him normally */
dx = one_px_mov[direction * 2] * player->speed;
dy = one_px_mov[direction * 2 + 1] * player->speed;
player->x += dx;
player->y += dy;
}
/* Check if we should change map */
Map *target = map_get_for_tile(game,
game->map_level->x + player->x / T_WIDTH +
one_px_mov[direction * 2],
game->map_level->y + player->y / T_HEIGHT +
one_px_mov[direction * 2 + 1]);
if(target != game->map_level) {
if(target->x > game->map_level->x) {
player->x = 0;
}
if(target->x < game->map_level->x) {
player->x = target->w * T_WIDTH;
}
if(target->y > game->map_level->y) {
player->y = 0;
}
if(target->y < game->map_level->y) {
player->y = target->h * T_HEIGHT;
}
npc_reload(game);
game->map_level = target;
}
}
void player_action(Game *game) {
/* already doing something, or can't do anything*/
if(game->player.isDoingAction || !game->player.canDoSomething)
return;
if(!game->player.isInteractingWithNPC) {
/* we can do something */
/* we indicate that the player is occupied */
game->player.isDoingAction = true;
Sign *sign = &game->map_level->signs[game->player.whichAction];
bopti_image_t *face;
/* we use the correct image as per the type of the item */
if(sign->icon)
face = &INFO_Icon_img;
else
face = &SGN_Icon_img;
uint32_t dialogStart = sign->dialogID;
dialogs_initiate_sequence(game, face, dialogStart);
/* when done we release the occupied status of the player */
game->player.isDoingAction = false;
} else {
/* we can do something (action IS with an NPC) */
/* we indicate that the player is occupied */
game->player.isDoingAction = true;
NPC *currentNPC = &game->map_level->npcs[game->player.whichAction];
/* we use the correct image as per the class of the item */
bopti_image_t *face = &npc_male;
/* It's a NPC */
face = (bopti_image_t *)faces[currentNPC->face];
uint32_t dialogStart = currentNPC->dialogID;
/* we set this NPC to paused to avoid changing its position while
* talking (the rest of the NPCs pursue their action) */
currentNPC->paused = true;
dialogs_initiate_sequence(game, face, dialogStart);
/* when done we release the occupied status of the player */
game->player.isDoingAction = false;
currentNPC->paused = false;
}
}
bool player_collision(Game *game, Direction direction,
Checkpos nomov_axis_check) {
Player *player = &game->player;
/* Where is the tile where he will go to from his position. */
char dx = one_px_mov[direction * 2];
char dy = one_px_mov[direction * 2 + 1];
if(!dx) {
dx += nomov_axis_check;
} else if(!dy) {
dy += nomov_axis_check;
}
dx = dx * (P_WIDTH / 2 + 1);
dy = dy * (P_HEIGHT / 2 + 1);
/* The tile he will go to. */
int player_tile_x = player->x + dx;
int player_tile_y = player->y + dy;
/* Handle a negative position differently than a positive one. */
if(player_tile_x < 0)
player_tile_x = player_tile_x / T_WIDTH - 1;
else
player_tile_x = player_tile_x / T_WIDTH;
if(player_tile_y < 0)
player_tile_y = player_tile_y / T_HEIGHT - 1;
else
player_tile_y = player_tile_y / T_HEIGHT;
int on_walkable = map_get_walkable(game, player_tile_x, player_tile_y);
int speed = (on_walkable >= 0 && on_walkable < WALKABLE_TILE_MAX)
? walkable_speed[on_walkable]
: 0;
// speed = SPEED;
/* if he's on a hard tile */
if(!speed) {
return true; /* He will collide with it. */
}
player->speed = speed;
return false; /* He won't collide with a hard tile. */
}
void player_fix_position(Game *game, bool fix_x, bool fix_y) {
Player *player = &game->player;
/* I fix his poition on x or/and on y if y need to, so that he won't be over
* the hard tile that he collided with. */
if(fix_x)
player->x = player->x / T_WIDTH * T_WIDTH + P_WIDTH / 2;
if(fix_y)
player->y = player->y / T_HEIGHT * T_HEIGHT + P_HEIGHT / 2;
}
void player_damage(Game *game, int amount) {
Player *player = &game->player;
player->life -= amount;
/* TODO: Let the player dye if life < 1. */
};