/* * command2.cpp * Command handling/parsing 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 "mud.h" #include "commands.h" #include "move.h" #include "unique.h" #define STONE_SCROLL_INDEX 10 //********************************************************************* // stoneScroll //********************************************************************* int stoneScroll(Player* player, cmd* cmnd) { char filename[80]; sprintf(filename, "%sstone_scroll", SIGNSCROLL); if(!strncmp(cmnd->str[2], "class", strlen(cmnd->str[2]))) strcat(filename, "_class"); else if(!strncmp(cmnd->str[2], "race", strlen(cmnd->str[2]))) strcat(filename, "_race"); else if(!strncmp(cmnd->str[2], "deity", strlen(cmnd->str[2]))) strcat(filename, "_deity"); else if(!strncmp(cmnd->str[2], "breakdown", strlen(cmnd->str[2]))) strcat(filename, "_breakdown"); strcat(filename, ".txt"); viewLoginFile(player->getSock(), filename); return(0); } //********************************************************************* // cmdLook //********************************************************************* int cmdLook(Player* player, cmd* cmnd) { void *target=0; int targetType = -1; int flags = player->displayFlags(); bool found=false; if(!player->ableToDoCommand()) return(0); if(player->isBlind()) { player->printColor("^CYou're blind!\n"); return(0); } if(!cmnd || cmnd->num < 2) { display_rom(player); return(0); } found = findTarget(player, FIND_OBJ_INVENTORY | FIND_OBJ_EQUIPMENT | FIND_OBJ_ROOM | FIND_MON_ROOM | FIND_PLY_ROOM, flags, cmnd->str[1], cmnd->val[1], &target, &targetType); if(found) { switch(targetType) { case PLAYER: case MONSTER: player->displayCreature((Creature *)target); break; case OBJECT: if(cmnd->num == 3 && ((Object*)target)->info.id == STONE_SCROLL_INDEX && ((Object*)target)->info.isArea("misc")) stoneScroll(player, cmnd); else displayObject(player, (Object*)target); break; default: player->print("You get all confused looking at it.\n"); break; } } else { player->print("You don't see that here.\n"); } return(0); } //********************************************************************* // getGuardingExit //********************************************************************* Monster* BaseRoom::getGuardingExit(const Exit* exit) const { ctag* cp=0; if(exit->flagIsSet(X_PASSIVE_GUARD)) { cp = first_mon; while(cp) { if(cp->crt->flagIsSet(M_PASSIVE_EXIT_GUARD)) return(cp->crt->getMonster()); cp = cp->next_tag; } } return(0); } //********************************************************************* // cmdThrow //********************************************************************* void finishDropObject(Object* object, BaseRoom* room, Creature* player, bool cash, bool printPlayer, bool printRoom); int cmdThrow(Creature* creature, cmd* cmnd) { Object* object=0; Creature* victim=0; Monster* guard=0; Exit* exit=0; BaseRoom *room=0; Room* uRoom=0; AreaRoom* aRoom=0; Player* player = creature->getPlayer(), *pVictim=0; if(!creature->ableToDoCommand()) return(0); if(!creature->checkAttackTimer()) return(0); if(creature->isBlind()) { creature->printColor("^CYou can't do that. You're blind!\n"); return(0); } object = findObject(creature, creature->first_obj, cmnd); room = creature->getRoom(); if(!object) { creature->print("You don't have that in your inventory.\n"); return(0); } if( object->getActualWeight() > creature->strength.getCur()/3 && !creature->checkStaff("%O is too heavy for you to throw!\n", object) ) return(0); exit = findExit(creature, cmnd, 2, room->first_ext); if(!exit) victim = room->findCreature(creature, cmnd, 2); if(exit) { if(creature->inCombat() && !creature->checkStaff("Not while you are in combat!\n")) return(0); creature->printColor("You throw %P to the %s.\n", object, exit->name); broadcast(creature->getSock(), room, "%M throws %P to the %s.", creature, object, exit->name); creature->delObj(object); guard = room->getGuardingExit(exit); // dont bother getting rooms if exit is closed or guarded if(!exit->flagIsSet(X_CLOSED) && !guard) Move::getRoom(creature, exit, &uRoom, &aRoom, false, &exit->target.mapmarker); room->wake("Loud noises disturb your sleep.", true); // closed, or off map, or being guarded if(exit->flagIsSet(X_CLOSED) || (!uRoom && !aRoom) || guard) { if(guard) broadcast(0, room, "%M knocks %P to the ground.", guard, object); else broadcast(0, room, "%O hits the %s and falls to the ground.", object, exit->name); finishDropObject(object, room, creature, false, true, true); } else { if(aRoom) room = aRoom; else room = uRoom; broadcast(0, room, "%1O comes flying into the room.", object); room->wake("Loud noises disturb your sleep.", true); finishDropObject(object, room, creature, false, false, true); if(aRoom && aRoom->canDelete()) aRoom->area->remove(aRoom); } creature->updateAttackTimer(true, DEFAULT_WEAPON_DELAY); } else if(victim) { pVictim = victim->getPlayer(); if(!creature->canAttack(victim)) return(0); if(creature->vampireCharmed(pVictim) || (victim->hasCharm(creature->name) && creature->isPlayer() && creature->flagIsSet(P_CHARMED))) { creature->print("You like %N too much to do that.\n", victim); return(0); } creature->updateAttackTimer(true, DEFAULT_WEAPON_DELAY); creature->smashInvis(); creature->unhide(); if(player) player->statistics.swing(); creature->printColor("You throw %P at %N.\n", object, victim); victim->printColor("%M throws %P at you.\n", creature, object); broadcast(creature->getSock(), victim->getSock(), room, "%M throws %P at %N.", creature, object, victim); creature->delObj(object); if(!pVictim) victim->getMonster()->addEnmCrt(creature); if(pVictim && victim->flagIsSet(P_MISTED)) { pVictim->statistics.wasMissed(); if(player) player->statistics.miss(); broadcast(victim->getSock(), room, "%O passes right through %N.", object, victim); victim->printColor("%O passes right through you.\n", object); } else { int skillLevel = (int)creature->getSkillGained("thrown"); AttackResult result = creature->getAttackResult(victim, 0, NO_FUMBLE, skillLevel); if(pVictim && victim->flagIsSet(P_UNCONSCIOUS)) result = ATTACK_HIT; if(result == ATTACK_DODGE) { if(pVictim) pVictim->statistics.dodge(); if(player) player->statistics.miss(); victim->printColor("You dodge out of the way.\n"); broadcast(victim->getSock(), room, "%M dodges out of the way.", victim); creature->checkImprove("thrown", false); } else if(result == ATTACK_MISS || result == ATTACK_BLOCK || result == ATTACK_PARRY) { if(pVictim) pVictim->statistics.wasMissed(); if(player) player->statistics.miss(); victim->printColor("You knock %P to the ground.\n", object); broadcast(victim->getSock(), room, "%M knocks %P to the ground.", victim, object); creature->checkImprove("thrown", false); } else { Damage damage; damage.set(1); if(object->getActualWeight() > 1) damage.set(mrand(1,6)); victim->modifyDamage(creature, PHYSICAL, damage); if(pVictim) pVictim->statistics.wasHit(); if(player) { player->statistics.hit(); player->statistics.attackDamage(damage.get(), Statistics::damageWith(player, object)); } creature->printColor("You hit %N for %s%d^x damage.\n", victim, creature->customColorize("*CC:DAMAGE*"), damage.get()); victim->printColor("%M hit you%s for %s%d^x damage!\n", creature, victim->isBrittle() ? "r brittle body" : "", victim->customColorize("*CC:DAMAGE*"), damage.get()); broadcastGroup(false, victim, "^M%M^x hit ^M%N^x for *CC:DAMAGE*%d^x damage, %s%s\n", creature, victim, damage.get(), victim->heShe(), victim->getStatusStr(damage.get())); creature->doDamage(victim, damage.get(), CHECK_DIE); creature->checkImprove("thrown", true); } } finishDropObject(object, room, creature, false, true, true); room->wake("Loud noises disturb your sleep.", true); } else { creature->printColor("Throw %P at what?\n", object); } return(0); } //********************************************************************* // cmdKnock //********************************************************************* int cmdKnock(Creature* creature, cmd* cmnd) { BaseRoom* targetRoom=0; Exit *exit=0; if(cmnd->num < 2) { creature->print("Knock on what exit?\n"); return(0); } lowercize(cmnd->str[1], 0); exit = findExit(creature, cmnd); if(!exit) { creature->print("Knock on what exit?\n"); return(0); } if(!exit->flagIsSet(X_CLOSED)) { creature->print("That exit is not closed!\n"); return(0); } creature->getRoom()->wake("You awaken suddenly!", true); creature->print("You knock on the %s.\n", exit->name); broadcast(creature->getSock(), creature->getRoom(), "%M knocks on the %s.", creature, exit->name); // change the meaning of exit exit = exit->getReturnExit(creature->getRoom(), &targetRoom); targetRoom->wake("You awaken suddenly!", true); if(exit) broadcast(0, targetRoom, "You hear someone knocking on the %s.", exit); else broadcast(0, targetRoom, "You hear the sound of someone knocking."); return(0); } //********************************************************************* // cmdPrepare //********************************************************************* int cmdPrepare(Player* player, cmd* cmnd) { if(cmnd->num < 2) return(cmdPrepareForTraps(player, cmnd)); return(cmdPrepareObject(player, cmnd)); } //********************************************************************* // loseAcid //********************************************************************* // This function goes through a players possessions worn AND carried // and determines whether it was fragged due to dissolving acid. void Player::loseAcid() { Object* object=0; otag *op=0; int i=0; // Check all equipped items for(i=0; i<MAXWEAR; i++) { if(ready[i]) { if( !ready[i]->flagIsSet(O_RESIST_DISOLVE) && (mrand(1,100) <= 5 - abs(ready[i]->getAdjustment())) ) { if(ready[i]->flagIsSet(O_NO_PREFIX)) { printColor("^r%s was dissolved by acid!\n", ready[i]->name); } else { printColor("^rYour %s was dissolved by acid!\n", ready[i]->name); } logn("log.dissolve", "%s(L%d) lost %s to acid in room %s.\n", name, level, ready[i]->name, getRoom()->fullName().c_str()); unequip(i+1, UNEQUIP_DELETE); } } } computeAC(); computeAttackPower(); // Remove and possibly dissolve possessions op = first_obj; while(op) { object = op->obj; op = op->next_tag; if( !object->flagIsSet(O_RESIST_DISOLVE) && (mrand(1,100) <= 5 - abs(object->getAdjustment())) ) { if(object->flagIsSet(O_NO_PREFIX)) { printColor("^r%s is dissolved by acid!\n", object->name); } else { printColor("^rYour %s is dissolved by acid!\n", object->name); } logn("log.dissolve", "%s(L%d) lost %s to acid in room %s.\n", name, level, object->name, getRoom()->fullName().c_str()); delObj(object, true, false, true, false); delete object; } } checkDarkness(); } //********************************************************************* // resistLose //********************************************************************* bool resistLose(Object* object) { if(object->flagIsSet(O_RESIST_DISOLVE) || object->getQuestnum()) return(true); int r = 15 + abs(object->getAdjustment()) * 5; // containers with lots of items are more resistant, // since it would suck to lose a lot of items if(object->getType() == CONTAINER) r += object->getShotscur() * 5; return(mrand(1,100) < r); } //********************************************************************* // lose_all //********************************************************************* void lose_all(Player* player, bool destroyAll) { Object* object=0; otag *op=0; int i=0; // if we send !destroyAll, there are some scenarios where // we dont destroy everything // remove all equipted items for(i=0; i<MAXWEAR; i++) { if(player->ready[i]) { if(!destroyAll && resistLose(player->ready[i])) { player->printColor("%O resisted destruction.\n", player->ready[i]); continue; } logn("log.dissolve", "%s(L%d) lost %s to acid in room %s.\n", player->name, player->getLevel(), player->ready[i]->name, player->getRoom()->fullName().c_str()); player->ready[i]->popBag(player, true, false, false, false, true); player->unequip(i+1, UNEQUIP_DELETE); } } player->computeAC(); player->computeAttackPower(); // delete all possessions op = player->first_obj; while(op) { object = op->obj; op = op->next_tag; if(!destroyAll && resistLose(object)) { player->printColor("%O resisted destruction.\n", object); continue; } logn("log.dissolve", "%s(L%d) lost %s to acid in room %s.\n", player->name, player->getLevel(), object->name, player->getRoom()->fullName().c_str()); object->popBag(player, true, false, false, false, true); player->delObj(object, true, false, true, false); delete object; } player->checkDarkness(); } //********************************************************************* // dissolveItem //********************************************************************* // dissolve_item will randomly select one equipted (including held or // wield) items on the given player and then delete it. The player // receives a message that the item was destroyed as well as who is // responsible for the deed. void Player::dissolveItem(Creature* creature) { char checklist[MAXWEAR]; int numwear=0, i=0, n=0; for(i=0; i<MAXWEAR; i++) { checklist[i] = 0; // if(i==WIELD-1 || i==HELD-1) continue; if(ready[i]) checklist[numwear++] = i+1; } if(!numwear) n = 0; else { i = mrand(0, numwear-1); n = (int) checklist[i]; } if(n) { if(ready[n-1]) { if(ready[n - 1]->flagIsSet(O_RESIST_DISOLVE)) { printColor("%M tried to dissolve your %s.\n", creature, ready[n-1]->name); n = 0; return; } } } if(n) { broadcast(getSock(), getRoom(),"%M destroys %N's %s.", creature, this, ready[n-1]->name); printColor("%M destroys your %s.\n",creature, ready[n-1]->name); logn("log.dissolve", "%s(L%d) lost %s to acid in room %s.\n", name, level, ready[n-1]->name, getRoom()->fullName().c_str()); // Unequip it and don't add it to the inventory, delete it unequip(n, UNEQUIP_DELETE); computeAC(); } } //********************************************************************* // cmdBreak //********************************************************************* int cmdBreak(Player* player, cmd* cmnd) { int duration=0; CatRef newloc; int chance=0, dmg=0, xpgain=0, splvl=0; float mtbf=0, shots=0, shotsmax=0; ctag *cp=0; Room *new_rom=0; char item[80]; Object *object=0; player->clearFlag(P_AFK); if(cmnd->num < 2) { player->print("Break what?\n"); return(0); } if(!player->checkAttackTimer()) return(0); object = findObject(player, player->first_obj, cmnd); if(!object) { player->print("You don't have that in your inventory.\n"); return(0); } if( object->flagIsSet(O_BROKEN_BY_CMD) || object->flagIsSet(O_NO_BREAK) || object->getType() == KEY || object->getSpecial() || object->getType() == MONEY || (object->getShotscur() < 1 && object->getType() != CONTAINER) ) { player->print("You can't break that.\n"); return(0); } if(object->getType() == CONTAINER && object->getShotsmax() < 1) { player->print("You can't break that.\n"); return(0); } if((object->getType() == WAND || (object->getType() < 5 && object->flagIsSet(O_WEAPON_CASTS) && object->getMagicpower())) && player->getRoom()->flagIsSet(R_SAFE_ROOM) && !player->isCt()) { player->print("You really shouldn't do that in here. Someone could get hurt.\n"); return(0); } chance = player->strength.getCur()*4; if(player->isDm()) player->print("Chance(str): +%d%\n", player->strength.getCur()*4); shots = (float)object->getShotscur(); shotsmax = (float)object->getShotsmax(); mtbf = shots/shotsmax; mtbf *=100; mtbf = (100 - mtbf); chance += (int)mtbf; if(player->isDm()) player->print("Chance(shotslost): +%f%\n", mtbf); chance -= object->getAdjustment()*10; if(player->isDm()) player->print("Chance(adj): -%d%\n", object->getAdjustment()*10); chance -= object->getWeight()/10; if(player->isDm()) player->print("Chance(wgt): -%d%\n",object->getWeight()/10 ); chance = MAX(1, chance); if(object->getQuestnum()) chance = 0; if(player->isDm()) { player->print("Chance: %d%\n", chance); // chance = 101; } if(object->getType() == CONTAINER && object->first_obj) { player->print("You have to dump its contents out first!\n"); return(0); } if(mrand(1,100) <= chance) { object->setFlag(O_BROKEN_BY_CMD); if(object->compass) { delete object->compass; object->compass = 0; } if(!(object->getType() == POISON && player->getClass() == PALADIN)) { player->printColor("You manage to break %P.\n", object); broadcast(player->getSock(), player->getRoom(), "%M breaks %P.", player, object); } else { player->printColor("You dispose of the vile %P.\n", object); broadcast(player->getSock(), player->getRoom(), "%M neatly disposes of the vile %P.", player, object); } if(object->getType() == POISON || object->getType() == POTION) strcpy(item, "used up "); else strcpy(item, "broken "); strcat(item, object->name); sprintf(object->name, item); strncpy(object->key[2],"broken",20); if(object->getType() == CONTAINER) { object->setShotsmax(0); object->setType(MISC); } object->setShotscur(0); object->setFlag(O_NO_FIX); player->updateAttackTimer(true, 60); if( (player->getClass() == BERSERKER || player->isCt()) && object->getType() > 5 && (shots > 0.0) && object->value[GOLD] > 100 ) { xpgain = (int)mtbf+(object->getAdjustment()*10)+mrand(1,50); if(object->getType() == WAND) xpgain *= 3; if(!player->halftolevel()) { player->print("You %s %d experience for your deed.\n", gConfig->isAprilFools() ? "lose" : "gain", xpgain); player->addExperience(xpgain); } } if( (object->getType() == WAND || (object->getType() < 5 && object->flagIsSet(O_WEAPON_CASTS) && object->getMagicpower())) && (shots > 0.0) && (mrand(1,100) <= 50) ) { splvl = get_spell_lvl(object->getMagicpower()-1); if(player->isCt()) player->print("Spell level: %d.\n", splvl); if(splvl >= 4) { player->printColor("%O explodes in a retributive strike!\n", object); broadcast(player->getSock(), player->getRoom(), "%O explodes in a retributive strike!", object); player->delObj(object, true); delete object; } else { player->printColor("%O explodes!\n", object); broadcast(player->getSock(), player->getRoom(), "%O explodes!", object); broadcast(player->getSock(), player->getRoom(), "%M is engulfed by magical energy!", player); player->delObj(object, true); delete object; } broadcast(player->getSock(), player->getRoom(), "%M is engulfed by a magical vortex!", player); dmg = mrand(1,5); dmg += MAX(5,(mrand(splvl*2, splvl*6)))+((int)mtbf/2); if(player->chkSave(BRE, player, 0)) dmg /= 2; player->printColor("You take %s%d^x damage from the release of magical energy!\n", player->customColorize("*CC:DAMAGE*"), dmg); if(splvl >= 4) { cp = player->getRoom()->first_ply; while(cp) { if(cp->crt != player && !cp->crt->inCombat()) { dmg = mrand(splvl*4, splvl*8); if(cp->crt->chkSave(BRE, cp->crt, 0)) dmg /= 2; cp->crt->printColor("You take %s%d^x damage from the magical explosion!\n", cp->crt->customColorize("*CC:DAMAGE*"), dmg); /* cp->crt->hp.getCur() = MAX(1, cp->crt->hp.getCur()); */ /* if(cp->crt->hp.getCur() < 1) { // HAHAHA...Suicide wand explosion attacks to make people lose xp! die(cp->crt, EXPLOSION);// That'd be cool! but no...heh cp = cp->next_tag; continue; }*/ // Grin...suicide exploding wand pkill attacks! // At some risk to one's self though..heh if(player->doDamage(cp->crt, dmg, CHECK_DIE)) { cp = cp->next_tag; continue; } } cp = cp->next_tag; } } player->hp.decrease(dmg); if(player->hp.getCur() < 1) { player->die(EXPLOSION); return(0); } if(splvl >= 4) { if(mrand(1,100) <= 50) { newloc = getEtherealTravelRoom(); if(player->inJail()) newloc = player->parent_rom->info; else if(player->getRoom()->flagIsSet(R_ETHEREAL_PLANE)) newloc.id = 50; if(!loadRoom(newloc, &new_rom)) return(0); if(!player->getRoom()->flagIsSet(R_ETHEREAL_PLANE)) { broadcast(player->getSock(), player->getRoom(), "%M was blown from this universe by the explosion!", player); player->print("You are blown into an alternate dimension.\n"); } player->deleteFromRoom(); player->addToRoom(new_rom); player->doPetFollow(); } } return(0); } if(object->getType() == POISON && (player->getClass() == PALADIN || player->isCt())) { xpgain = object->getEffectStrength()*15; // no super fast leveling by breaking poison over and over. if(player->getLevel() < 9) xpgain = MIN(xpgain, player->getLevel()*10); if(!player->halftolevel()) { player->print("You %s %d experience for your deed.\n", gConfig->isAprilFools() ? "lose" : "gain", xpgain); player->addExperience(xpgain); } } } else { if(!(object->getType() == POISON && player->getClass() == PALADIN)) { player->printColor("You were unable to break %P.\n", object); broadcast(player->getSock(), player->getRoom(), "%M tried to break %P.", player, object); } else { player->printColor("You were unable to properly dispose of the %P.\n", object); broadcast(player->getSock(), player->getRoom(), "%M tried to dispose of the %P.", player, object); chance = 30 - (bonus((int)player->dexterity.getCur())); if(player->immuneToPoison()) chance = 101; if(mrand(1,100) <= chance) { player->printColor("^r^#You accidentally poisoned yourself!\n"); broadcast(player->getSock(), player->getRoom(), "%M accidentally poisoned %sself!", player, player->himHer()); duration = standardPoisonDuration(object->getEffectDuration(), player->constitution.getCur()); if(player->chkSave(POI, player, -1)) duration = duration * 2 / 3; player->poison(player, object->getEffectStrength(), duration); } } player->updateAttackTimer(true, 40); } return(0); }