/*
* player2.cpp
* Player routines.
* ____ _
* | _ \ ___ __ _| |_ __ ___ ___
* | |_) / _ \/ _` | | '_ ` _ \/ __|
* | _ < __/ (_| | | | | | | \__ \
* |_| \_\___|\__,_|_|_| |_| |_|___/
*
* Permission to use, modify and distribute is granted via the
* Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License
* http://creativecommons.org/licenses/by-nc-sa/3.0/
*
* Copyright (C) 2007-2009 Jason Mitchell, Randi Mitchell
* Contributions by Tim Callahan, Jonathan Hseu
* Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman
*
*/
#include <math.h>
#include "mud.h"
#include "login.h"
#include "commands.h"
void Player::calcStats(vstat sendStat, vstat *toStat) {
int i=0, levels = 0, index, switchNum=0;
int cls=0, cls2=0, lvl=0, r=0;
int hptemp=0,mptemp=0;
// stats are 1 based, the array is 0 based, so give us extra room
int num[MAX_STAT+1];
if(sendStat.race)
r = tstat.race;
else
r = race;
if(sendStat.cls)
cls = tstat.cls;
else
cls = cClass;
if(sendStat.cls2)
cls2 = tstat.cls2;
else
cls2 = cClass2;
if(sendStat.level)
lvl = tstat.level;
else
lvl = level;
for(i=1; i<=MAX_STAT; i++) {
num[i] = sendStat.num[i-1];
num[i] += gConfig->getRace(r)->getStatAdj(i);
num[i] = MAX(1, num[i]) * 10;
}
// Now to adjust hit points accordingly
if(!cls2) {
hptemp = class_stats[(int)cls].hpstart;
mptemp = class_stats[(int)cls].mpstart;
} else {
hptemp = (class_stats[(int)cls].hpstart + class_stats[(int)cls2].hpstart) / 2;
mptemp = (class_stats[(int)cls].mpstart + class_stats[(int)cls2].mpstart) / 2;
}
for(levels = 0; levels < lvl-1; levels ++) {
index = levels % 10;
if(!cls2)
switchNum = level_cycle[(int)cls][index];
else
switchNum = multiStatCycle[getMultiClassID(cls, cls2)][index];
num[switchNum] += 10;
// Now to adjust hit points accordingly
if(!cls2) {
hptemp += class_stats[(int)cls].hp;
mptemp += class_stats[(int)cls].mp;
} else {
hptemp += multiHpMpAdj[getMultiClassID(cls, cls2)][0];
mptemp += multiHpMpAdj[getMultiClassID(cls, cls2)][1];
}
if(cls != LICH) {
if(cls == BERSERKER && num[CON] >= 70)
hptemp++;
if(num[CON] >= 130)
hptemp++;
if(num[CON] >= 210)
hptemp++;
if(num[CON] >= 250)
hptemp++;
}
// liches gain an extra HP at every even level.
if(levels % 2 == 0 && cls == LICH)
hptemp++;
}
for(i=0; i<MAX_STAT; i++)
toStat->num[i] = num[i+1];
toStat->hp = hptemp;
toStat->mp = mptemp;
}
bool Player::checkConfusion() {
int action=0, dmg=0;
mType targetType = PLAYER;
char atk[50];
Creature* target=0;
Exit *newExit=0;
BaseRoom* room = getRoom(), *bRoom=0;
CatRef cr;
if(!isEffected("confusion"))
return(false);
action = mrand(1,4);
switch(action) {
case 1: // Stand confused
broadcast(getSock(), room, "%M stands with a confused look on %s face.",
this, hisHer());
printColor("^BYou are confused and dizzy. You stand and look around cluelessly.\n");
stun(mrand(5,10));
return(true);
break;
case 2: // Wander to random exit
newExit = getFleeableExit();
if(!newExit)
return(false);
bRoom = getFleeableRoom(newExit);
if(!bRoom)
return(false);
printColor("^BWanderlust overtakes you.\n");
printColor("^BYou wander aimlessly to the %s exit.\n", newExit->name);
broadcast(getSock(), room, "%M wanders aimlessly to the %s exit.", this, newExit->name);
deleteFromRoom();
addToRoom(bRoom);
doPetFollow();
return(true);
break;
case 3: // Attack something randomly
if(!checkAttackTimer(false))
return(false);
switch(mrand(1,2)) {
case 1:
target = getRandomMonster(room);
if(!target)
target = getRandomPlayer(room);
if(target) {
targetType = target->getType();
break;
}
break;
case 2:
target = getRandomPlayer(room);
if(!target)
target = getRandomMonster(room);
if(target) {
targetType = target->getType();
break;
}
break;
default:
break;
}
if(!target || target == this)
targetType = INVALID;
switch(targetType) {
case PLAYER: // Random player in room
printColor("^BYou are convinced %s is trying to kill you!\n", target->name);
attackCreature(target);
return(true);
break;
case INVALID: // Self
if(ready[WIELD-1]) {
dmg = ready[WIELD-1]->damage.roll() +
bonus((int)strength.getCur()) + ready[WIELD-1]->getAdjustment();
printColor("^BYou frantically swing your weapon at imaginary enemies.\n");
} else {
printColor("^BYou madly flail around at imaginary enemies.\n");
if(cClass == MONK) {
dmg = mrand(1,2) + level/3 + mrand(1,(1+level)/2);
if(strength.getCur() < 90) {
dmg -= (90-strength.getCur())/10;
dmg = MAX(1,dmg);
}
} else
dmg = damage.roll();
}
getDamageString(atk, this, ready[WIELD - 1]);
printColor("^BYou %s yourself for %d damage!\n", atk, dmg);
broadcast(getSock(), room, "%M %s %sself!", this, atk, himHer());
hp.decrease(dmg);
if(hp.getCur() < 1) {
hp.setCur(1);
printColor("^BYou accidentally killed yourself!\n");
broadcast("### Sadly, %s accidentally killed %sself.", name, himHer());
mp.setCur(1);
if(!inJail()) {
bRoom = getLimboRoom().loadRoom(this);
if(bRoom) {
deleteFromRoom();
addToRoom(bRoom);
doPetFollow();
}
}
}
return(true);
break;
default: // Random monster in room.
if(target->flagIsSet(M_UNKILLABLE))
return(false);
printColor("^BYou think %s is attacking you!\n", target);
broadcast(getSock(), room, "%M yells, \"DIE %s!!!\"\n", this, target->name);
attackCreature(target);
return(true);
break;
}
break;
case 4: // flee
printColor("^BPhantom dangers in your head beckon you to flee!\n");
broadcast(getSock(), room, "%M screams in terror, pointing around at everything.", this);
flee();
return(true);
break;
default:
break;
}
return(false);
}
Monster* Player::getPet() const {
Monster* pet=0;
ctag* fp=0;
fp = first_fol;
if(!fp)
return(0);
while(fp) {
if(fp->crt->isPet())
pet = fp->crt->getMonster();
if(pet && pet->following == this)
return(pet);
fp = fp->next_tag;
}
return(0);
}
void Player::doFollow() {
ctag* cp=0;
cp = first_fol;
while(cp) {
Player* pFollow = cp->crt->getPlayer();
Monster* mFollow = cp->crt->getMonster();
if(cp->crt->getRoom() != getRoom()) {
if(pFollow) {
pFollow->deleteFromRoom();
pFollow->addToRoom(getRoom());
} else {
mFollow->deleteFromRoom();
mFollow->addToRoom(getRoom());
}
}
cp = cp->next_tag;
}
}
void Player::doPetFollow() {
Monster *pet = getPet();
if(pet && pet->getRoom() != getRoom()) {
pet->deleteFromRoom();
pet->addToRoom(getRoom());
}
if(alias_crt && alias_crt->getRoom() != getRoom()) {
alias_crt->deleteFromRoom();
alias_crt->addToRoom(getRoom());
}
}
CatRef getEtherealTravelRoom() {
const CatRefInfo* eth = gConfig->getCatRefInfo("et");
CatRef cr;
cr.setArea("et");
cr.id = mrand(1, eth->teleportWeight);
return(cr);
}
void etherealTravel(Player* player) {
Room *newRoom=0;
CatRef cr = getEtherealTravelRoom();
if(!loadRoom(cr, &newRoom))
return;
player->deleteFromRoom();
player->addToRoom(newRoom);
player->doPetFollow();
}
//*********************************************************************
// cmdSurname
//*********************************************************************
int cmdSurname(Player* player, cmd* cmnd) {
int nonalpha=0;
unsigned int i=0;
bool illegalNonAlpha=false;
if(!player->ableToDoCommand())
return(0);
if(player->flagIsSet(P_NO_SURNAME)) {
player->print("You have lost the privelage of choosing a surname.\n");
return(0);
}
if(player->flagIsSet(P_CHOSEN_SURNAME) && !player->isCt()) {
player->print("You've already chosen your surname.\n");
return(0);
}
if(player->getLevel() < SURNAME_LEVEL && !player->isStaff()) {
player->print("You must be level %d to choose a surname.\n", SURNAME_LEVEL);
return(0);
}
if(cmnd->num < 2) {
player->print("Syntax: surname <surname>\n");
return(0);
}
if(strlen(cmnd->str[1]) > 14) {
player->print("Your surname may only be a max of 14 characters long.\n");
return(0);
}
if(strlen(cmnd->str[1]) < 3) {
player->print("Your surname must be at least 3 characters in length.\n");
return(0);
}
for(i=0; i< strlen(cmnd->str[1]); i++) {
if(!isalpha(cmnd->str[1][i])) {
nonalpha++;
if(cmnd->str[1][i] != '\'' && cmnd->str[1][i] != '-')
illegalNonAlpha=true;
}
}
if(illegalNonAlpha) {
player->print("The only non-alpha characters allowed in surnames are ' and -.\n");
return(0);
}
if(nonalpha && strlen(cmnd->str[1]) < 6) {
player->print("Your surname must be at least 6 characters in order to contain a - or '.\n");
return(0);
}
if(nonalpha > 1) {
player->print("Your surname may not have more than one non-alpha character.\n");
return(0);
}
for(i=0; i < strlen(cmnd->str[1]); i++) {
if(!isalpha(cmnd->str[1][i]) && cmnd->str[1][i] != '\'' && cmnd->str[1][i] != '-') {
player->print("Your surname must be alphabetic.\n");
player->print("It may only contain the non-alpha characters ' and -.\n");
return(0);
}
}
if(cmnd->str[1][0] == '\'' || cmnd->str[1][0] == '-') {
player->print("The first character of your surname must be a letter.\n");
return(0);
}
if(cmnd->str[1][strlen(cmnd->str[1])-1] == '\'' || cmnd->str[1][strlen(cmnd->str[1])-1] == '-' ||
cmnd->str[1][strlen(cmnd->str[1])-2] == '\'' || cmnd->str[1][strlen(cmnd->str[1])-2] == '-') {
player->print("The last two characters of your surname must be letters.\n");
return(0);
}
lowercize(cmnd->str[1], 1);
player->setSurname(cmnd->str[1]);
player->printColor("^WNote that profane or otherwise idiotic surnames, as well as\n");
player->printColor("idiotic combinations of name and surname, will not be tolerated.^x\n\n");
player->print("Your full name will be %s %s.\n", player->name, player->getSurname().c_str());
player->print("Is this acceptable?(Y/N)?\n");
player->getSock()->setState(CON_CONFIRM_SURNAME);
return(0);
}
//*********************************************************************
// doSurname
//*********************************************************************
void doSurname(Socket* sock, char *str) {
if(low(str[0]) == 'y') {
sock->print("You are now known as %s %s.\n", sock->getPlayer()->getName(), sock->getPlayer()->getSurname().c_str());
if(!sock->getPlayer()->isStaff())
broadcast("### %s is now known as %s %s.", sock->getPlayer()->getName(), sock->getPlayer()->getName(),
sock->getPlayer()->getSurname().c_str());
sock->getPlayer()->setFlag(P_CHOSEN_SURNAME);
} else
sock->print("Aborted.\n");
sock->setState(CON_PLAYING);
}
//********************************************************************
// remFromGroup
//********************************************************************
// if player is in a group, they will be removed from it.
void remFromGroup(Creature* player) {
ctag *cp=0, *prev=0;
Creature *leader=0;
if(!player->following)
return;
leader = player->following;
cp = leader->first_fol;
if(cp->crt == player) {
leader->first_fol = cp->next_tag;
delete cp;
} else {
while(cp) {
if(cp->crt == player) {
prev->next_tag = cp->next_tag;
delete cp;
break;
}
prev = cp;
cp = cp->next_tag;
}
}
player->following = 0;
}
//********************************************************************
// cmdVisible
//********************************************************************
int cmdVisible(Player* player, cmd* cmnd) {
if(!player->isInvisible()) {
player->print("You are not invisible.\n");
return(0);
} else {
player->removeEffect("invisibility");
player->removeEffect("greater-invisibility");
}
return(0);
}
//********************************************************************
// cmdDice
//********************************************************************
int cmdDice(Creature* player, cmd* cmnd) {
char *str=0, *tok=0, diceOutput[256], add[256];
int strLen=0, i=0;
int diceSides=0,diceNum=0,diceAdd=0;
int rolls=0, total=0;
const char *Syntax = "\nSyntax: dice 1d2\n"
" dice 1d2+3\n";
strcpy(diceOutput, "");
strcpy(add ,"");
strLen = strlen(cmnd->fullstr);
// This kills all leading whitespace
while(i<strLen && isspace(cmnd->fullstr[i]))
i++;
// This kills the command itself
while(i<strLen && !isspace(cmnd->fullstr[i]))
i++;
str = strstr(&cmnd->fullstr[i], "d");
if(!str)
return(action(player, cmnd));
str = strdup(&cmnd->fullstr[i]);
if(!str) {
player->print(Syntax);
return(0);
}
tok = strtok(str, "d");
if(!tok) {
player->print(Syntax);
return(0);
}
diceNum = atoi(tok);
tok = strtok(NULL, "+");
if(!tok) {
player->print(Syntax);
return(0);
}
diceSides = atoi(tok);
tok = strtok(NULL, "+");
if(tok)
diceAdd = atoi(tok);
if(diceNum < 0) {
player->print("How can you roll a negative number of dice?\n");
return(0);
}
diceNum = MAX(1, diceNum);
if(diceSides<2) {
player->print("A die has a minimum of 2 sides.\n");
return(0);
}
diceNum = MIN(100, diceNum);
diceSides = MIN(100, diceSides);
diceAdd = MAX(-100,MIN(100, diceAdd));
sprintf(diceOutput, "%dd%d", diceNum, diceSides);
if(diceAdd) {
if(diceAdd > 0)
sprintf(add, "+%d", diceAdd);
else
sprintf(add, "%d", diceAdd);
strcat(diceOutput, add);
}
for(rolls=0;rolls<diceNum;rolls++)
total += mrand(1, diceSides);
total += diceAdd;
player->print("You roll %s\n: %d\n", diceOutput, total);
broadcast(player->getSock(), player->getRoom(), "(Dice %s): %M got %d.", diceOutput, player, total );
return(0);
}
//********************************************************************
// plyChooseAlignment
//********************************************************************
int plyChooseAlignment(Player* player, cmd* cmnd) {
char syntax[] = "Syntax: alignment lawful\n"
" alignment chaotic\n"
"Note: Tieflings must be chaotic.\n\n";
if(player->isStaff() && !player->isCt())
return(0);
if(!player->ableToDoCommand())
return(0);
if(player->flagIsSet(P_CHOSEN_ALIGNMENT)) {
player->print("You have already chosen your alignment.\n");
if(player->flagIsSet(P_CHAOTIC))
player->print("In order to convert to lawful, use the 'convert' command. HELP CONVERT.\n");
return(0);
} else if(player->getLevel() < ALIGNMENT_LEVEL) {
player->print("You cannot choose your alignment until level %d.\n", ALIGNMENT_LEVEL);
return(0);
} else if(player->getLevel() > ALIGNMENT_LEVEL) {
player->print("Your alignment has already been chosen.\n");
return(0);
}
if(cmnd->num < 2) {
player->print(syntax);
return(0);
}
if(!strcasecmp(cmnd->str[1], "lawful")) {
if(player->getRace() == TIEFLING) {
player->print("Tieflings are required to be chaotic.\n\n");
} else {
broadcast("^B### %s chooses to adhere to the order of LAW!", player->getName());
player->setFlag(P_CHOSEN_ALIGNMENT);
}
} else if(!strcasecmp(cmnd->str[1], "chaotic")) {
broadcast("^R### %s chooses to embrace the whims of CHAOS!", player->getName());
player->setFlag(P_CHAOTIC);
player->setFlag(P_CHOSEN_ALIGNMENT);
} else {
player->print(syntax);
return(0);
}
return(0);
}
//********************************************************************
// plyHasObj
//********************************************************************
// This will check to see if a player has a specific object type
// either in their inventory or in a bag, or in their worn equipment.
bool plyHasObj(Creature* player, Object *item) {
int a=0;
Object *obj=0;
Room* room=0;
otag *op=0, *cop=0;
//check inventory
op = player->first_obj;
while(op) {
obj = op->obj;
if(*&obj->info == *&item->info)
return(true);
// if item is a bag, check in bag
if(obj->getType() == CONTAINER) {
cop = obj->first_obj;
while(cop) {
if(*&cop->obj->info == *&item->info)
return(true);
cop = cop->next_tag;
}
}
op = op->next_tag;
}
// check worn equipment
for(a=0;a<MAXWEAR;a++) {
if(!player->ready[a])
continue;
obj = player->ready[a];
if(*&obj->info == *&item->info && obj != item)
return(true);
// if worn item is a bag, check in bag
if(obj->getType() == CONTAINER) {
cop = obj->first_obj;
while(cop) {
if(*&cop->obj->info == *&item->info && cop->obj != item)
return(true);
cop = cop->next_tag;
}
}
}
// check player's storage room, if it exists
if(player->isPlayer()) {
CatRef sr = gConfig->getSingleProperty(player->getPlayer(), PROP_STORAGE);
if(sr.id < 1 || !loadRoom(sr, &room))
return(0);
op = room->first_obj;
while(op) {
if(op->obj->getType() == CONTAINER) {
cop = op->obj->first_obj;
while(cop) {
if(*&cop->obj->info == *&item->info && cop->obj != item)
return(true);
cop = cop->next_tag;
}
}
if(*&op->obj->info == *&item->info && op->obj != item)
return(true);
op = op->next_tag;
}
}
return(false);
}