472 lines
No EOL
18 KiB
C
472 lines
No EOL
18 KiB
C
/*
|
|
This program 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.
|
|
|
|
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <curses.h>
|
|
#include <stdlib.h>
|
|
#include <ncurses.h>
|
|
#include <menu.h>
|
|
#include <panel.h>
|
|
#include <mwchar/data.h>
|
|
|
|
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
|
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
|
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
|
|
|
|
#define mvwclrtobot(win, y, x) (wmove((win), (y), (x)) == ERR ? ERR : wclrtobot((win)))
|
|
#define NUM_FIELDS 7
|
|
void draw_output(WINDOW* win, int race, int sex, int spec, int sign, int attrs[ATTRIBUTE_COUNT], int skills[SKILL_COUNT]);
|
|
|
|
enum FIELDS {
|
|
RACE, SEX, SPEC, ATTR, SKILL_MAJ, SKILL_MIN, SIGN
|
|
};
|
|
typedef struct mw_menu {
|
|
MENU *m;
|
|
WINDOW *w;
|
|
PANEL *p;
|
|
int *selections;
|
|
int num_selections;
|
|
int max_selections;
|
|
} mw_menu;
|
|
|
|
int main() {
|
|
int ch, x, y;
|
|
WINDOW *win_input, *win_output;
|
|
PANEL *panel_input, *panel_output;
|
|
initscr();
|
|
cbreak();
|
|
noecho();
|
|
keypad(stdscr, 1);
|
|
curs_set(0);
|
|
refresh();
|
|
|
|
|
|
|
|
win_input = newwin(8, COLS, 0, 0);
|
|
box(win_input, 0, 0);
|
|
panel_input = new_panel(win_input);
|
|
win_output = newwin(LINES-8, COLS, 8, 0);
|
|
panel_output = new_panel(win_output);
|
|
|
|
int out_skills[SKILL_COUNT] = {0};
|
|
int out_attrs[ATTRIBUTE_COUNT] = {0};
|
|
|
|
int input_col_width = (COLS - 2) / 5;
|
|
int input_col_height = 6;
|
|
WINDOW *field_windows[NUM_FIELDS];
|
|
field_windows[0] = newwin(input_col_height/2, input_col_width, 1, 1 );
|
|
field_windows[1] = newwin(input_col_height/2, input_col_width, input_col_height/2+1, 1 );
|
|
field_windows[2] = newwin(input_col_height/2, input_col_width, 1, input_col_width+1);
|
|
field_windows[3] = newwin(input_col_height/2, input_col_width, input_col_height/2+1, input_col_width+1);
|
|
field_windows[4] = newwin(input_col_height, input_col_width, 1, input_col_width*2+1);
|
|
field_windows[5] = newwin(input_col_height, input_col_width, 1, input_col_width*3+1);
|
|
field_windows[6] = newwin(input_col_height, input_col_width, 1, input_col_width*4+1);
|
|
|
|
PANEL *field_panels[NUM_FIELDS];
|
|
for (int i = 0; i < NUM_FIELDS; ++i) {
|
|
field_panels[i] = new_panel(field_windows[i]);
|
|
}
|
|
const char *field_headers[] = {
|
|
"Race",
|
|
"Sex",
|
|
"Specializ.",
|
|
"Favored Attr.",
|
|
"Major Skills",
|
|
"Minor Skills",
|
|
"Birthsign"
|
|
};
|
|
const char **field_strings[] = {
|
|
MW_RACE_STR,
|
|
MW_SEX_STR,
|
|
MW_SPECIALIZATION_STR,
|
|
MW_ATTRIBUTE_STR,
|
|
MW_SKILL_STR,
|
|
MW_SKILL_STR,
|
|
MW_BIRTHSIGN_STR
|
|
};
|
|
|
|
const int field_items[] = {
|
|
RACE_COUNT,
|
|
SEX_COUNT,
|
|
SPECIALIZATION_COUNT,
|
|
ATTRIBUTE_COUNT,
|
|
SKILL_COUNT,
|
|
SKILL_COUNT,
|
|
BIRTHSIGN_COUNT
|
|
};
|
|
|
|
const int field_max_selections[] = {
|
|
1,
|
|
0,
|
|
1,
|
|
2,
|
|
5,
|
|
5,
|
|
1
|
|
};
|
|
|
|
int selected_col = 0;
|
|
int selected_row = 0;
|
|
int selected_field = 0;
|
|
WINDOW* selected_field_window;
|
|
|
|
for(int i = 0; i < 7; ++i) {
|
|
WINDOW* w = field_windows[i];
|
|
wattron(w, A_BOLD);
|
|
mvwaddstr(w, 0, 0, field_headers[i]);
|
|
wattroff(w, A_BOLD);
|
|
}
|
|
mvwchgat(field_windows[selected_field], 0, 0, -1, A_BOLD | A_REVERSE, 0, NULL);
|
|
mw_menu field_menus[NUM_FIELDS] = {0};
|
|
for (int i = 0; i < NUM_FIELDS; ++i) {
|
|
int x, y;
|
|
field_menus[i].num_selections = 0;
|
|
field_menus[i].max_selections = field_max_selections[i];
|
|
field_menus[i].selections = malloc(field_menus[i].max_selections * sizeof(*field_menus[0].selections));
|
|
for (int j = 0; j < field_menus[i].max_selections; ++j) {
|
|
field_menus[i].selections[j] = -1;
|
|
}
|
|
if (i == SEX) continue;
|
|
ITEM ** tmp_items = calloc(field_items[i] + 1, sizeof(ITEM*));
|
|
for (int j = 0; j < field_items[i]; ++j) {
|
|
tmp_items[j] = new_item(field_strings[i][j], field_strings[i][j]);
|
|
}
|
|
tmp_items[field_items[i]] = NULL;
|
|
field_menus[i].m = new_menu(tmp_items);
|
|
if(i == SKILL_MIN || i == SKILL_MAJ) {
|
|
set_menu_format(field_menus[i].m, 9, 3);
|
|
}
|
|
if(field_menus[i].max_selections > 1) {
|
|
menu_opts_off(field_menus[i].m, O_ONEVALUE);
|
|
} else {
|
|
field_menus[i].num_selections = 1;
|
|
field_menus[i].selections[0] = 0;
|
|
}
|
|
menu_opts_off(field_menus[i].m, O_SHOWDESC);
|
|
menu_opts_off(field_menus[i].m, O_ROWMAJOR);
|
|
set_menu_mark(field_menus[i].m, "*");
|
|
scale_menu(field_menus[i].m, &y, &x);
|
|
y+=2;
|
|
x+=2;
|
|
field_menus[i].w = newwin(y, x, (LINES-y)/2, (COLS-x)/2);
|
|
set_menu_sub(field_menus[i].m, derwin(field_menus[i].w, y-2, x-2, 1, 1));
|
|
post_menu(field_menus[i].m);
|
|
field_menus[i].p = new_panel(field_menus[i].w);
|
|
hide_panel(field_menus[i].p);
|
|
box(field_menus[i].w, 0, 0);
|
|
|
|
}
|
|
field_menus[RACE].selections[0] = DARK_ELF;
|
|
field_menus[SPEC].selections[0] = COMBAT;
|
|
field_menus[SIGN].selections[0] = APPRENTICE;
|
|
field_menus[SEX].selections[0] = MALE;
|
|
mvwaddstr(field_windows[RACE], 1, 0, field_strings[RACE][field_menus[RACE].selections[0]]);
|
|
mvwaddstr(field_windows[SEX], 1, 0, field_strings[SEX][field_menus[SEX].selections[0]]);
|
|
mvwaddstr(field_windows[SPEC], 1, 0, field_strings[SPEC][field_menus[SPEC].selections[0]]);
|
|
mvwaddstr(field_windows[SIGN], 1, 0, field_strings[SIGN][field_menus[SEX].selections[0]]);
|
|
|
|
draw_output(win_output, field_menus[RACE].selections[0], field_menus[SEX].selections[0], field_menus[SPEC].selections[0], field_menus[SIGN].selections[0], out_attrs, out_skills);
|
|
update_panels();
|
|
doupdate();
|
|
|
|
int in_field_menu = -1;
|
|
int selection_changed = 0;
|
|
selected_field_window = field_windows[selected_field];
|
|
mw_menu *cur_menu;
|
|
ITEM **cur_menu_items;
|
|
int *other_menu_selected;
|
|
while ((ch = getch()) != KEY_F(1)) {
|
|
if (in_field_menu == -1) {
|
|
switch(ch) {
|
|
case KEY_LEFT:
|
|
selected_col = (selected_col + 4) % 5;
|
|
selection_changed = 1;
|
|
break;
|
|
case KEY_RIGHT:
|
|
selected_col = (selected_col + 1) % 5;
|
|
selection_changed = 1;
|
|
break;
|
|
case KEY_UP:
|
|
case KEY_DOWN:
|
|
selected_row = !selected_row;
|
|
selection_changed = 1;
|
|
break;
|
|
case ' ':
|
|
if (selected_field == SEX) {
|
|
field_menus[SEX].selections[0] = !field_menus[SEX].selections[0];
|
|
mvwclrtobot(selected_field_window, 1, 0);
|
|
waddstr(selected_field_window, field_strings[SEX][field_menus[SEX].selections[0]]);
|
|
break;
|
|
}
|
|
|
|
cur_menu = &field_menus[selected_field];
|
|
other_menu_selected = NULL;
|
|
if (selected_field == SKILL_MAJ) {
|
|
other_menu_selected = field_menus[SKILL_MIN].selections;
|
|
} else if (selected_field == SKILL_MIN) {
|
|
other_menu_selected = field_menus[SKILL_MAJ].selections;
|
|
}
|
|
cur_menu_items = menu_items(cur_menu->m);
|
|
if (other_menu_selected != NULL) {
|
|
for (int i = 0; i < field_items[selected_field]; ++i) {
|
|
item_opts_on(cur_menu_items[i], O_SELECTABLE);
|
|
}
|
|
for (int i = 0; i < cur_menu->max_selections && other_menu_selected[i] != -1; ++i) {
|
|
item_opts_off(cur_menu_items[other_menu_selected[i]], O_SELECTABLE);
|
|
}
|
|
}
|
|
show_panel(cur_menu->p);
|
|
in_field_menu = selected_field;
|
|
break;
|
|
}
|
|
if (selection_changed) {
|
|
mvwchgat(selected_field_window, 0, 0, -1, A_BOLD, 0, NULL);
|
|
if (selected_col < 2) {
|
|
selected_field = selected_col * 2 + selected_row;
|
|
} else {
|
|
selected_field = selected_col + 2;
|
|
}
|
|
selected_field_window = field_windows[selected_field];
|
|
mvwchgat(selected_field_window, 0, 0, -1, A_BOLD | A_REVERSE, 0, NULL);
|
|
selection_changed = 0;
|
|
}
|
|
} else {
|
|
switch(ch) {
|
|
case KEY_UP:
|
|
menu_driver(cur_menu->m, REQ_UP_ITEM);
|
|
break;
|
|
case KEY_DOWN:
|
|
menu_driver(cur_menu->m, REQ_DOWN_ITEM);
|
|
break;
|
|
case KEY_LEFT:
|
|
menu_driver(cur_menu->m, REQ_LEFT_ITEM);
|
|
break;
|
|
case KEY_RIGHT:
|
|
menu_driver(cur_menu->m, REQ_RIGHT_ITEM);
|
|
break;
|
|
case ' ':
|
|
if(cur_menu->max_selections > 1) {
|
|
ITEM* cur_item = current_item(cur_menu->m);
|
|
if (item_opts(cur_item) & O_SELECTABLE) {
|
|
menu_driver(cur_menu->m, REQ_TOGGLE_ITEM);
|
|
if (item_value(cur_item)) {
|
|
if(cur_menu->num_selections == cur_menu->max_selections) {
|
|
menu_driver(cur_menu->m, REQ_TOGGLE_ITEM);
|
|
} else {
|
|
cur_menu->num_selections += 1;
|
|
}
|
|
} else {
|
|
cur_menu->num_selections -= 1;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case '\n':
|
|
if (cur_menu->max_selections > 1) {
|
|
for (int i = 0, j = 0; i < field_items[selected_field]; ++i){
|
|
if(item_value(cur_menu_items[i])) {
|
|
cur_menu->selections[j++] = i;
|
|
}
|
|
}
|
|
for (int j = cur_menu->num_selections; j < cur_menu->max_selections; ++j) {
|
|
cur_menu->selections[j] = -1;
|
|
}
|
|
} else {
|
|
cur_menu->selections[0] = item_index(current_item(cur_menu->m));
|
|
}
|
|
mvwclrtobot(selected_field_window, 1, 0);
|
|
|
|
for (int i = 0; i < cur_menu->num_selections; ++i) {
|
|
mvwaddstr(selected_field_window, i+1, 0, field_strings[selected_field][cur_menu->selections[i]]);
|
|
}
|
|
|
|
for (int i = 0; i < SKILL_COUNT; ++i) {
|
|
out_skills[i] = 0;
|
|
}
|
|
for (int i = 0; i < field_menus[SKILL_MIN].num_selections; ++i) {
|
|
out_skills[field_menus[SKILL_MIN].selections[i]] = 1;
|
|
}
|
|
for (int i = 0; i < field_menus[SKILL_MAJ].num_selections; ++i) {
|
|
out_skills[field_menus[SKILL_MAJ].selections[i]] = 2;
|
|
}
|
|
for (int i = 0; i < ATTRIBUTE_COUNT; ++i) {
|
|
out_attrs[i] = 0;
|
|
}
|
|
for (int i = 0; i < field_menus[ATTR].num_selections; ++i) {
|
|
out_attrs[field_menus[ATTR].selections[i]] = 1;
|
|
}
|
|
draw_output(win_output, field_menus[RACE].selections[0], field_menus[SEX].selections[0], field_menus[SPEC].selections[0], field_menus[SIGN].selections[0], out_attrs, out_skills);
|
|
hide_panel(cur_menu->p);
|
|
in_field_menu = -1;
|
|
break;
|
|
}
|
|
}
|
|
update_panels();
|
|
doupdate();
|
|
}
|
|
endwin();
|
|
return 0;
|
|
}
|
|
|
|
void draw_output(WINDOW* win, int race, int sex, int spec, int sign, int attrs[ATTRIBUTE_COUNT], int skills[SKILL_COUNT]) {
|
|
int y, x;
|
|
getmaxyx(win, y, x);
|
|
int col_width = x / 5;
|
|
int max_row = 15;
|
|
|
|
int final_skills[SKILL_COUNT];
|
|
for(int i = 0; i < SKILL_COUNT; ++i) {
|
|
int n = 5;
|
|
if (skills[i] == 1) n += 10;
|
|
if (skills[i] == 2) n += 25;
|
|
if (i / 9 == spec) n += 5;
|
|
n += MW_RACE_SKILLS[race][i];
|
|
final_skills[i] = n;
|
|
}
|
|
|
|
int final_attrs[ATTRIBUTE_COUNT];
|
|
for(int i = 0; i < ATTRIBUTE_COUNT; ++i) {
|
|
int n = 40;
|
|
n += MW_RACE_ATTRS[race][sex][i];
|
|
if (attrs[i]) n += 10;
|
|
final_attrs[i] = n;
|
|
}
|
|
|
|
werase(win);
|
|
wattron(win, A_BOLD);
|
|
mvwaddstr(win, 0, 0, "Attributes");
|
|
mvwaddstr(win, 0, col_width, "Major Skills");
|
|
mvwaddstr(win, 7, col_width, "Minor Skills");
|
|
mvwaddstr(win, 0, col_width * 2, "Misc Skills");
|
|
wattroff(win, A_BOLD);
|
|
|
|
if (sign == STEED) {
|
|
final_attrs[SPEED] += 25;
|
|
} else if (sign == LOVER) {
|
|
final_attrs[AGILITY] += 25;
|
|
}
|
|
int final_health = (final_attrs[STRENGTH]+final_attrs[ENDURANCE])/2;
|
|
int final_magicka = final_attrs[INTELLIGENCE]
|
|
* (1 + MW_RACE_MAGICKA_BONUS[race] + MW_BIRTHSIGN_MAGICKA_BONUS[sign]);
|
|
int final_fatigue = final_attrs[STRENGTH]
|
|
+ final_attrs[WILLPOWER]
|
|
+ final_attrs[AGILITY]
|
|
+ final_attrs[ENDURANCE];
|
|
|
|
if (sign == LADY) {
|
|
final_attrs[PERSONALITY] += 25;
|
|
final_attrs[ENDURANCE] += 25;
|
|
}
|
|
int final_abilities_count = 0, final_powers_count = 0, final_spells_count = 0;
|
|
int final_abilities[ABILITY_COUNT] = {0};
|
|
for (int i = 0; i < ABILITY_COUNT; ++i) {
|
|
final_abilities[i] = MW_RACE_ABILITIES[race][i] || MW_BIRTHSIGN_ABILITIES[sign][i];
|
|
if (final_abilities[i]) final_abilities_count++;
|
|
}
|
|
int final_powers[POWER_COUNT] = {0};
|
|
for (int i = 0; i < POWER_COUNT; ++i) {
|
|
final_powers[i] = MW_RACE_POWERS[race][i] || MW_BIRTHSIGN_POWERS[sign][i];
|
|
if (final_powers[i]) final_powers_count++;
|
|
}
|
|
int final_spells[SPELL_COUNT] = {0};
|
|
for (int i = 0; i < SPELL_COUNT; ++i) {
|
|
final_spells[i] = MW_RACE_SPELLS[race][i] || MW_BIRTHSIGN_SPELLS[sign][i];
|
|
}
|
|
for (int i = 0; i < STARTING_SPELL_COUNT; ++i) {
|
|
if(skills[MW_STARTING_SPELL[i][1]] == 0) continue;
|
|
int sum = final_skills[MW_STARTING_SPELL[i][1]]*2 + final_attrs[WILLPOWER]/5 + final_attrs[LUCK]/10;
|
|
if(sum >= MW_STARTING_SPELL[i][2]) final_spells[MW_STARTING_SPELL[i][0]] = 1;
|
|
}
|
|
for (int i = 0; i < SPELL_COUNT; ++i) {
|
|
if (final_spells[i]) final_spells_count++;
|
|
}
|
|
|
|
|
|
mvwprintw(win, 1, 0, "%-11s%4d", "Health", final_health);
|
|
mvwprintw(win, 2, 0, "%-11s%4d", "Magicka", final_magicka);
|
|
mvwprintw(win, 3, 0, "%-11s%4d", "Fatigue", final_fatigue);
|
|
|
|
|
|
for (int i = 0; i < ATTRIBUTE_COUNT; ++i) {
|
|
mvwprintw(win, i + 5, 0, "%-12s%3d", MW_ATTRIBUTE_STR[i], final_attrs[i]);
|
|
}
|
|
|
|
int dyn_row = 15;
|
|
if (final_powers_count != 0) {
|
|
for (int i = POWER_COUNT-1; i >= 0; --i) {
|
|
if (final_powers[i]) {
|
|
mvwaddstr(win, dyn_row, col_width*3, MW_POWER_STR[i]);
|
|
dyn_row -= 1;
|
|
}
|
|
}
|
|
wattron(win, A_BOLD);
|
|
mvwaddstr(win, dyn_row, col_width*3, "Powers");
|
|
wattroff(win, A_BOLD);
|
|
dyn_row -= 2;
|
|
}
|
|
if (final_abilities_count != 0) {
|
|
for (int i = ABILITY_COUNT-1; i >= 0; --i) {
|
|
if (final_abilities[i]) {
|
|
mvwaddstr(win, dyn_row, col_width*3, MW_ABILITY_STR[i]);
|
|
dyn_row -= 1;
|
|
}
|
|
}
|
|
wattron(win, A_BOLD);
|
|
mvwaddstr(win, dyn_row, col_width*3, "Abilities");
|
|
wattroff(win, A_BOLD);
|
|
dyn_row -= 2;
|
|
}
|
|
|
|
int spell_row = max_row;
|
|
if (final_spells_count != 0) {
|
|
for (int i = SPELL_COUNT-1; i >= 0; --i) {
|
|
if (final_spells[i]) {
|
|
mvwaddstr(win, spell_row, col_width*4, MW_SPELL_STR[i]);
|
|
spell_row -= 1;
|
|
}
|
|
}
|
|
wattron(win, A_BOLD);
|
|
mvwaddstr(win, spell_row, col_width*4, "Spells");
|
|
wattroff(win, A_BOLD);
|
|
spell_row -= 2;
|
|
}
|
|
int skill_min_i = 0;
|
|
int skill_maj_i = 0;
|
|
int skill_msc_i = 0;
|
|
int skill_msc_overflow = 0;
|
|
for (int i = 0; i < SKILL_COUNT; ++i) {
|
|
int row;
|
|
int col;
|
|
if (skills[i] == 2) {
|
|
row = ++skill_maj_i;
|
|
col = col_width;
|
|
} else if (skills[i] == 1) {
|
|
row = ++skill_min_i + 7;
|
|
col = col_width;
|
|
} else if (!skill_msc_overflow) {
|
|
int row_mod = max_row;
|
|
row = (skill_msc_i % row_mod) + 1;
|
|
col = col_width * (skill_msc_i / row_mod + 2);
|
|
if (col >= col_width * 3 && row >= dyn_row) {
|
|
skill_msc_overflow = 1;
|
|
continue;
|
|
}
|
|
skill_msc_i += 1;
|
|
}
|
|
|
|
mvwprintw(win, row, col, "%-12s%3d", MW_SKILL_STR[i], final_skills[i]);
|
|
}
|
|
|
|
|
|
} |