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