/*
* $Id: chargen.c,v 1.1.1.1 2005/01/11 21:18:04 kstevens Exp $
*
* Author: Markus Stenberg <fingon@iki.fi>
*
* Copyright (c) 1996 Markus Stenberg
* All rights reserved
*
* Created: Wed Sep 18 00:35:56 1996 fingon
* Last modified: Sat Jun 6 19:59:03 1998 fingon
*
*/
/*
Basic/Advanced Academy
-//- University
( -//- Intelligence College)
Basic Academy 9
3@1, 3@2
Advanced Academy 15
2@1, 2@2, 2@3
Aerospace,Infantry,Specialist(Recon/Tech),Battlemech,Cavalry
Basic University 12
4@1,4@2
Advanced university 18
3@1,3@2,2@3
same as academy + Military Science + Leadership (2 careers)
None 0
*/
/* Alternative, menu-based chargen */
/* Due to peculiar nature of this file, it's not compiled but included
at end of btechstats.c */
#include "coolmenu.h"
#define BEGINSTARTS "Type 'begin' to start!"
#define BEGIN_PRI_POINTS 8
enum {
NOTBEGUN, PRI_PICK, ADV_PICK, ATT_PICK, PACK_PICK, PACKSET_PICK,
PACKSKI_PICK, SKI_PICK, DONE
};
enum {
MENU_PRI, MENU_ADV, MENU_ATT, MENU_PACK, MENU_PACKSET, MENU_PACKSKI,
NUM_MENUS
};
enum {
ATHLETIC, MENTAL, PHYSICAL, SOCIAL, NUM_SKIMENUS
};
enum {
ADVANTAGE, ATTRIBUTE, SKILL, NUM_PRIORITIES
};
enum {
BASIC_ACADEMY, ADV_ACADEMY, BASIC_UNIV, ADV_UNIV, PACKAGE_NONE,
NUM_PACKAGES
};
enum {
CAVALRY, BMECH, AERO, ARTILLERY, DROPSHIP, TECHMECH, TECHVEH,
NUM_CAREERS
};
int das_career_magic[NUM_PACKAGES][3] = {
{3, 3, 0},
{2, 2, 2},
{4, 4, 0},
{3, 3, 2},
{0, 0, 0}
};
int das_career_costs[NUM_PACKAGES] = { 9, 15, 12, 18, 0 };
char *das_menus[NUM_PRIORITIES + 1] =
{ "Advantages", "Attributes", "Skills", NULL };
char *das_packages[NUM_PACKAGES + 1] =
{ "Basic Academy", "Advanced Academy",
"Basic University", "Advanced University", "No package", NULL
};
char *das_careers[NUM_CAREERS + 1] =
{ "Cavalry", "Battlemech", "Aerospace", "Artillery", "DropShip",
"Mech Technician",
"Vehicle Technician", NULL
};
#define EA_NUMBER char_getvaluecode("Exceptional_Attribute")
#define INT_NUMBER char_getvaluecode("Intuition")
#define LEARN_NUMBER char_getvaluecode("Learn")
#define FIRST_ATT char_getvaluecode("Build")
#define LAST_ATT char_getvaluecode("Charisma")
#define SKIBTH(s,a) a - st->skills[s]
#define SKIBASEBTH(a,b) \
18 - st->attributes[a] - st->attributes[b]
#define ATHBASEBTH SKIBASEBTH(FIRST_ATT,FIRST_ATT+1)
#define MENBASEBTH SKIBASEBTH(FIRST_ATT+2,FIRST_ATT+3)
#define PHYBASEBTH SKIBASEBTH(FIRST_ATT+1,FIRST_ATT+2)
#define SOCBASEBTH SKIBASEBTH(FIRST_ATT+2,FIRST_ATT+4)
#define ATHBTH(s) SKIBTH(s, ATHBASEBTH)
#define MENBTH(s) SKIBTH(s, MENBASEBTH)
#define PHYBTH(s) SKIBTH(s, PHYBASEBTH)
#define SOCBTH(s) SKIBTH(s, SOCBASEBTH)
#define SKILLBTH(s) \
(char_values[s].flag & CHAR_ATHLETIC) ? ATHBTH(s) : \
(char_values[s].flag & CHAR_MENTAL) ? MENBTH(s) : \
(char_values[s].flag & CHAR_PHYSICAL) ? PHYBTH(s) : SOCBTH(s)
struct chargen_struct {
dbref player;
/*[Begin]/Priority/Adv/Att/Pack/PackSet/PackSki/(Skills)/[Done] */
int state;
int skill_substate;
int applied;
coolmenu *cm[NUM_MENUS];
coolmenu *sm[NUM_SKIMENUS];
int pritotal;
int pribought;
int advbought;
int advtotal;
int attbought;
int atttotal;
int eacount;
int skibought;
int skitotal;
int chosen_packagetype; /* Number, 1-5 or 0 if not yet set */
int chosen_packages; /* Basically, a bit vector */
int packagesbought;
int packages[NUM_PACKAGES];
int careersbought;
int careerstotal;
int careers[NUM_CAREERS];
int prioritys[NUM_PRIORITIES];
int advantages[NUM_CHARVALUES];
int attributes[NUM_CHARVALUES];
int pskills[NUM_CHARVALUES]; /* Skills from packages */
int skills[NUM_CHARVALUES];
int career_or_package_changed;
struct chargen_struct *next;
};
struct chargen_struct *chargen_list = NULL;
#define State (st=retrieve_chargen_struct(player))->state
#define Applied st->applied
struct chargen_struct *retrieve_chargen_struct(dbref player)
{
struct chargen_struct *foo;
for (foo = chargen_list; foo; foo = foo->next)
if (foo->player == player)
return foo;
Create(foo, struct chargen_struct, 1);
foo->player = player;
ADD_TO_LIST_HEAD(chargen_list, next, foo);
return foo;
}
#include "chargen_menus.h"
static coolmenu *find_proper_menu(struct chargen_struct *c)
{
switch (c->state) {
case PRI_PICK:
return c->cm[MENU_PRI];
case ADV_PICK:
return c->cm[MENU_ADV];
case ATT_PICK:
return c->cm[MENU_ATT];
case PACK_PICK:
return c->cm[MENU_PACK];
case PACKSET_PICK:
return c->cm[MENU_PACKSET];
case PACKSKI_PICK:
return c->cm[MENU_PACKSKI];
case SKI_PICK:
return c->sm[c->skill_substate];
}
return NULL;
}
int recursive_add(int lev)
{
int i, t = 0;
for (i = 1; i <= lev; i++)
t += i;
return t;
}
static void update_status(struct chargen_struct *st, coolmenu * c,
coolmenu * e, int now, int max)
{
char buf[MBUF_SIZE];
int bt = 0;
strcpy(buf, "");
if (st->state == SKI_PICK) {
switch (st->skill_substate) {
case ATHLETIC:
bt = ATHBASEBTH;
break;
case MENTAL:
bt = MENBASEBTH;
break;
case PHYSICAL:
bt = PHYBASEBTH;
break;
case SOCIAL:
bt = SOCBASEBTH;
break;
}
if (bt)
sprintf(buf, "BTH base: %d+ ", bt);
} else if (e && (st->state == PACK_PICK || st->state == PACKSET_PICK))
st->career_or_package_changed = 1;
for (; c; c = c->next)
if (c->id == -1) {
if (c->text)
free((void *) c->text);
if (now > max)
c->text =
strdup(tprintf("%s%%cr%d out of %d used - drop some?",
buf, now, max));
else if (now < max)
c->text =
strdup(tprintf("%s%%cc%d out of %d used (%d free)",
buf, now, max, max - now));
else if (now == max)
c->text =
strdup(tprintf
("%s%%cgAll selected. Type 'next' to advance to next stage.",
buf));
}
}
static int pack_skills_status(struct chargen_struct *st, coolmenu * c)
{
char buf[MBUF_SIZE];
int used[3];
int i, x, y, can_go = 1;
for (i = 0; i < 3; i++)
used[i] = 0;
for (i = 0; i < NUM_CHARVALUES; i++)
if (st->pskills[i])
used[st->pskills[i] - 1]++;
strcpy(buf, "");
for (i = 0; i < 3; i++) {
if ((x = used[i]) != (y =
das_career_magic[st->chosen_packagetype][i])) {
if (c) {
if (x < y)
sprintf(buf + strlen(buf), "%s%d level %d skills left",
buf[0] ? ", " : "", (y - x), i + 1);
else
sprintf(buf + strlen(buf),
"%sremove %d level %d skills", buf[0] ? ", " : "",
(y - x), i + 1);
}
can_go = 0;
}
}
if (can_go && c)
sprintf(buf,
"%%cgAll selected. Type 'next' to advance to next stage.");
for (; c; c = c->next)
if (c->id == -1) {
if (c->text)
free((void *) c->text);
c->text = strdup(buf);
}
return can_go;
}
int can_proceed(dbref player, struct chargen_struct *st)
{
int x = 0, y = 0, i, j;
switch (State) {
case PRI_PICK:
x = st->pribought;
y = st->pritotal;
#define GENNOT(name) \
if (x < y) \
{ \
notify(player, tprintf("You haven't used all your %s yet!", name)); \
return -1; \
} \
else \
if (x > y) \
{ \
notify(player, tprintf("Whoa! Free some %s first.", name)); \
return -1; \
}
GENNOT("priority points");
return 1;
case ADV_PICK:
x = st->advbought;
y = st->advtotal;
GENNOT("advantages");
return 1;
case ATT_PICK:
x = st->attbought;
y = st->atttotal;
GENNOT("attribute points");
for (i = FIRST_ATT; i <= LAST_ATT; i++)
if (st->attributes[i] < 3) {
notify(player, "No attributes below 3, please!");
return -1;
}
if (st->eacount != st->advantages[EA_NUMBER]) {
notify(player, "Invalid number of exceptional attributes!");
return -1;
}
return 1;
case PACK_PICK:
/* Pack picking's tricky - you can always pick just one */
x = st->packagesbought;
y = 1;
GENNOT("package(s)");
for (i = 0; i < NUM_PACKAGES; i++)
if (st->packages[i]) {
if (das_career_costs[i] > st->skitotal) {
notify(player,
"You can't afford it! Get more skill points!");
return -1;
}
st->chosen_packagetype = i;
}
return 1;
break;
case PACKSET_PICK:
x = st->careersbought;
y = (st->chosen_packagetype >= BASIC_UNIV) ? 2 : 1;
GENNOT("career(s)");
j = 0;
for (i = 0; i < NUM_CAREERS; i++)
if (st->careers[i])
j += 16 * (1 << i);
st->chosen_packages = j;
return 1;
break;
case PACKSKI_PICK:
/* This has a special status (fear) */
if (!pack_skills_status(st, NULL)) {
notify(player,
"You haven't allocated all your package skills properly yet!");
return -1;
}
return 1;
case SKI_PICK:
x = st->skibought;
y = st->skitotal;
if ((st->skill_substate + 1) < NUM_SKIMENUS)
return 1;
GENNOT("skill points");
if (st->chosen_packagetype != PACKAGE_NONE &&
(!st->skills[char_getvaluecode("Small_Arms")] ||
!st->skills[char_getvaluecode("Medtech")])) {
notify(player,
"All packages require at least one course on both Small_Arms and Medtech!");
return -1;
}
for (i = 0; i < NUM_CHARVALUES; i++) {
if ((x = st->skills[i]) > (y = st->attributes[LEARN_NUMBER])) {
notify(player,
tprintf
("Some of your skill(s) are above your LRN! (%d > %d",
x, y));
return -1;
} else if (x)
if ((y = SKILLBTH(i)) < 4) {
notify(player,
tprintf
("Skills can't have better than 4+ BTH! (%s = %d+)",
char_values[i].name, y));
return -1;
}
if (st->pskills[i] > st->skills[i]) {
notify(player,
tprintf
("Note: You can't drop skills from what you chose for them from packages. (%s: %d->%d can't be done)",
char_values[i].name, st->pskills[i],
st->skills[i]));
return -1;
}
}
return 1;
}
return 0;
}
static void update_skillmenu_entry(struct chargen_struct *st, int id,
int val)
{
coolmenu *d;
int i;
for (i = 0; i < NUM_SKIMENUS; i++)
if (st->sm[i])
for (d = st->sm[i]; d; d = d->next)
if (d->id == id)
d->value = val;
}
static void chargen_recalculate_menu(dbref player, coolmenu * menu,
coolmenu * e)
{
struct chargen_struct *st;
int x = 0, y = 0, i;
switch (State) {
case PACK_PICK:
/* Pack picking's tricky - you can always pick just one */
if (e) {
st->packagesbought += e->value - (st->packages[e->id - 1]);
st->packages[e->id - 1] = e->value;
}
x = st->packagesbought;
y = 1;
break;
case PACKSET_PICK:
if (e) {
st->careersbought += e->value - (st->careers[e->id - 1]);
st->careers[e->id - 1] = e->value;
}
x = st->careersbought;
y = (st->chosen_packagetype >= BASIC_UNIV) ? 2 : 1;
break;
case PACKSKI_PICK:
/* This has a special status (fear) */
if (e) {
st->skills[e->id - 1] += e->value - st->pskills[e->id - 1];
/* Also, we have to update the damned menu entry */
update_skillmenu_entry(st, e->id, st->skills[e->id - 1]);
st->pskills[e->id - 1] = e->value;
}
pack_skills_status(st, menu);
return;
case PRI_PICK:
if (e) {
st->pribought += e->value - (st->prioritys[e->id - 1]);
st->prioritys[e->id - 1] = e->value;
}
x = st->pribought;
y = st->pritotal;
break;
case ADV_PICK:
if (e) {
st->advbought += e->value - st->advantages[e->id - 1];
st->advantages[e->id - 1] = e->value;
}
x = st->advbought;
y = st->advtotal;
break;
case ATT_PICK:
if (e) {
if ((e->id - 1) == INT_NUMBER)
st->attbought +=
(2 * e->value) - (2 * st->attributes[e->id - 1]);
else
st->attbought += (e->value - st->attributes[e->id - 1]);
if (e->value > 6 && st->attributes[e->id - 1] <= 6)
st->eacount++;
else if (e->value <= 6 && st->attributes[e->id - 1] > 6)
st->eacount--;
st->attributes[e->id - 1] = e->value;
}
x = st->attbought;
y = st->atttotal;
break;
case SKI_PICK:
if (e) {
i = MAX(e->value, st->pskills[e->id - 1]);
DOCHECK(i != e->value,
tprintf
("Error: You can't go below the level allocated from the package skills (%d)",
i));
st->skibought +=
recursive_add(i) - recursive_add(st->skills[e->id - 1]);
st->skills[e->id - 1] = i;
}
x = st->skibought;
y = st->skitotal;
break;
}
update_status(st, menu, e, x, y);
}
/* DasMagic is the one that figures how to get coolmenu struct */
#define DASMAGIC \
coolmenu *c = find_proper_menu(retrieve_chargen_struct(player))
/* DasMagic2 is name of the coolmenu struct in the function */
#define DASMAGIC2 c
/* DasMagic3 is called if value in the menu changes */
/* c = main menu, d = the changed entry */
#define DASMAGIC3 chargen_recalculate_menu(player,c,d)
/* If DasMagic4 is set, menu is re-shown every time value changes */
#undef DASMAGIC4
#include "coolmenu_interface2.h"
COMMANDSET(cm);
int can_advance_state(struct chargen_struct *st)
{
if (st->state == DONE)
return 0;
if (st->state == NOTBEGUN)
return 0;
return 1;
}
int can_go_back_state(struct chargen_struct *st)
{
return ((st->state != DONE || !Applied) && st->state > PRI_PICK);
}
static coolmenu *make_generic_menu(dbref player, struct chargen_struct *st,
int type, int flag, int maxval)
{
coolmenu *c, *d;
c = create_menu_of_charvalues(player, NULL, type, flag, maxval);
if (st->state == SKI_PICK) {
for (d = c; d; d = d->next)
if (d->id > 0)
d->value = st->skills[d->id - 1];
}
if (st->state == PACKSKI_PICK) {
for (d = c; d; d = d->next)
if (d->id > 0)
d->value = st->pskills[d->id - 1];
}
if (st->state == ADV_PICK) {
for (d = c; d; d = d->next)
if (d->id > 0)
d->flags |= CM_NO_HILITE;
}
if (st->state != NOTBEGUN) {
CreateMenuEntry_Simple(&c, "Prev = Previous menu",
CM_TWO | CM_CENTER);
CreateMenuEntry_Simple(&c, "Next = Next menu", CM_TWO | CM_CENTER);
}
CreateMenuEntry_Normal(&c, "Status", CM_ONE | CM_CENTER, -1, 0);
CreateMenuEntry_Simple(&c, NULL, CM_ONE | CM_LINE);
return c;
}
static coolmenu *make_text_menu(dbref player, struct chargen_struct *st,
char *desc, char **ch, int type, int maxval)
{
coolmenu *c;
c = SelCol_Menu(-1, desc, ch, type, maxval);
CreateMenuEntry_Normal(&c, "Status", CM_ONE, -1, 0);
CreateMenuEntry_Simple(&c, NULL, CM_ONE | CM_LINE);
return c;
}
void recalculate_skillpoints(struct chargen_struct *st)
{
int i = 0;
int j;
i += das_career_costs[st->chosen_packagetype];
for (j = 0; j < NUM_CHARVALUES; j++)
if (st->skills[j])
i += recursive_add(st->skills[j]) -
recursive_add(st->pskills[j]);
st->skibought = i;
}
static void advance_state(dbref player, struct chargen_struct *st)
{
coolmenu *c;
int i;
switch (st->state) {
case NOTBEGUN:
if (!st->cm[MENU_PRI])
st->cm[MENU_PRI] =
make_text_menu(player, st, "Priority Selection", das_menus,
CM_NUMBER, 4);
st->state = PRI_PICK;
break;
case PRI_PICK:
if (!st->cm[MENU_ADV])
st->cm[MENU_ADV] =
make_generic_menu(player, st, CHAR_ADVANTAGE, -1, 8);
st->advtotal = st->prioritys[ADVANTAGE];
st->atttotal = 18 + (st->prioritys[ATTRIBUTE]) * 3;
st->skitotal = 8 + (st->prioritys[SKILL]) * 4;
st->state = ADV_PICK;
if (!st->advbought && !st->advtotal) {
advance_state(player, st);
return;
}
break;
case ADV_PICK:
if (!st->cm[MENU_ATT])
st->cm[MENU_ATT] =
make_generic_menu(player, st, CHAR_ATTRIBUTE, -1, 7);
st->state = ATT_PICK;
if (!st->attbought && !st->atttotal) {
advance_state(player, st);
return;
}
break;
case ATT_PICK:
if (!st->cm[MENU_PACK])
st->cm[MENU_PACK] =
make_text_menu(player, st, "Package Selection",
das_packages, CM_TOGGLE, 1);
st->state = PACK_PICK;
break;
case PACK_PICK:
if (!st->cm[MENU_PACKSET])
st->cm[MENU_PACKSET] =
make_text_menu(player, st, "Career Selection", das_careers,
CM_TOGGLE, 1);
st->state = PACKSET_PICK;
break;
case PACKSET_PICK:
/*
This _is_ painful part..
1) We have to ensure that pskills is empty,
2) Always create new menu (according to careers)
*/
if (st->career_or_package_changed) {
st->career_or_package_changed = 0;
if (st->cm[MENU_PACKSKI])
free((void *) st->cm[MENU_PACKSKI]);
for (i = 0; i < NUM_CHARVALUES; i++)
if (st->pskills[i]) {
st->skills[i] -= st->pskills[i];
st->pskills[i] = 0;
}
st->cm[MENU_PACKSKI] = create_packskill_menu(player, st);
for (i = 0; i < NUM_SKIMENUS; i++)
if (st->sm[i]) {
free((void *) st->sm[i]);
st->sm[i] = NULL;
}
}
st->state = PACKSKI_PICK;
break;
case PACKSKI_PICK:
/*
Once we leave packskills, we _do_ have to recalculate our
skillpoint total used (mainly thanks to packages fudging it,
and some other small things
*/
recalculate_skillpoints(st);
st->skill_substate = -1;
st->state = SKI_PICK;
case SKI_PICK:
if ((++st->skill_substate) == NUM_SKIMENUS)
st->state = DONE;
else {
if (!st->sm[st->skill_substate])
st->sm[st->skill_substate] =
make_generic_menu(player, st, CHAR_SKILL,
1 << st->skill_substate, 7);
}
break;
}
c = find_proper_menu(st);
if (State == DONE) {
chargen_look(player, NULL, NULL);
return;
}
if (!c)
return;
chargen_recalculate_menu(player, c, NULL);
ShowCoolMenu(player, c);
}
void go_back_state(dbref player, struct chargen_struct *st)
{
coolmenu *c;
switch (st->state) {
case ADV_PICK:
st->state = PRI_PICK;
break;
case ATT_PICK:
st->state = ADV_PICK;
if (!st->advbought && !st->advtotal) {
go_back_state(player, st);
return;
}
break;
case DONE:
st->state = SKI_PICK;
st->skill_substate = NUM_SKIMENUS - 1;
break;
case SKI_PICK:
if ((--st->skill_substate) < 0)
st->state = PACKSKI_PICK;
break;
case PACK_PICK:
st->state = ATT_PICK;
if (!st->attbought && !st->atttotal) {
go_back_state(player, st);
return;
}
break;
case PACKSET_PICK:
st->state = PACK_PICK;
break;
case PACKSKI_PICK:
st->state = PACKSET_PICK;
break;
}
c = find_proper_menu(st);
if (!c)
return;
chargen_recalculate_menu(player, c, NULL);
ShowCoolMenu(player, c);
}
static void apply_values(dbref player, coolmenu * c, int overwrite)
{
int v, i;
for (; c; c = c->next)
if (c->id > 0) {
i = c->id - 1;
v = c->value;
if (overwrite) {
char_setvaluebycode(player, i, v);
} else {
char_setvaluebycode(player, i, char_getvaluebycode(player,
i) + v);
}
}
}
#include "chargen_commands.h"