loop.c
Raw
1#define _POSIX_C_SOURCE 200809L
2#define _XOPEN_SOURCE_EXTENDED
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#include <assert.h>
7#include <stdbool.h>
8#include <unistd.h>
9
10#include <ncurses.h>
11
12#include <game.h>
13#include <helper.h>
14#include <tui.h>
15#include <algorithm.h>
16#include <config.h>
17
18#define NOOBJ_OBJECT (struct object){.type = NOOBJ, .symbol = ' '}
19
20unsigned m_id = 0;
21
22struct choice get_input(void)
23{
24 struct choice c;
25
26start:;
27 int res = getch();
28 assert(res != ERR);
29 switch(res)
30 {
31 case 'w':
32 c.a = MOVE;
33 c.u.d = UP;
34 break;
35 case 'a':
36 c.a = MOVE;
37 c.u.d = LEFT;
38 break;
39 case 's':
40 c.a = MOVE;
41 c.u.d = DOWN;
42 break;
43 case 'd':
44 c.a = MOVE;
45 c.u.d = RIGHT;
46 break;
47 case 'f':
48 c.a = SPAWN;
49 break;
50 case 'q':
51 c.a = QUIT;
52 break;
53 case 'i':
54 c.a = INVENTORY;
55 break;
56 case KEY_RESIZE:
57 c.a = REDRAW;
58 break;
59 case KEY_UP:
60 log_scroll(UP);
61 goto start;
62 case KEY_DOWN:
63 log_scroll(DOWN);
64 goto start;
65 default:
66 c.a = INVALID;
67 }
68
69 return c;
70}
71
72static void addxp(struct monster *m, unsigned xp)
73{
74 m->xp += xp;
75 if(m->xp >= m->level *100) {
76 m->xp -= m->level*100;
77 m->level++;
78 m->dmg++;
79 m->max_hp += 10;
80 m->current_hp += 10;
81 const char *str = "!You leveled up!";
82 messagebox(str, 1, strlen(str));
83 }
84}
85
86static void attack(struct object *src, struct object *target)
87{
88 assert(src->type == MONSTER);
89
90 if(target->type == MONSTER) {
91 if(src->u.m.isplayer)
92 logstr("%s hit %s for %d damage", src->u.m.name, target->u.m.name, src->u.m.dmg);
93
94 if(src->u.m.dmg >= target->u.m.current_hp) {
95 logstr("%s killed %s", src->u.m.name ,target->u.m.name);
96
97 if(src->u.m.isplayer) {
98 addxp(&src->u.m, target->u.m.level*10);
99 } else {
100 messagebox("U done did dedden", 1, 0);
101 exit(0);
102 }
103 darray_free(target->u.m.items);
104 *target = NOOBJ_OBJECT;
105 } else {
106 target->u.m.current_hp -= src->u.m.dmg;
107 }
108 } else {
109 logstr("Target is not a monster or player");
110 }
111}
112
113static bool additem(struct monster *m, struct item *it)
114{
115 darray_append(m->items, *it);
116 return true;
117}
118
119static void rmvitem(struct monster *m, size_t i)
120{
121 darray_remove(m->items, i);
122}
123
124bool inventorycallback(struct monster *m, size_t i)
125{
126 struct item *it = &darray_item(m->items,i);
127 if(it->type == USE) {
128 m->current_hp = MIN(m->max_hp, m->current_hp+it->u.u.restorehp);
129 rmvitem(m, i);
130 return true;
131 } else if(it->type == EQUIP) {
132 if(it->u.e.isequipped) {
133 m->max_hp -= it->u.e.hp;
134 m->current_hp = MAX(1, m->current_hp-it->u.e.hp);
135 m->dmg -= it->u.e.dmg;
136 } else {
137 m->max_hp += it->u.e.hp;
138 m->current_hp += it->u.e.hp;
139 m->dmg += it->u.e.dmg;
140 }
141
142 it->u.e.isequipped = !it->u.e.isequipped;
143 }
144 return false;
145}
146
147void inventory(struct monster *m)
148{
149 itemselect(m, inventorycallback);
150}
151
152
153//TODO: player moves on target, create new map
154struct point move_object(struct game *g, struct point src, struct point target)
155{
156 if(game_at(g, target)->type == NOOBJ) {
157 *game_at(g, target) = *game_at(g, src);
158 *game_at(g, src) = NOOBJ_OBJECT;
159 return target;
160 } else if(game_at(g, target)->type == ITEM && game_at(g, src)->type == MONSTER) {
161 if(!additem(&game_at(g, src)->u.m, &game_at(g, target)->u.i)) {
162 logstr("Inventory is full");
163 return src;
164 }
165 *game_at(g, target) = *game_at(g, src);
166 *game_at(g, src) = NOOBJ_OBJECT;
167 return target;
168 } else {
169 return src;
170 }
171}
172
173static inline struct point attack_move(struct game *g, struct point src, struct point target)
174{
175 struct object *t = game_at(g, target);
176
177 if(t->type == MONSTER) {
178 attack(game_at(g, src), t);
179 } else {
180 return move_object(g, src, target);
181 }
182 return src;
183}
184
185void run_ai(struct game *g, struct point player)
186{
187 darray(unsigned) checked = darray_new();
188 for(size_t y = 0; y < g->ys; y++)
189 {
190 for(size_t x = 0; x < g->xs; x++)
191 {
192 pointarr res;
193 if(game_at(g, (struct point){y,x})->type == MONSTER && !game_at(g, (struct point){y,x})->u.m.isplayer) {
194 unsigned *id;
195 darray_foreach(id, checked)
196 {
197 if(*id == game_at(g, (struct point){y,x})->u.m.id)
198 goto next;
199 }
200 darray_append(checked, game_at(g, (struct point){y,x})->u.m.id);
201
202 res = astar(g, (struct point){y,x}, player, AGGRO_RANGE);
203 if(res.size >= 2) {
204 attack_move(g, (struct point){y,x}, darray_item(res, res.size-2));
205 } else {
206 struct point ps[4] =
207 {
208 {y, x-1},
209 {y, x+1},
210 {y-1, x},
211 {y+1, x}
212 };
213 move_object(g, (struct point){y,x}, ps[randrange(0,3)]);
214 }
215 darray_free(res);
216
217 }
218 next:;
219 }
220 }
221 darray_free(checked);
222}
223
224
225void loop(struct game *g)
226{
227/*
228"\
229███████╗██████╗ ██╗ ██╗ ███╗ ██╗███████╗ ██████╗██╗ ██╗ █████╗ ██████╗ ██████╗ ███████╗███╗ ███╗██╗ ██╗███████╗███╗ ██╗██████╗ \n\
230██╔════╝██╔══██╗██║ ██║ ████╗ ██║██╔════╝██╔════╝██║ ██╔╝██╔══██╗██╔══██╗██╔════╝ ██╔════╝████╗ ████║██║ ██║██╔════╝████╗ ██║██╔══██╗\n\
231███████╗██████╔╝███████║ ██╔██╗ ██║█████╗ ██║ █████╔╝ ███████║██████╔╝██║ ███╗█████╗ ██╔████╔██║██║ ██║█████╗ ██╔██╗ ██║██║ ██║\n\
232╚════██║██╔══██╗██╔══██║ ██║╚██╗██║██╔══╝ ██║ ██╔═██╗ ██╔══██║██╔══██╗██║ ██║██╔══╝ ██║╚██╔╝██║██║ ██║██╔══╝ ██║╚██╗██║██║ ██║\n\
233███████║██║ ██║██║ ██║ ██║ ╚████║███████╗╚██████╗██║ ██╗██║ ██║██║ ██║╚██████╔╝███████╗██║ ╚═╝ ██║╚██████╔╝███████╗██║ ╚████║██████╔╝\n\
234"
235*/
236 *game_at(g, (struct point){1,1}) = (struct object)
237 {
238 .type = MONSTER,
239 .symbol = 'P',
240 .u.m =
241 {
242 .id = -1,
243 .name = "Player",
244 .max_hp = 20,
245 .current_hp = 20,
246 .dmg = 2,
247 .level = 1,
248 .xp = 0,
249 .items = darray_new(),
250 .isplayer = true
251 }
252 };
253
254 struct point player = {1,1};
255
256 while(true)
257 {
258 charclear();
259 charprint("(づ。◕‿‿◕。)づ");
260 charprint("Name: %s", game_at(g, player)->u.m.name);
261 charprint("HP : %u/%u", game_at(g, player)->u.m.current_hp, game_at(g, player)->u.m.max_hp);
262 charprint("DMG: %u", game_at(g, player)->u.m.dmg);
263 charprint("LVL: %u", game_at(g, player)->u.m.level);
264 charprint("XP : %u", game_at(g, player)->u.m.xp);
265 draw_map(g, player);
266
267 struct choice c = get_input();
268 switch(c.a)
269 {
270 case INVALID:
271 logstr("Invalid input, try again");
272 continue;
273 case QUIT:
274 return;
275 case MOVE:;
276 struct point targetpos = relative(player, c.u.d);
277
278 if(!in_bounds(g, targetpos)) {
279 logstr("Target not in bounds");
280 continue;
281 }
282
283 player = attack_move(g, player, targetpos);
284 run_ai(g, player);
285 break;
286 case SPAWN:
287 spawn_enemy(g);
288 break;
289 case INVENTORY:
290 inventory(&game_at(g, player)->u.m);
291 break;
292 case REDRAW:
293 setup();
294 printlog();
295 break;
296 }
297 }
298}
299