/* * 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); }