/* Copyright 2008 Ido Yehieli This file is part of CryptRover. CryptRover 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. CryptRover 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 CryptRover. If not, see . */ #include #include #include "map.h" #include "utils.h" #include "entities.h" #include "items.h" #include "io.h" #ifdef __SDL__ Mix_Chunk *bite = NULL; Mix_Chunk *punch = NULL; Mix_Chunk *grunt = NULL; Mix_Chunk *alert = NULL; #endif bool close_to_player(int y,int x) { return in_range(ent_l[0].y,ent_l[0].x,y,x,FOV_RADIUS-1); } void init_ents(int level) { #ifdef __SDL__ if(!bite) bite = Mix_LoadWAV("media/bite.wav"); if(!punch) punch = Mix_LoadWAV("media/punch.wav"); if(!grunt) grunt = Mix_LoadWAV("media/grunt.wav"); if(!alert) alert = Mix_LoadWAV("media/alert.wav"); #endif memset(ent_m,(int)NULL,sizeof(ent_t *)*Y_*X_); for (int e=0; eid=e; ce->awake=false; do { ce->y=rand()%Y_; ce->x=rand()%X_; } while (WALL==tile_m[ce->y][ce->x].type || NULL!=ent_m[ce->y][ce->x] || (e && close_to_player(ce->y,ce->x)) ); if (e>0) { ce->hp=2; ce->air=1; ce->speed=3; ce->battery=1; ce->coins=0; ce->light_on=1; ce->type=ARACHNID; ce->color=COLOR_PAIR(COLOR_RED); } ent_m[ce->y][ce->x]=ce; } //initial player attributes ent_t *pl=&ent_l[0]; if (1==level) { pl->hp=PLAYER_HP; pl->air=PLAYER_AIR; pl->speed=0;//special case: move every turn pl->battery=PLAYER_BATTERY; pl->coins=0; pl->light_on=1; pl->type='@'; pl->color=COLOR_PAIR(COLOR_WHITE); } } //compare 2 tiles by their distance to the player int compare_tiles(const void* t1, const void* t2) { int py=ent_l[0].y; int px=ent_l[0].x; tile_t* tile1 = (tile_t*)t1; tile_t* tile2 = (tile_t*)t2; if (dist(tile1->y,tile1->x,py,px)y,tile2->x,py,px)) return -1; else if (dist(tile1->y,tile1->x,py,px)==dist(tile2->y,tile2->x,py,px)) return 0; else return 1; } //field of view void fov(int y, int x, int radius) { int nr=ent_l[0].light_on?radius*ent_l[0].battery/PLAYER_BATTERY:0; for (int yy=max(y-nr,0); yy<=min(y+nr,Y_-1); yy++) for (int xx=max(x-nr,0); xx<=min(x+nr,X_-1); xx++) if (los(y,x,yy,xx,WALL,NULL)) view_m[yy][xx]=IN_SIGHT; } //move entity if there is no living entity on the way bool move_to(int *y,int *x,int dy,int dx) { int id=ent_m[*y][*x]->id; //don't move into walls if (WALL==tile_m[*y+dy][*x+dx].type) { if (!id) add_message("There is a wall in the way!",0); return false; } //if the destination tile has an entity in it if (NULL!=ent_m[*y+dy][*x+dx]) { ent_t *de=ent_m[*y+dy][*x+dx]; //to prevent enemies from attacking one another if (!id||!de->id) { de->hp--; if (id) { #ifdef __SDL__ Mix_PlayChannel(-1, bite, 0); Mix_PlayChannel(-1, grunt, 0); #endif add_message("The arachnid bites you.",0); if (de->hp<=MED_CHARGE && de->hp>0){ #ifdef __SDL__ Mix_PlayChannel(-1, alert, 0); #endif add_message("DANGER - LOW HITPOINTS.",C_MED|A_BOLD); } } else { #ifdef __SDL__ Mix_PlayChannel(-1, punch, 0); #endif add_message("You hit the arachnid.",0); } } else return false; //if it's dead remove its reference from the entity map if (de->hp<1) { ent_m[de->y][de->x]=NULL; if (!id){ add_message("You kill the arachnid!",de->color); //blood splatter tile_color_m[de->y+dy][de->x+dx]=COLOR_PAIR(COLOR_RED); for(int yy=de->y-1;yy<=de->y+1;yy++) for(int xx=de->x-1;xx<=de->x+1;xx++) if(!0==rand()%4) tile_color_m[yy][xx]=COLOR_PAIR(COLOR_RED)|(!(rand()%3)?A_BOLD:A_NORMAL); } else add_message("The arachnid kills you!",C_MED|A_STANDOUT); } //the move was still successful because of the attack return true; } //remove reference to the entity's old position ent_m[*y][*x]=NULL; //update entity's position *y+=dy; *x+=dx; //add reference to the entity's new position ent_m[*y][*x]=&ent_l[id]; return true; } void move_enemy(ent_t *enemy, ent_t *player) { int *ey=&enemy->y; int *ex=&enemy->x; if (enemy->awake || (abs(*ey-player->y)<=FOV_RADIUS && abs(*ex-player->x)<=FOV_RADIUS && los(*ey,*ex,player->y,player->x,WALL,NULL))) { if (!enemy->awake) enemy->awake=true; //sort the adjunct tiles by their distance to the player tile_t adj_tile[9]; int t=0; for (int y=*ey-1;y<=*ey+1;y++) for (int x=*ex-1;x<=*ex+1;x++) adj_tile[t++]=tile_m[y][x]; qsort(adj_tile,9,sizeof(tile_t),compare_tiles); //move to the closest possible tile t=0; while (t<9 && !move_to(ey,ex,adj_tile[t].y-*ey,adj_tile[t].x-*ex)) { t++; } } else { //sleeping enemies move randomly move_to(ey,ex,-1+rand()%3,-1+rand()%3); } }