-
Notifications
You must be signed in to change notification settings - Fork 0
/
enemy.c
executable file
·259 lines (224 loc) · 6.92 KB
/
enemy.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
#include <assert.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <curses.h>
#include "enemy.h"
#include "mob.h"
#include "effect.h"
/**
* Definitions of enemies
*/
#define ENEMY(sym, n, col, hlth, atk, def, cn, dep) { \
.symbol = (sym), .colour = (col), .name = (n), .is_bold = false,\
.hostile = true,\
.health = (hlth), .max_health = (hlth),\
.attack = (atk), .defense = (def), .con = (cn),\
.level = NULL, .xpos = 0, .ypos = 0,\
.moblist = {.next = NULL, .prev=NULL},\
.turn_action = NULL,\
.score = 0,\
.darksight = true, .luminosity = 0,\
.min_depth = (dep)}
/* should keep the same structure as EnemyType in enemy.h.
* should also be ordered by dep. */
const struct Mob default_enemies[] = {
ENEMY('H', "Hedgehog", COLOR_YELLOW, 5, 1, 0, 0, 0),
ENEMY('S', "Squirrel", COLOR_YELLOW, 10, 2, 0, 0, 0),
ENEMY('d', "Duck", COLOR_GREEN, 10, 1, 1, 1, 1),
ENEMY('g', "Goose", COLOR_WHITE, 15, 2, 2, 2, 2),
ENEMY('o', "Orc", COLOR_YELLOW, 15, 3, 2, 7, 2),
ENEMY('P', "Cave Pirate", COLOR_RED, 20, 3, 3, 5, 5),
ENEMY('W', "Wolfman", COLOR_YELLOW, 25, 10, 3, 10, 10),
ENEMY('A', "Fallen Angel", COLOR_YELLOW, 50, 12, 10, 100, 25),
ENEMY('D', "Dragon", COLOR_RED, 100,10, 10, 100, 30)
};
#undef ENEMY
/**
* Create and return an enemy of the specified type.
* @param mobtype The type of mob to make.
* @return The mob created.
*/
Mob * create_enemy(enum EnemyType mobtype){
Mob * new = xalloc(Mob);
memcpy(new, &default_enemies[mobtype], sizeof(Mob));
new->turn_action = &simple_enemy_turn;
new->death_action = &drop_corpse;
if (mobtype == ORC){
Item * sword = clone_item(O_SWORD);
new->inventory = insert(new->inventory, &sword->inventory);
wield_item(new, sword);
if (rand() % 2) {
Item * food = clone_item(FOOD_RATION);
new->inventory = insert(new->inventory, &food->inventory);
}
} else if(mobtype == CAVE_PIRATE) {
new->turn_action = &hunter_turn;
new->death_action = &hunter_death;
Item * food = clone_item(HARD_TACK);
new->inventory = insert(new->inventory, &food->inventory);
Item * cutlass = clone_item(CUTLASS);
new->inventory = insert(new->inventory, &cutlass->inventory);
wield_item(new,cutlass);
} else if(mobtype == WOLFMAN) {
new->turn_action = &hunter_turn;
new->death_action = &hunter_death;
Item * food = clone_item(N_FOOD_RATION);
new->inventory = insert(new->inventory, &food->inventory);
} else if(mobtype == FALLEN_ANGEL) {
Item * sword = clone_item(F_SWORD);
new->inventory = insert(new->inventory, &sword->inventory);
wield_item(new, sword);
Item * food = clone_item(MANNA);
new->inventory = insert(new->inventory, &food->inventory);
new->is_bold = true;
new->luminosity = 1;
} else if(mobtype == DRAGON) {
Item * weapon = clone_item(DRAG_FIRE);
new->weapon = weapon;
wield_item(new, weapon);
Item * armour = clone_item(D_MAIL);
new->inventory = insert(new->inventory, &armour->inventory);
new->armour = armour;
}
return new;
}
/**
* Move an enemy randomly, not including diagonals.
* @param enemy Enemy to move
*/
void random_move(Mob * enemy) {
if(rand() % 2) {
move_mob_relative(enemy, (rand() % 2) ? 1 : -1, 0);
} else {
move_mob_relative(enemy, 0, (rand() % 2) ? 1 : -1);
}
}
/**
* Move an enemy randomly, including diagonals.
* @param enemy Enemy to move
*/
void random_move_diagonals(Mob * enemy) {
move_mob_relative(enemy, (rand() % 3) - 1, (rand() % 3) - 1);
}
/**
* Move towards a position. If the move fails, another axis will be
* tried.
* @param enemy Enemy to move
* @param x Target X
* @param y Target Y
* @param diagonal Can move diagonally
*/
void move_towards(Mob * enemy,
unsigned int x, unsigned int y,
bool diagonal) {
int xdiff = enemy->xpos - x;
int ydiff = enemy->ypos - y;
int dx = 0;
int dy = 0;
if(diagonal) {
/* Move diagonally if we can, and are far enough away */
if(xdiff > 5) {
dx = -1;
} else if(xdiff < -5) {
dx = 1;
}
if(ydiff > 5) {
dy = -1;
} else if(ydiff < -5) {
dy = 1;
}
}
/* Move along the axis furthest away from the target */
if(abs(xdiff) > abs(ydiff)) {
bool res = move_mob_relative(enemy, (xdiff < 0) ? 1 : -1, dy);
/* If there was something solid in the way, try moving y */
if(!res) {
move_mob_relative(enemy, dx, (ydiff < 0) ? 1 : -1);
}
} else {
bool res = move_mob_relative(enemy, dx, (ydiff < 0) ? 1 : -1);
if(!res) {
move_mob_relative(enemy, (xdiff < 0) ? 1 : -1, dy);
}
}
}
/**
* A very simple enemy: move towards the player, and damage them if adjacent.
* @param enemy Entity to move.
*/
void simple_enemy_turn(Mob * enemy) {
Mob * player = enemy->level->player;
/* If we can't see the player, move randomly */
if(!can_see_other(enemy, player)) {
random_move(enemy);
return;
}
int xdiff = enemy->xpos - player->xpos;
int ydiff = enemy->ypos - player->ypos;
if((abs(xdiff) == 1 && ydiff == 0)
|| (xdiff == 0 && abs(ydiff) == 1)) {
/* If (orthogonally) adjacent to the player, damage them */
attack_mob(enemy, player);
} else {
/* Move along the axis furthest away from the player */
move_towards(enemy, player->xpos, player->ypos, false);
}
}
/**
* A more sophisticated enemy: hunters share their data field, and use
* it to instantly communicate the player position. The behaviour is
* as follows: wander randomly (including diagonals) if the player
* hasn't been seen, and all home in on the player as soon as one sees
* them.
* @param enemy Entity to move
*/
void hunter_turn(Mob * enemy) {
assert(enemy->data != NULL);
Mob * player = enemy->level->player;
HunterState * data = (HunterState *) enemy->data;
int xdiff = enemy->xpos - player->xpos;
int ydiff = enemy->ypos - player->ypos;
/* If (diagonally) adjacent to the player, damage them */
if((abs(xdiff) == 1 && ydiff == 0)
|| (xdiff == 0 && abs(ydiff) == 1)
|| (abs(xdiff) == 1 && abs(ydiff) == 1)) {
attack_mob(enemy, player);
return;
}
/* Player found, update the shared state */
if(can_see_other(enemy, player)) {
data->x = player->xpos;
data->y = player->ypos;
data->chase = true;
} else if(data->chase &&
can_see_point(enemy->level,
enemy->xpos, enemy->ypos,
data->x, data->y)) {
/* If we can see the target, but not the player, return to wandering */
data->chase = false;
}
/* If the chase is on, pursue */
if (data->chase) {
move_towards(enemy,
data->x, data->y,
true);
} else {
/* No information: move randomly */
random_move_diagonals(enemy);
}
}
/**
* Free a hunter's state. As hunters share the state, there is a count
* of how many hunters are still surviving.
* @param enemy The enemy to kill/free
*/
void hunter_death(Mob * enemy) {
HunterState * data = (HunterState *) enemy->data;
if(data->refcount == 1) {
xfree(data);
} else {
data->refcount --;
}
drop_corpse(enemy);
}