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 monster *src, struct field *target)
87{
88 if(src->isplayer)
89 logstr("%s hit %s for %d damage", src->name, target->m.name, src->dmg);
90
91 if(src->dmg >= target->m.current_hp) {
92 logstr("%s killed %s", src->name ,target->m.name);
93
94 if(src->isplayer) {
95 addxp(src, target->m.level*10);
96 } else {
97 messagebox("U done did dedden", 1, 0);
98 exit(0);
99 }
100 darray_free(target->m.items);
101 target->has_monster = false;
102 } else {
103 target->m.current_hp -= src->dmg;
104 }
105}
106
107static bool additem(struct monster *m, struct item *it)
108{
109 darray_append(m->items, *it);
110 return true;
111}
112
113static void rmvitem(struct monster *m, size_t i)
114{
115 darray_remove(m->items, i);
116}
117
118bool inventorycallback(struct monster *m, size_t i)
119{
120 struct item *it = &darray_item(m->items,i);
121 if(it->type == USE) {
122 m->current_hp = MIN(m->max_hp, m->current_hp+it->u.u.restorehp);
123 rmvitem(m, i);
124 return true;
125 } else if(it->type == EQUIP) {
126 if(it->u.e.isequipped) {
127 m->max_hp -= it->u.e.hp;
128 m->current_hp = MAX(1, m->current_hp-it->u.e.hp);
129 m->dmg -= it->u.e.dmg;
130 } else {
131 m->max_hp += it->u.e.hp;
132 m->current_hp += it->u.e.hp;
133 m->dmg += it->u.e.dmg;
134 }
135
136 it->u.e.isequipped = !it->u.e.isequipped;
137 }
138 return false;
139}
140
141void inventory(struct monster *m)
142{
143 itemselect(m, inventorycallback);
144}
145
146
147//TODO: player moves on target, create new map
148struct point move_object(struct game *g, struct point src, struct point target)
149{
150 if(game_at(g, target)->u->ground.type == NOOBJ && !game_at(g, target)->has_monster) {
151 game_at(g, target)->has_monster = true;
152 game_at(g, target)->m = game_at(g, src)->m;
153 game_at(g, src)->has_monster = false;
154 return target;
155 } else if(game_at(g, target)->u->ground.type == ITEM && !game_at(g, target)->has_monster && game_at(g, src)->has_monster) {
156 if(!additem(&game_at(g, src)->m, &game_at(g, target)->u->it)) {
157 logstr("Inventory is full");
158 return src;
159 }
160 game_at(g, target)->has_monster = true;
161 game_at(g, target)->m = game_at(g, src)->m;
162 game_at(g, target)->u->ground = EMPTYFIELD;
163 game_at(g, src)->has_monster = false;
164 return target;
165 } else {
166 return src;
167 }
168}
169
170static inline struct point attack_move(struct game *g, struct point src, struct point target)
171{
172 assert(game_at(g, src)->has_monster);
173 struct field *t = game_at(g, target);
174
175 if(t->has_monster) {
176 attack(&game_at(g, src)->m, t);
177 } else {
178 return move_object(g, src, target);
179 }
180 return src;
181}
182
183void run_ai(struct game *g, struct point player)
184{
185 darray(unsigned) checked = darray_new();
186 for(size_t y = 0; y < g->ys; y++)
187 {
188 for(size_t x = 0; x < g->xs; x++)
189 {
190 pointarr res;
191 if(game_at(g, (struct point){y,x})->has_monster && !game_at(g, (struct point){y,x})->m.isplayer) {
192 unsigned *id;
193 darray_foreach(id, checked)
194 {
195 if(*id == game_at(g, (struct point){y,x})->m.id)
196 goto next;
197 }
198 darray_append(checked, game_at(g, (struct point){y,x})->m.id);
199
200 res = astar(g, (struct point){y,x}, player, AGGRO_RANGE);
201 if(res.size >= 2) {
202 attack_move(g, (struct point){y,x}, darray_item(res, res.size-2));
203 } else {
204 struct point ps[4] =
205 {
206 {y, x-1},
207 {y, x+1},
208 {y-1, x},
209 {y+1, x}
210 };
211 move_object(g, (struct point){y,x}, ps[randrange(0,3)]);
212 }
213 darray_free(res);
214
215 }
216 next:;
217 }
218 }
219 darray_free(checked);
220}
221
222
223void loop(struct game *g)
224{
225/*
226"\
227███████╗██████╗ ██╗ ██╗ ███╗ ██╗███████╗ ██████╗██╗ ██╗ █████╗ ██████╗ ██████╗ ███████╗███╗ ███╗██╗ ██╗███████╗███╗ ██╗██████╗ \n\
228██╔════╝██╔══██╗██║ ██║ ████╗ ██║██╔════╝██╔════╝██║ ██╔╝██╔══██╗██╔══██╗██╔════╝ ██╔════╝████╗ ████║██║ ██║██╔════╝████╗ ██║██╔══██╗\n\
229███████╗██████╔╝███████║ ██╔██╗ ██║█████╗ ██║ █████╔╝ ███████║██████╔╝██║ ███╗█████╗ ██╔████╔██║██║ ██║█████╗ ██╔██╗ ██║██║ ██║\n\
230╚════██║██╔══██╗██╔══██║ ██║╚██╗██║██╔══╝ ██║ ██╔═██╗ ██╔══██║██╔══██╗██║ ██║██╔══╝ ██║╚██╔╝██║██║ ██║██╔══╝ ██║╚██╗██║██║ ██║\n\
231███████║██║ ██║██║ ██║ ██║ ╚████║███████╗╚██████╗██║ ██╗██║ ██║██║ ██║╚██████╔╝███████╗██║ ╚═╝ ██║╚██████╔╝███████╗██║ ╚████║██████╔╝\n\
232"
233*/
234 game_at(g, (struct point){1,1})->m = (struct monster)
235 {
236 .symbol = 'P',
237 .id = -1,
238 .name = "Player",
239 .max_hp = 20,
240 .current_hp = 20,
241 .dmg = 2,
242 .level = 1,
243 .xp = 0,
244 .items = darray_new(),
245 .isplayer = true
246 };
247 game_at(g, (struct point){1,1})->has_monster = true;
248
249 struct point player = {1,1};
250
251 while(true)
252 {
253 charclear();
254 charprint("(づ。◕‿‿◕。)づ");
255 charprint("Name: %s", game_at(g, player)->m.name);
256 charprint("HP : %u/%u", game_at(g, player)->m.current_hp, game_at(g, player)->m.max_hp);
257 charprint("DMG: %u", game_at(g, player)->m.dmg);
258 charprint("LVL: %u", game_at(g, player)->m.level);
259 charprint("XP : %u", game_at(g, player)->m.xp);
260 draw_map(g, player);
261
262 struct choice c = get_input();
263 switch(c.a)
264 {
265 case INVALID:
266 logstr("Invalid input, try again");
267 continue;
268 case QUIT:
269 return;
270 case MOVE:;
271 struct point targetpos = relative(player, c.u.d);
272
273 if(!in_bounds(g, targetpos)) {
274 logstr("Target not in bounds");
275 continue;
276 }
277
278 player = attack_move(g, player, targetpos);
279 run_ai(g, player);
280 break;
281 case SPAWN:
282 spawn_enemy(g);
283 break;
284 case INVENTORY:
285 inventory(&game_at(g, player)->m);
286 break;
287 case REDRAW:
288 setup();
289 printlog();
290 break;
291 }
292 }
293}
294