#define _POSIX_C_SOURCE 200809L #define _XOPEN_SOURCE_EXTENDED #include #include #include #include #include #include #include #include #include #include #include #include #define NOOBJ_OBJECT (struct object){.type = NOOBJ, .symbol = ' '} unsigned m_id = 0; struct choice get_input(void) { struct choice c; start:; int res = getch(); assert(res != ERR); switch(res) { case 'w': c.a = MOVE; c.u.d = UP; break; case 'a': c.a = MOVE; c.u.d = LEFT; break; case 's': c.a = MOVE; c.u.d = DOWN; break; case 'd': c.a = MOVE; c.u.d = RIGHT; break; case 'f': c.a = SPAWN; break; case 'q': c.a = QUIT; break; case 'i': c.a = INVENTORY; break; case KEY_RESIZE: c.a = REDRAW; break; case KEY_UP: log_scroll(UP); goto start; case KEY_DOWN: log_scroll(DOWN); goto start; default: c.a = INVALID; } return c; } static void addxp(struct monster *m, unsigned xp) { m->xp += xp; if(m->xp >= m->level *100) { m->xp -= m->level*100; m->level++; m->dmg++; m->max_hp += 10; m->current_hp += 10; const char *str = "!You leveled up!"; messagebox(str, 1, strlen(str)); } } static void attack(struct monster *src, struct field *target) { if(src->isplayer) logstr("%s hit %s for %d damage", src->name, target->m.name, src->dmg); if(src->dmg >= target->m.current_hp) { logstr("%s killed %s", src->name ,target->m.name); if(src->isplayer) { addxp(src, target->m.level*10); } else { messagebox("U done did dedden", 1, 0); exit(0); } darray_free(target->m.items); target->has_monster = false; } else { target->m.current_hp -= src->dmg; } } static bool additem(struct monster *m, struct item *it) { darray_append(m->items, *it); return true; } static void rmvitem(struct monster *m, size_t i) { darray_remove(m->items, i); } bool inventorycallback(struct monster *m, size_t i) { struct item *it = &darray_item(m->items,i); if(it->type == USE) { m->current_hp = MIN(m->max_hp, m->current_hp+it->u.u.restorehp); rmvitem(m, i); return true; } else if(it->type == EQUIP) { if(it->u.e.isequipped) { m->max_hp -= it->u.e.hp; m->current_hp = MAX(1, m->current_hp-it->u.e.hp); m->dmg -= it->u.e.dmg; } else { m->max_hp += it->u.e.hp; m->current_hp += it->u.e.hp; m->dmg += it->u.e.dmg; } it->u.e.isequipped = !it->u.e.isequipped; } return false; } void inventory(struct monster *m) { itemselect(m, inventorycallback); } //TODO: player moves on target, create new map struct point move_object(struct game *g, struct point src, struct point target) { if(game_at(g, target)->u->ground.type == NOOBJ && !game_at(g, target)->has_monster) { game_at(g, target)->has_monster = true; game_at(g, target)->m = game_at(g, src)->m; game_at(g, src)->has_monster = false; return target; } else if(game_at(g, target)->u->ground.type == ITEM && !game_at(g, target)->has_monster && game_at(g, src)->has_monster) { if(!additem(&game_at(g, src)->m, &game_at(g, target)->u->it)) { logstr("Inventory is full"); return src; } game_at(g, target)->has_monster = true; game_at(g, target)->m = game_at(g, src)->m; game_at(g, target)->u->ground = EMPTYFIELD; game_at(g, src)->has_monster = false; return target; } else { return src; } } static inline struct point attack_move(struct game *g, struct point src, struct point target) { assert(game_at(g, src)->has_monster); struct field *t = game_at(g, target); if(t->has_monster) { attack(&game_at(g, src)->m, t); } else { return move_object(g, src, target); } return src; } void run_ai(struct game *g, struct point player) { darray(unsigned) checked = darray_new(); for(size_t y = 0; y < g->ys; y++) { for(size_t x = 0; x < g->xs; x++) { pointarr res; if(game_at(g, (struct point){y,x})->has_monster && !game_at(g, (struct point){y,x})->m.isplayer) { unsigned *id; darray_foreach(id, checked) { if(*id == game_at(g, (struct point){y,x})->m.id) goto next; } darray_append(checked, game_at(g, (struct point){y,x})->m.id); res = astar(g, (struct point){y,x}, player, AGGRO_RANGE); if(res.size >= 2) { attack_move(g, (struct point){y,x}, darray_item(res, res.size-2)); } else { struct point ps[4] = { {y, x-1}, {y, x+1}, {y-1, x}, {y+1, x} }; move_object(g, (struct point){y,x}, ps[randrange(0,3)]); } darray_free(res); } next:; } } darray_free(checked); } void loop(struct game *g) { /* "\ ███████╗██████╗ ██╗ ██╗ ███╗ ██╗███████╗ ██████╗██╗ ██╗ █████╗ ██████╗ ██████╗ ███████╗███╗ ███╗██╗ ██╗███████╗███╗ ██╗██████╗ \n\ ██╔════╝██╔══██╗██║ ██║ ████╗ ██║██╔════╝██╔════╝██║ ██╔╝██╔══██╗██╔══██╗██╔════╝ ██╔════╝████╗ ████║██║ ██║██╔════╝████╗ ██║██╔══██╗\n\ ███████╗██████╔╝███████║ ██╔██╗ ██║█████╗ ██║ █████╔╝ ███████║██████╔╝██║ ███╗█████╗ ██╔████╔██║██║ ██║█████╗ ██╔██╗ ██║██║ ██║\n\ ╚════██║██╔══██╗██╔══██║ ██║╚██╗██║██╔══╝ ██║ ██╔═██╗ ██╔══██║██╔══██╗██║ ██║██╔══╝ ██║╚██╔╝██║██║ ██║██╔══╝ ██║╚██╗██║██║ ██║\n\ ███████║██║ ██║██║ ██║ ██║ ╚████║███████╗╚██████╗██║ ██╗██║ ██║██║ ██║╚██████╔╝███████╗██║ ╚═╝ ██║╚██████╔╝███████╗██║ ╚████║██████╔╝\n\ " */ game_at(g, (struct point){1,1})->m = (struct monster) { .symbol = 'P', .id = -1, .name = "Player", .max_hp = 20, .current_hp = 20, .dmg = 2, .level = 1, .xp = 0, .items = darray_new(), .isplayer = true }; game_at(g, (struct point){1,1})->has_monster = true; struct point player = {1,1}; while(true) { charclear(); charprint("(づ。◕‿‿◕。)づ"); charprint("Name: %s", game_at(g, player)->m.name); charprint("HP : %u/%u", game_at(g, player)->m.current_hp, game_at(g, player)->m.max_hp); charprint("DMG: %u", game_at(g, player)->m.dmg); charprint("LVL: %u", game_at(g, player)->m.level); charprint("XP : %u", game_at(g, player)->m.xp); draw_map(g, player); struct choice c = get_input(); switch(c.a) { case INVALID: logstr("Invalid input, try again"); continue; case QUIT: return; case MOVE:; struct point targetpos = relative(player, c.u.d); if(!in_bounds(g, targetpos)) { logstr("Target not in bounds"); continue; } player = attack_move(g, player, targetpos); run_ai(g, player); break; case SPAWN: spawn_enemy(g); break; case INVENTORY: inventory(&game_at(g, player)->m); break; case REDRAW: setup(); printlog(); break; } } }