/*
* rogues.cpp
* Functions that deal with rouge like abilities
* ____ _
* | _ \ ___ __ _| |_ __ ___ ___
* | |_) / _ \/ _` | | '_ ` _ \/ __|
* | _ < __/ (_| | | | | | | \__ \
* |_| \_\___|\__,_|_|_| |_| |_|___/
*
* 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-2012 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 "dm.h"
#include "move.h"
#include "factions.h"
#include "calendar.h"
//*********************************************************************
// cmdPrepareForTraps
//*********************************************************************
// This function allows players to prepare themselves for traps that
// might be in the next room they enter. This way, they can hope to
// avoid a trap that they already know is there.
int cmdPrepareForTraps(Player* player, cmd* cmnd) {
long i=0, t = time(0);
player->clearFlag(P_AFK);
if(!player->ableToDoCommand())
return(0);
if(player->flagIsSet(P_PREPARED)) {
player->print("You've already prepared.\n");
return(0);
}
if(player->inCombat()) {
player->print("You are too busy trying to keep from dying.\n");
return(0);
}
i = LT(player, LT_PREPARE);
if(t < i) {
player->pleaseWait(i-t);
return(0);
}
player->lasttime[LT_PREPARE].ltime = t;
player->lasttime[LT_PREPARE].interval = player->isDm() ? 0:15;
player->print("You prepare yourself for traps.\n");
broadcast(player->getSock(), player->getParent(), "%M prepares for traps.", player);
player->setFlag(P_PREPARED);
if(player->isBlind())
player->clearFlag(P_PREPARED);
return(0);
}
//*********************************************************************
// cmdBribe
//*********************************************************************
// This function allows players to bribe monsters. If they give the
// monster enough money, it will leave the room. If not, the monster
// keeps the money and stays.
int cmdBribe(Player* player, cmd* cmnd) {
Monster *creature=0;
unsigned long amount=0;
Money cost;
player->clearFlag(P_AFK);
if(!player->ableToDoCommand())
return(0);
if(player->getClass() == BUILDER) {
player->print("You cannot do that.\n");
return(0);
}
if(cmnd->num < 2) {
player->print("Bribe whom?\n");
return(0);
}
if(cmnd->num < 3 || cmnd->str[2][0] != '$') {
player->print("Syntax: bribe <monster> $<amount>\n");
return(0);
}
creature = player->getParent()->findMonster(player, cmnd);
if(!creature) {
player->print("That is not here.\n");
return(0);
}
if(!Faction::willDoBusinessWith(player, creature->getPrimeFaction())) {
player->print("%M refuses to do business with you.\n", creature);
return(0);
}
if(creature->isPet()) {
player->print("%M is too loyal to %s for you to bribe %s.\n", creature,
creature->getMaster()->getCName(), creature->himHer());
creature->getMaster()->print("%M tried to bribe %N.\n", player, creature);
return(0);
}
amount = strtoul(&cmnd->str[2][1], 0, 0);
if(amount < 1 || amount > player->coins[GOLD]) {
player->print("Please enter the amount of the bribe.\n");
return(0);
}
cost.set(creature->getExperience()*50, GOLD);
cost = Faction::adjustPrice(player, creature->getPrimeFaction(), cost, true);
player->coins.sub(amount, GOLD);
gServer->logGold(GOLD_OUT, player, Money(amount, GOLD), creature, "Bribe");
if(amount < cost[GOLD] || creature->flagIsSet(M_PERMENANT_MONSTER)) {
player->print("%M takes your money, but stays.\n", creature);
broadcast(player->getSock(), player->getParent(), "%M tried to bribe %N.", player, creature);
creature->coins.add(amount, GOLD);
} else {
player->print("%M takes your money and leaves.\n", creature);
broadcast(player->getSock(), player->getParent(), "%M bribed %N.", player, creature);
log_immort(true, player, "%s bribed %s.\n", player->getCName(), creature->getCName());
creature->deleteFromRoom();
gServer->delActive(creature);
free_crt(creature);
}
return(0);
}
//*********************************************************************
// cmdGamble
//*********************************************************************
// Code for people to gamble money
int cmdGamble(Player* player, cmd* cmnd) {
if(!player->getRoomParent()->flagIsSet(R_CASINO) && !player->isCt()) {
player->print("You can't gamble here.\n");
return(0);
}
player->unhide();
player->print("Not implemented yet.\n");
return(0);
}
//*********************************************************************
// canSearch
//*********************************************************************
bool canSearch(const Player* player) {
if(!player->ableToDoCommand())
return(false);
if(player->isBlind()) {
player->print("You're blind! How can you do that?\n");
return(false);
}
if(player->flagIsSet(P_SITTING)) {
player->print("You must stand up first!\n");
return(false);
}
return(true);
}
//*********************************************************************
// doSearch
//*********************************************************************
void doSearch(Player* player, bool immediate) {
BaseRoom* room = player->getRoomParent();
int chance=0;
bool found=false, detectMagic = player->isEffected("detect-magic");
player->clearFlag(P_AFK);
if(!canSearch(player))
return;
player->unhide();
int level = (int)(player->getSkillLevel("search"));
// TODO: SKILLS: tweak the formula
chance = 15 + 5*bonus((int)player->piety.getCur()) + level*2;
chance = MIN(chance, 90);
if(player->getClass() == RANGER)
chance += level*8;
if(player->getClass() == WEREWOLF)
chance += level*7;
if(player->getClass() == DRUID)
chance += level*6;
if(player->getClass() == CLERIC && (
(player->getDeity() == KAMIRA && player->getAdjustedAlignment() >= NEUTRAL) ||
(player->getDeity() == ARACHNUS && player->alignInOrder())
) )
chance += level*3;
if(player->isStaff())
chance = 100;
for(Exit* ext : room->exits) {
if( (ext->flagIsSet(X_SECRET) && mrand(1,100) <= (chance + searchMod(ext->getSize()))) ||
(ext->isConcealed(player) && mrand(1,100) <= 5))
{
// canSee doesnt handle DescOnly
if(player->canSee(ext) && !ext->flagIsSet(X_DESCRIPTION_ONLY)) {
found = true;
player->printColor("You found an exit: %s^x.\n", ext->getCName());
if(ext->isWall("wall-of-fire"))
player->printColor("%s", ext->blockedByStr('R', "wall of fire", "wall-of-fire", detectMagic, true).c_str());
if(ext->isWall("wall-of-force"))
player->printColor("%s", ext->blockedByStr('s', "wall of force", "wall-of-force", detectMagic, true).c_str());
if(ext->isWall("wall-of-thorns"))
player->printColor("%s", ext->blockedByStr('o', "wall of thorns", "wall-of-thorns", detectMagic, true).c_str());
}
}
}
for(Object* obj : room->objects) {
if( obj->flagIsSet(O_HIDDEN) &&
player->canSee(obj) &&
mrand(1,100) <= (chance + searchMod(obj->getSize())) )
{
found = true;
player->printColor("You found %1P.\n", obj);
}
}
for(Player* ply : room->players) {
if( ply->flagIsSet(P_HIDDEN) &&
player->canSee(ply) &&
mrand(1,100) <= (chance + searchMod(ply->getSize())))
{
found = true;
player->print("You found %s hiding.\n", ply->getCName());
}
}
for(Monster* mons : room->monsters) {
if( mons->flagIsSet(M_HIDDEN) &&
player->canSee(mons) &&
mrand(1,100) <= (chance + searchMod(mons->getSize())))
{
found = true;
player->print("You found %1N hiding.\n", mons);
}
}
// reuse chance as chance to find herbs
// make it a 15-85% scale, range from 1-MAXALVL*10
chance = level * (MAXALVL*10 / (85-15)) + 15;
if(player->isStaff())
chance = 100;
if(chance >= mrand(1,100)) {
if(player->inAreaRoom() && player->getAreaRoomParent()->spawnHerbs()) {
player->print("You found some herbs!\n");
found = true;
}
}
if(found) {
if(immediate)
broadcast(player->getSock(), room, "%s found something!", player->upHeShe());
else
broadcast(player->getSock(), room, "%M found something!", player);
player->checkImprove("search", true);
} else {
player->print("You didn't find anything.\n");
player->checkImprove("search", false);
}
}
void doSearch(const DelayedAction* action) {
doSearch(action->target->getAsPlayer(), false);
}
//*********************************************************************
// cmdSearch
//*********************************************************************
// This function allows a player to search a room for hidden objects,
// exits, monsters and players.
int cmdSearch(Player* player, cmd* cmnd) {
long i=0, t = time(0);
player->clearFlag(P_AFK);
if(!canSearch(player))
return(0);
if(gServer->hasAction(player, ActionSearch)) {
player->print("You are already searching!\n");
return(0);
}
i = LT(player, LT_SEARCH);
if(t < i) {
player->pleaseWait(i-t);
return(0);
}
player->lasttime[LT_SEARCH].ltime = t;
if( player->isDm() )
player->lasttime[LT_SEARCH].interval = 0;
else if(player->getClass() == RANGER || player->getClass() == WEREWOLF)
player->lasttime[LT_SEARCH].interval = 3;
else if((player->getClass() == CLERIC && player->getDeity() == KAMIRA && player->getAdjustedAlignment() >= NEUTRAL) || player->getClass() == THIEF || player->getClass() == ROGUE)
player->lasttime[LT_SEARCH].interval = 5;
else
player->lasttime[LT_SEARCH].interval = 7;
player->interruptDelayedActions();
broadcast(player->getSock(), player->getParent(), "%M searches the room.", player);
// big rooms take longer to search
if(player->getRoomParent()->getSize() >= SIZE_GARGANTUAN) {
// doSearch calls unhide, no need to do it twice
player->unhide();
player->print("You begin searching.\n");
gServer->addDelayedAction(doSearch, player, 0, ActionSearch, player->lasttime[LT_SEARCH].interval);
} else {
doSearch(player, true);
}
return(0);
}
//*********************************************************************
// spawnHerbs
//*********************************************************************
bool AreaRoom::spawnHerbs() {
if(!area)
return(false);
const TileInfo* tile = area->getTile(area->getTerrain(0, &mapmarker, 0, 0, 0, true), area->getSeasonFlags(&mapmarker));
if(!tile)
return(false);
return(tile->spawnHerbs(this));
}
//*********************************************************************
// spawnHerbs
//*********************************************************************
bool TileInfo::spawnHerbs(BaseRoom* room) const {
Object* object=0;
int max = herbs.size();
short num = mrand(1,MAX(1,max)), i=0, n=0, k=0;
std::list<CatRef>::const_iterator it;
bool found=false;
if(!max)
return(false);
// don't spawn if there's already herbs here
for(Object *obj : room->objects) {
if(obj->flagIsSet(O_DISPOSABLE))
return(false);
}
for(; i<num; i++) {
k = 1;
n = mrand(1, max);
for(it = herbs.begin() ; it != herbs.end() ; it++) {
if(k++==n)
break;
}
if(loadObject(*it, &object)) {
found = true;
object->setFlag(O_DISPOSABLE);
object->setDroppedBy(room, "SpawnHerb");
object->addToRoom(room);
}
}
return(found);
}
//*********************************************************************
// cmdHide
//*********************************************************************
// This command allows a player to try and hide themself in the shadows
// or it can be used to hide an object in a room.
int cmdHide(Player* player, cmd* cmnd) {
Object *object=0;
long i = LT(player, LT_HIDE), t = time(0);
int chance=0;
player->clearFlag(P_AFK);
if(!player->ableToDoCommand())
return(0);
if(!player->knowsSkill("hide")) {
player->print("You don't know how to hide effectively.\n");
return(0);
}
if(player->flagIsSet(P_SITTING)) {
player->print("You cannot do that while sitting down.\n");
return(0);
}
if(player->isEffected("mist")) {
player->print("You are already hidden as a mist.\n");
return(0);
}
if(i > t && !player->isCt()) {
player->pleaseWait(i-t);
return(0);
}
player->lasttime[LT_HIDE].ltime = t;
if( player->getClass() == THIEF ||
player->getClass() == ASSASSIN ||
player->getClass() == ROGUE ||
player->getClass() == RANGER
)
player->lasttime[LT_HIDE].interval = 5;
else
player->lasttime[LT_HIDE].interval = 15;
if(player->getSecondClass() == THIEF || player->getSecondClass() == ASSASSIN || (player->getClass() == CLERIC && player->getDeity() == KAMIRA))
player->lasttime[LT_HIDE].interval = 6L;
int level = (int)player->getSkillLevel("hide");
if(cmnd->num == 1) {
switch(player->getClass()) {
case THIEF:
if(player->getSecondClass() == MAGE) {
chance = MIN(90, 5 + 4*level + 3*bonus((int) player->dexterity.getCur()));
player->lasttime[LT_HIDE].interval = 8;
} else
chance = MIN(90, 5 + 6*level + 3*bonus((int) player->dexterity.getCur()));
break;
case ASSASSIN:
chance = MIN(90, 5 + 6*level + 3*bonus((int) player->dexterity.getCur()));
break;
case FIGHTER:
if(player->getSecondClass() == THIEF)
chance = MIN(90, 5 + 4*level + 3*bonus((int) player->dexterity.getCur()));
else
chance = MIN(90, 5 + 2*level + 3*bonus((int) player->dexterity.getCur()));
break;
case MAGE:
if(player->getSecondClass() == ASSASSIN || player->getSecondClass() == THIEF) {
chance = MIN(90, 5 + 4*level + 3*bonus((int) player->dexterity.getCur()));
player->lasttime[LT_HIDE].interval = 8;
} else
chance = MIN(90, 5 + 2*level +
3*bonus((int) player->dexterity.getCur()));
break;
case CLERIC:
if(player->getSecondClass() == ASSASSIN) {
chance = MIN(90, 5 + 5*level + 3*bonus((int) player->dexterity.getCur()));
} else if( (player->getDeity() == KAMIRA && player->getAdjustedAlignment() >= NEUTRAL) ||
(player->getDeity() == ARACHNUS && player->alignInOrder())
) {
chance = MIN(90, 5 + 4*level + 3*bonus((int) player->piety.getCur()));
} else
chance = MIN(90, 5 + 2*level + 3*bonus((int) player->dexterity.getCur()));
break;
case RANGER:
case DRUID:
chance = 5 + 10*level + 3*bonus((int) player->dexterity.getCur());
break;
case ROGUE:
chance = MIN(90, 5 + 5*level + 3*bonus((int) player->dexterity.getCur()));
break;
default:
chance = MIN(90, 5 + 2*level + 3*bonus((int) player->dexterity.getCur()));
break;
}
if(player->isStaff())
chance = 101;
if(player->isEffected("camouflage") && player->getRoomParent()->isOutdoors())
chance += 20;
if( (player->getRoomParent()->flagIsSet(R_DARK_AT_NIGHT) && !isDay()) ||
player->getRoomParent()->flagIsSet(R_DARK_ALWAYS) ||
player->getRoomParent()->isEffected("dense-fog")
)
chance += 10;
if(player->dexterity.getCur()/10 < 9 && !(player->getClass() == CLERIC && player->getDeity() == KAMIRA))
chance -= 10*(9 - player->dexterity.getCur()/10); // Having less then average dex
player->print("You attempt to hide in the shadows.\n");
if((player->getClass() == RANGER || player->getClass() == DRUID) && !player->getRoomParent()->isOutdoors()) {
chance /= 2;
chance = MAX(25, chance);
player->print("You have trouble hiding while inside.\n");
}
if(player->inCombat())
chance = 0;
if(player->isBlind())
chance = MIN(chance, 20);
if(mrand(1,100) <= chance || player->isEffected("mist")) {
player->setFlag(P_HIDDEN);
player->print("You slip into the shadows unnoticed.\n");
player->checkImprove("hide", true);
} else {
player->unhide();
broadcast(player->getSock(), player->getParent(), "%M tries to hide in the shadows.", player);
player->checkImprove("hide", false);
}
return(0);
}
object = player->getRoomParent()->findObject(player, cmnd, 1);
if(!object) {
player->print("You don't see that here.\n");
return(0);
}
player->unhide();
if( object->flagIsSet(O_NO_TAKE) &&
!player->checkStaff("You cannot hide that.\n")
)
return(0);
if(isGuardLoot(player->getRoomParent(), player, "%M will not let you hide that.\n"))
return(0);
if(player->isDm())
chance = 100;
else if(player->getClass() == THIEF || player->getClass() == ASSASSIN || player->getClass() == ROGUE)
chance = MIN(90, 10 + 5*level + 5*bonus((int) player->dexterity.getCur()));
else if(player->getClass() == RANGER || player->getClass() == DRUID)
chance = 5 + 9*level + 3*bonus((int) player->dexterity.getCur());
else
chance = MIN(90, 5 + 3*level + 3*bonus((int) player->dexterity.getCur()));
player->print("You attempt to hide it.\n");
broadcast(player->getSock(), player->getParent(), "%M attempts to hide %1P.", player, object);
if(mrand(1, 100) <= chance) {
object->setFlag(O_HIDDEN);
player->printColor("You tuck %1P into a corner.\n", object);
player->checkImprove("hide", true);
} else {
object->clearFlag(O_HIDDEN);
player->checkImprove("hide", false);
}
return(0);
}
//*********************************************************************
// doScout
//*********************************************************************
bool doScout(Player* player, const Exit* exit) {
BaseRoom *room=0;
if(exit->flagIsSet(X_STAFF_ONLY) && !player->isStaff()) {
player->print("A magical force prevents you from seeing into the room.\n");
return(false);
}
Move::getRoom(player, exit, &room, true);
if(player->getClass() == BUILDER && room && !room->isConstruction()) {
player->print("A magical force prevents you from seeing into the room.\n");
return(false);
}
if( room &&
room->isEffected("dense-fog")&&
!player->checkStaff("That room is filled with a dense fog.\n"))
return(false);
if(!room && exit->target.mapmarker.getArea()) {
Area* area = gConfig->getArea(exit->target.mapmarker.getArea());
if(!area) {
player->print("Off the map in that direction.\n");
if(player->isStaff())
player->printColor("^eArea does not exist.\n");
return(false);
}
// There is no room, but we're going to pretend there is.
player->print("\n");
if(area->name != "")
player->printColor("%s%s^x\n\n",
(!player->flagIsSet(P_NO_EXTRA_COLOR) && area->isSunlight(&exit->target.mapmarker) ? "^C" : "^c"),
area->name.c_str());
player->printColor("%s", area->showGrid(player, &exit->target.mapmarker, false).c_str());
player->printColor("^g%s exits: north, east, south, west, northeast, northwest, southeast, southwest.^w\n\n",
player->isStaff() ? "All" : "Obvious");
} else if(room) {
display_rom(player, room);
} else {
player->print("Off the map in that direction.\n");
if(player->isStaff())
player->printColor("^eRoom does not exist.\n");
return(false);
}
return(true);
}
//*********************************************************************
// cmdScout
//*********************************************************************
int cmdScout(Player* player, cmd* cmnd) {
Exit *exit=0;
int chance=0;
long t=0, i=0;
bool alwaysSucceed=false;
player->clearFlag(P_AFK);
if(!player->ableToDoCommand())
return(0);
if(!player->isStaff() && !player->knowsSkill("scout")) {
player->print("You lack the training to properly scout exits.\n");
return(0);
}
if(!player->isStaff() && player->isBlind()) {
player->printColor("^CYou're blind!\n");
return(0);
}
if( player->getRoomParent()->isEffected("dense-fog") &&
!player->checkStaff("This room is filled with a dense fog.\nYou cannot scout effectively.\n")
)
return(0);
if(cmnd->num < 2) {
player->print("Scout where?\n");
return(0);
}
i = player->lasttime[LT_SCOUT].ltime + player->lasttime[LT_SCOUT].interval;
t = time(0);
if(!player->isStaff() && t < i) {
player->pleaseWait(i - t);
return(0);
}
exit = findExit(player, Move::formatFindExit(cmnd), cmnd->val[1], player->getRoomParent());
if(!exit) {
player->print("You don't see that exit.\n");
player->lasttime[LT_SCOUT].ltime = t;
player->lasttime[LT_SCOUT].interval = 10L;
return(0);
}
alwaysSucceed = (exit->flagIsSet(X_CAN_LOOK) || exit->flagIsSet(X_LOOK_ONLY));
if( !alwaysSucceed &&
exit->flagIsSet(X_NO_SCOUT) &&
!player->checkStaff("You were unable to scout it.\n"))
return(0);
const Monster* guard = player->getRoomParent()->getGuardingExit(exit, player);
if(guard && !player->checkStaff("%M %s.\n", guard, guard->flagIsSet(M_FACTION_NO_GUARD) ? "doesn't like you enough to let you scout there" : "won't let you scout there"))
return(0);
if(!alwaysSucceed) {
player->lasttime[LT_SCOUT].ltime = t;
player->lasttime[LT_SCOUT].interval = 20L;
chance = 40 + (bonus((int) player->dexterity.getCur()) + (int)player->getSkillLevel("scout")) * 4;
if(exit->isEffected("wall-of-fire"))
chance -= 15;
if(exit->isEffected("wall-of-thorns"))
chance -= 15;
chance = MIN(85, chance);
if(!player->isStaff() && mrand(1, 100) > chance) {
player->print("You fail scout in that direction.\n");
player->checkImprove("scout", false);
return(0);
}
if( (exit->flagIsSet(X_LOCKED) || exit->flagIsSet(X_CLOSED) ) &&
!player->checkStaff("You cannot scout through a closed exit.\n")
)
return(0);
if( exit->flagIsSet(X_NEEDS_FLY) &&
!player->isEffected("fly") &&
!player->checkStaff("You must fly to scout there.\n")
)
return(0);
}
// can't scout where you can't go
if(exit->target.mapmarker.getArea()) {
Area *area = gConfig->getArea(exit->target.mapmarker.getArea());
if(!area) {
player->print("Off the map in that direction.\n");
if(player->isStaff())
player->printColor("^eArea does not exist.\n");
return(0);
}
if( !area->canPass(player, &exit->target.mapmarker, true) &&
!player->checkStaff("You cannot scout that way.\n") )
return(0);
}
if(!alwaysSucceed)
player->checkImprove("scout", true);
player->printColor("You scout the %s^x exit.\n", exit->getCName());
if(player->isStaff() && player->flagIsSet(P_DM_INVIS))
broadcast(isStaff, player->getSock(), player->getRoomParent(), "%M scouts the %s^x exit.", player, exit->getCName());
else if(exit->flagIsSet(X_SECRET) || exit->isConcealed() || exit->flagIsSet(X_DESCRIPTION_ONLY))
broadcast(player->getSock(), player->getParent(), "%M scouts the area.", player);
else
broadcast(player->getSock(), player->getParent(), "%M scouts the %s^x exit.", player, exit->getCName());
doScout(player, exit);
return(0);
}
//*********************************************************************
// cmdEnvenom
//*********************************************************************
// This will allow an assassin to put poison on a weapon and use it to
// poison other players and monsters. -- TC
int cmdEnvenom(Player* player, cmd* cmnd) {
Object *weapon=0, *object=0;
player->clearFlag(P_AFK);
if(!player->ableToDoCommand())
return(0);
if(!player->knowsSkill("envenom")) {
player->print("You lack the training to envenom your weapons.\n");
return(0);
}
if(player->isBlind()) {
player->printColor("^CYou can't do that! You're blind!\n");
return(0);
}
if(cmnd->num < 3) {
player->print("Syntax: envenom (weapon) (poison)\n");
return(0);
}
weapon = player->findObject(player, cmnd, 1);
if(!weapon) {
player->print("You do not have that weapon in your inventory.\n");
return(0);
}
if(weapon->flagIsSet(O_ENVENOMED)) {
player->printColor("%O is already envenomed.\n", weapon);
return(0);
}
if(weapon->getShotsCur() < 1) {
player->print("You cannot envenom a broken weapon.\n");
return(0);
}
bstring category = weapon->getWeaponCategory();
if(category != "slashing" && category != "piercing"&&
!player->checkStaff("You can only envenom slashing and piercing weapons.\n"))
return(0);
object = player->findObject(player, cmnd, 2);
if(!object) {
player->print("You do not have that poison in your inventory.\n");
return(0);
}
if(object->getType() != POISON) {
player->print("That is not poison.\n");
return(0);
}
if(object->getShotsCur() < 1) {
player->printColor("%O is all used up.\n", object);
return(0);
}
player->printColor("You envenom %P with %P.\n", weapon, object);
player->checkImprove("envenom", true);
broadcast(player->getSock(), player->getParent(), "%M envenoms %P with %P.", player, weapon, object);
// TODO: make poison more powerful with better envenom skill
object->decShotsCur();
weapon->setFlag(O_ENVENOMED);
if(object->getEffect() != "")
weapon->setEffect(object->getEffect());
else
weapon->setEffect("poison");
weapon->setEffectDuration(object->getEffectDuration()); // Sets poison duration.
weapon->setEffectStrength(object->getEffectStrength()); // Sets poison tick damage.
// Did this because some poisons are better then others, and assassins
// will be buying the poison. The more expensive, the better it is. Having
// them have to buy it makes it more of a pain for some idiot to go around
// poisoning everyone with a small knife just for fun.
weapon->lasttime[LT_ENVEN].ltime = time(0);
weapon->lasttime[LT_ENVEN].interval = 60*(mrand(3,5)+weapon->getAdjustment());
return(0);
}
//*********************************************************************
// cmdShoplift
//*********************************************************************
// This function will allow thieves to steal from shops.
bool isValidShop(const UniqueRoom* shop, const UniqueRoom* storage);
int cmdShoplift(Player* player, cmd* cmnd) {
UniqueRoom *room=0, *storage=0;
Object *object=0, *object2=0;
int chance=0, guarded=0;
if(!player->ableToDoCommand())
return(0);
if(!needUniqueRoom(player))
return(0);
if(player->flagIsSet(P_SITTING)) {
player->print("You have to stand up first.\n");
return(0);
}
if(player->getClass() == BUILDER)
return(0);
room = player->getUniqueRoomParent();
if(!player->isCt() && player->getLevel() < 7 && player->getClass() != THIEF) {
player->print("You couldn't possibly succeed. Wait until level 7.\n");
return(0);
}
if(player->getClass() == THIEF && player->getLevel() < 4) {
player->print("You couldn't possibly succeed. Wait until level 4.\n");
return(0);
}
if(player->getClass() == PALADIN) {
player->print("Paladins do not allow themselves to fall to the level of petty theft.\n");
return(0);
}
if(player->isEffected("berserk")) {
player->print("You can't shoplift while your going berserk! Go break something!\n");
return(0);
}
if(!room->flagIsSet(R_SHOP)) {
player->print("This is not a shop; there's nothing to shoplift.\n");
return(0);
}
if(!room->flagIsSet(R_CAN_SHOPLIFT)) {
player->print("Someone surely would see you if you tried that here.\n");
return(0);
}
if(player->inCombat()) {
player->print("You can't shoplift in the middle of combat!\n");
return(0);
}
if(player->isEffected("mist")) {
player->print("You can't shoplift while you are a mist.\n");
return(0);
}
loadRoom(shopStorageRoom(room), &storage);
if(!isValidShop(room, storage)) {
player->print("This is not a shop; there's nothing to shoplift.\n");
return(0);
}
storage->addPermCrt();
for(Monster* mons : storage->monsters) {
if(mons->getLevel() >= 10 && mons->flagIsSet(M_PERMENANT_MONSTER))
guarded++;
}
// This is done in order that a catastrophic building mistake cannot be made.
// If someone forgot to perm mobs in the storeroom, and there were no perms
// in the shop itself to guard the items, then players would be able to steal
// all they wanted from the shop all day long whenever they wanted to.
if(guarded < 2) {
player->print("Someone surely would see you if you tried that here.\n");
return(0);
}
// This has to be done to prevent baiting.
// The mobs will wander after a time.
for(Monster* mons : room->monsters) {
if(mons->flagIsSet(M_ATTACKING_SHOPLIFTER) && !player->isDm()) {
player->print("The shopkeep or shop's guards are too alert right now.\n");
return(0);
}
}
if(cmnd->num < 2) {
player->print("Shoplift what?\n");
return(0);
}
object = storage->findObject(player, cmnd, 1);
if(!object) {
player->print("That item isn't on display.\n");
return(0);
}
// if(!strcmp(object->getCName(), "lottery ticket"))
// {
// player->print("Shoplifting a lottery ticket would bring down the wrath of the gods.\n");
// return(0);
// }
if(object->getName() == "storage room") {
player->print("You can't shoplift that!\n");
return(0);
}
if(object->flagIsSet(O_NO_SHOPLIFT)) {
player->print("That item is too well guarded for you to shoplift.\n");
return(0);
}
if(object->getActualWeight() > 50) {
player->print("That object is too heavy and bulky to shoplift.\n");
return(0);
}
if(player->getWeight() + object->getActualWeight() > player->maxWeight()) {
player->print("That would be too much for you to carry.\n");
return(0);
}
player->unhide();
switch (player->getClass()) {
case THIEF:
case CARETAKER:
case DUNGEONMASTER:
chance = ((player->getLevel()+35 - (2*object->getActualWeight())) - (object->value[GOLD]/1500))
+ (bonus((int) player->dexterity.getCur())*2);
break;
case ASSASSIN:
case BARD:
case ROGUE:
chance = ((player->getLevel()+20 - (2*object->getActualWeight())) - (object->value[GOLD]/1500))
+ (bonus((int) player->dexterity.getCur())*2);
break;
// Casting classes just aren't cut out for it.
case MAGE:
case LICH:
case DRUID:
case CLERIC:
chance = (((int)player->getLevel() - (2*object->getActualWeight())) - (object->value[GOLD]/1500))
+ (bonus((int) player->dexterity.getCur())*2);
default:
chance = (((int)player->getLevel()+10 - (2*object->getActualWeight())) - (object->value[GOLD]/1500))
+ (bonus((int) player->dexterity.getCur())*2);
break;
}
// Armor makes a lot of noise
if(object->getType() == ARMOR && object->getWearflag() != FINGER)
chance -= 10;
// An invisible person would have it easier.
if(player->isInvisible())
chance += 5;
chance = MIN(70, MAX(2,chance));
if(player->isCt())
player->print("Chance: %d%\n", chance);
if(player->isDm())
chance = 101;
player->statistics.attemptSteal();
player->interruptDelayedActions();
if(mrand(1,100) > chance) {
player->setFlag(P_CAUGHT_SHOPLIFTING);
player->print("You were caught!\n");
broadcast(player->getSock(), room, "%M tried to shoplift %1P.\n%M was seen!", player, object, player);
player->smashInvis();
// Activates lag protection.
if(player->flagIsSet(P_LAG_PROTECTION_SET))
player->setFlag(P_LAG_PROTECTION_ACTIVE);
for(Monster* mons : room->monsters) {
if(mons->flagIsSet(M_PERMENANT_MONSTER)) {
gServer->addActive(mons);
mons->clearFlag(M_DEATH_SCENE);
mons->clearFlag(M_FAST_WANDER);
mons->addPermEffect("detect-invisible");
mons->addPermEffect("true-sight");
mons->setFlag(M_BLOCK_EXIT);
mons->setFlag(M_ATTACKING_SHOPLIFTER);
mons->addEnemy(player, true);
mons->diePermCrt();
}
}
MonsterSet::iterator mIt = storage->monsters.begin();
while(mIt != storage->monsters.end()) {
Monster *mons = (*mIt++);
if(mons->flagIsSet(M_PERMENANT_MONSTER)) {
if(!storage->players.empty())
broadcast(mons->getSock(), mons->getRoomParent(),
"%M runs out after a shoplifter.", mons);
else
gServer->addActive(mons);
mons->clearFlag(M_PERMENANT_MONSTER); // This is all done to prevent people from getting
mons->clearFlag(M_FAST_WANDER); // killed mostly by others baiting these mobs in.
mons->clearFlag(M_DEATH_SCENE);
mons->addPermEffect("detect-invisible");
mons->addPermEffect("true-sight");
mons->setFlag(M_BLOCK_EXIT);
mons->setFlag(M_WILL_ASSIST);
mons->setFlag(M_WILL_BE_ASSISTED);
mons->clearFlag(M_AGGRESSIVE_GOOD);
mons->clearFlag(M_AGGRESSIVE_EVIL);
mons->clearFlag(M_AGGRESSIVE);
mons->setFlag(M_OUTLAW_AGGRO);
mons->setFlag(M_ATTACKING_SHOPLIFTER);
mons->setExperience(10);
mons->coins.zero();
mons->addEnemy(player, true);
mons->diePermCrt();
mons->deleteFromRoom();
mons->addToRoom(room);
}
}
// prevents shoptlift ; flee
player->stun(mrand(3,5));
} else {
object2 = new Object;
if(!object2)
merror("shoplift", FATAL);
*object2 = *object;
object2->clearFlag(O_PERM_INV_ITEM);
object2->clearFlag(O_PERM_ITEM);
object2->clearFlag(O_TEMP_PERM);
object2->setFlag(O_WAS_SHOPLIFTED);
object2->setDroppedBy(room, "Shoplift");
player->addObj(object2);
player->printColor("You manage to conceal %1P on your person.\n", object2);
broadcast(isCt, player->getSock(), player->getRoomParent(), "*DM* %M just shoplifted %1P.", player, object2);
player->statistics.steal();
}
return(0);
}
//*********************************************************************
// cmdBackstab
//*********************************************************************
// This function allows thieves and assassins to backstab a monster.
// If successful, a damage multiplier is given to the player. The
// player must be successfully hidden for the backstab to work. If
// the backstab fails, then the player is forced to wait double the
// normal amount of time for their next attack.
int cmdBackstab(Player* player, cmd* cmnd) {
Player *pTarget=0;
Creature* target=0;
int n=0;
int disembowel=0, dur=0, dmg=0;
float stabMod=0.0, cap=0.0;
Damage damage;
if(!player->ableToDoCommand())
return(0);
if(!player->knowsSkill("backstab")) {
player->print("You don't know how to backstab.\n");
return(0);
}
if(player->isBlind()) {
player->print("Backstab what?\n");
return(0);
}
Object* weapon = player->ready[WIELD - 1];
if(!weapon && !player->checkStaff("Backstabing requires a weapon.\n"))
return(0);
bstring category = weapon->getWeaponCategory();
if( category != "slashing" && category != "piercing" &&
!player->checkStaff("Backstabing requires a slashing or piercing weapon.\n")
)
return(0);
if( weapon && weapon->flagIsSet(O_NO_BACKSTAB) &&
!player->checkStaff("%O cannot be used to backstab.\n", weapon)
)
return(0);
if(!player->checkAttackTimer())
return(0);
if(player->checkHeavyRestrict("backstab"))
return(0);
if(!(target = player->findVictim(cmnd, 1, true, false, "Backstab what?\n", "You don't see that here.\n")))
return(0);
if(target)
pTarget = target->getAsPlayer();
if(!pTarget && target->getAsMonster()->isEnemy(player)) {
player->print("Not while you're already fighting %s.\n", target->himHer());
return(0);
}
if(!player->canAttack(target))
return(0);
player->updateAttackTimer();
if(player->dexterity.getCur() > 180)
player->modifyAttackDelay(-10);
player->print("You attempt to backstab %N.\n", target);
target->print("%M attempts to backstab you!\n", player);
broadcast(player->getSock(), target->getSock(), player->getParent(), "%M attempts to backstab %N.", player, target);
if(target->isMonster())
target->getAsMonster()->addEnemy(player);
if(player->breakObject(player->ready[WIELD-1], WIELD)) {
broadcast(player->getSock(), player->getParent(), "%s backstab failed.", player->upHisHer());
player->setAttackDelay(player->getAttackDelay()*2);
return(0);
}
int skillLevel = (int)((player->getWeaponSkill(weapon) + (player->getSkillGained("backstab")*2)) / 3);
AttackResult result = player->getAttackResult(target, weapon, DOUBLE_MISS, skillLevel);
if(result == ATTACK_HIT || result == ATTACK_CRITICAL || result == ATTACK_BLOCK || result == ATTACK_GLANCING) {
if(!player->isHidden() && !player->isEffected("mist"))
result = ATTACK_MISS;
if(!pTarget && target->flagIsSet(M_NO_BACKSTAB))
result = ATTACK_MISS;
}
player->smashInvis();
player->unhide();
int level = (int)player->getSkillLevel("backstab");
bstring with = Statistics::damageWith(player, player->ready[WIELD-1]);
if(player->isDm() && result != ATTACK_CRITICAL)
result = ATTACK_HIT;
player->statistics.swing();
if(result == ATTACK_HIT || result == ATTACK_CRITICAL || result == ATTACK_BLOCK || result == ATTACK_GLANCING) {
// int dmg = 0;
// Progressive backstab code for thieves and assassins....
if(player->getClass() == THIEF || player->getSecondClass() == THIEF) {
if(level < 10)
stabMod = 2.75;
else if(level < 16)
stabMod = 3.25;
else if(level < 22)
stabMod = 3.75;
else if(level < 28)
stabMod = 4.5;
else if(level < 36)
stabMod = 5.0;
else if(level < 40)
stabMod = 5.5;
else
stabMod = 5.75;
} else if(player->getClass() == ASSASSIN || player->getSecondClass() == ASSASSIN) {
if(level < 10)
stabMod = 3.25;
else if(level < 13)
stabMod = 3.75;
else if(level < 16)
stabMod = 4;
else if(level < 19)
stabMod = 4.25;
else if(level < 22)
stabMod = 4.5;
else if(level < 25)
stabMod = 5.0;
else if(level < 28)
stabMod = 5.25;
else if(level < 30)
stabMod = 5.5;
else if(level < 34)
stabMod = 6.0;
else if (level < 38)
stabMod = 6.25;
else
stabMod = 6.5;
} else
stabMod = 5.25;
if( player->getSecondClass() == THIEF &&
(player->getClass() == FIGHTER || player->getClass() == MAGE)
) {
cap = 3.0;
}
if( player->getSecondClass() == ASSASSIN ||
(player->getClass() == MAGE && player->getSecondClass() == THIEF)
) {
cap = 4.0;
}
if(cap > 0)
stabMod = tMIN<float>(cap, stabMod-1);
// Version 2.43c Update -- I'm multiplying the stabMod by 2.0 to compensate for the increase
// in armor absorb, and the removal of multiplier for attackPower
stabMod *= 2.0;
// Return of 1 means the weapon was shattered or otherwise rendered unsuable
int drain = 0;
bool wasKilled = false, meKilled = false;
if(player->computeDamage(target, weapon, ATTACK_BACKSTAB, result, damage, true, drain, stabMod) == 1) {
player->unequip(WIELD, UNEQUIP_DELETE);
weapon = NULL;
player->computeAttackPower();
}
damage.includeBonus();
n = damage.get();
if(result == ATTACK_BLOCK) {
player->printColor("^C%M partially blocked your attack!\n", target);
target->printColor("^CYou manage to partially block %N's attack!\n", player);
}
if(result == ATTACK_CRITICAL)
player->statistics.critical();
else
player->statistics.hit();
if(target->isPlayer())
target->getAsPlayer()->statistics.wasHit();
disembowel = target->hp.getCur() * 2;
player->printColor("You backstabbed %N for %s%d^x damage.\n", target, player->customColorize("*CC:DAMAGE*").c_str(), damage.get());
player->checkImprove("backstab", true);
log_immort(false,player, "%s backstabbed %s for %d damage.\n", player->getCName(), target->getCName(), damage.get());
target->printColor( "^M%M^x backstabbed you%s for %s%d^x damage.\n", player,
target->isBrittle() ? "r brittle body" : "", target->customColorize("*CC:DAMAGE*").c_str(), damage.get());
broadcastGroup(false, target, "^M%M^x backstabbed ^M%N^x for *CC:DAMAGE*%d^x damage, %s%s\n",
player, target, damage.get(), target->heShe(), target->getStatusStr(damage.get()));
player->statistics.attackDamage(damage.get(), with);
if( weapon && weapon->getMagicpower() &&
weapon->flagIsSet(O_WEAPON_CASTS) &&
mrand(1,100) <= 50 && weapon->getChargesCur() > 0)
{
n += player->castWeapon(target, weapon, meKilled);
}
if( weapon &&
( (weapon->flagIsSet(O_ENVENOMED) && player->getClass() == ASSASSIN && level >=7) ||
(weapon->flagIsSet(O_ENVENOMED) && player->isCt())
)
)
n += player->checkPoison(target, weapon);
meKilled = player->doReflectionDamage(damage, target) || meKilled;
player->doDamage(target, damage.get(), NO_CHECK);
wasKilled = target->hp.getCur() < 1;
// Check for disembowel
if(wasKilled && n > disembowel && mrand(1,100) < 50) {
switch(mrand(1,4)) {
case 1:
player->printColor("^cYou completely disemboweled %N! %s's dead!\n", target, target->upHeShe());
broadcast(player->getSock(), player->getParent(), "%M completely disembowels %N! %s's dead!",
player, target, target->upHeShe());
if(target->isPlayer())
target->print("%M completely disemboweled you! You're dead!\n", player);
break;
case 2:
player->printColor("^cYou impaled %N through %s back! %s's dead!\n",
target, target->hisHer(), target->upHeShe());
broadcast(player->getSock(), player->getParent(), "%M impales %N through %s back! %s's dead!",
player, target, target->hisHer(), target->upHeShe());
if(target->isPlayer())
target->print("%M impaled you through the back! You're dead!\n", player);
break;
case 3:
if(weapon) {
player->printColor("^cThe %s went completely through %N! %s's dead!\n",
weapon->getCName(), target, target->upHeShe());
broadcast(player->getSock(), player->getParent(), "%M's %s goes completely through %N! %s's dead!",
player, weapon->getCName(), target, target->upHeShe());
if(target->isPlayer())
target->print("%M's %s sticks out of your chest! You're dead!\n",
player, weapon->getCName());
}
break;
case 4:
player->printColor("^cYou cut %N in half from behind! %s's dead!\n",
target, target->upHeShe());
broadcast(player->getSock(), player->getParent(), "%M cut %N in half from behind! %s's dead!",
player, target, target->upHeShe());
if(target->isPlayer())
target->print("%M cut you in half from behind! You're dead!\n", player);
break;
}
}
if(weapon)
weapon->decShotsCur();
Creature::simultaneousDeath(player, target, false, false);
// only monsters flee, and not when their attacker was killed
if(!wasKilled && !meKilled && target->getAsMonster()) {
if(target->flee() == 2)
return(0);
}
} else if(result == ATTACK_MISS) {
player->statistics.miss();
if(target->isPlayer())
target->getAsPlayer()->statistics.wasMissed();
player->print("You missed.\n");
player->checkImprove("backstab", false);
broadcast(player->getSock(), player->getParent(), "%s backstab failed.", player->upHisHer());
player->setAttackDelay(mrand(30,90));
} else if(result == ATTACK_DODGE) {
target->dodge(player);
} else if(result == ATTACK_PARRY) {
int parryResult = target->parry(player);
if(parryResult == 2) {
// oh no, we died from a riposte...
return(0);
}
} else if(result == ATTACK_FUMBLE) {
player->statistics.fumble();
player->printColor("^gYou FUMBLED your weapon.\n");
broadcast(player->getSock(), player->getParent(), "^g%M fumbled %s weapon.", player, player->hisHer());
if(weapon->flagIsSet(O_ENVENOMED)) {
if(!player->immuneToPoison() &&
!player->chkSave(POI, player, -5) && !induel(player, target->getAsPlayer())
) {
player->printColor("^G^#You poisoned yourself!!\n");
broadcast(player->getSock(), player->getParent(), "%M poisoned %sself!!",
player, player->himHer());
if(weapon->getEffectStrength()) {
dmg = (mrand(1,3) + (weapon->getEffectStrength()/10));
player->hp.decrease(dmg);
player->print("You take %d damage as the poison takes effect!\n", dmg);
}
weapon->clearFlag(O_ENVENOMED);
if(player->hp.getCur() < 1) {
n = 0;
player->addObj(weapon);
weapon = 0;
player->computeAttackPower();
player->die(POISON_PLAYER);
return(0);
}
dur = standardPoisonDuration(weapon->getEffectDuration(), player->constitution.getCur());
player->poison(player, weapon->getEffectStrength(), dur);
} else {
player->print("You almost poisoned yourself!\n");
broadcast(player->getSock(), player->getParent(), "%M almost poisoned %sself!", player, player->himHer());
}
}
n = 0;
player->addObj(weapon);
player->ready[WIELD-1] = 0;
weapon = 0;
player->computeAttackPower();
player->computeAttackPower();
} else {
player->printColor("^RError!!! Unhandled attack result: %d\n", result);
}
return(0);
}
//*********************************************************************
// checkPoison
//*********************************************************************
// This will check for envenom poisoning against the target with the given weapon
int Player::checkPoison(Creature* target, Object* weapon) {
if(!weapon)
return(0);
int dmg = 0;
int level = (int)getSkillLevel("backstab");
int poisonchance = 50+((level - (target->getLevel()-1))*10);
poisonchance -= (2*bonus(target->constitution.getCur()));
if(target->mFlagIsSet(M_WILL_POISON))
poisonchance /= 2;
poisonchance = MAX(0,MIN(poisonchance, 80));
// Can't poison them if they're immune to poison
if(target->immuneToPoison())
poisonchance = 0;
// No poisoning people if they're in a duel
if(induel(this, target->getAsPlayer()))
poisonchance = 0;
// If they're already poisoned, don't poison them again
if(target && target->isPoisoned())
poisonchance = 0;
// Less of a chance to poison a perm
if(!target && target->flagIsSet(M_PERMENANT_MONSTER))
poisonchance = MIN(poisonchance, 10);
// We can always poison someone if we're a ct
if(isCt())
poisonchance = 101;
if(mrand(1,100) <= poisonchance && !target->chkSave(POI, this, -1)) {
// 75% chance to use up the poison
if(mrand (1,100) > 25)
weapon->clearFlag(O_ENVENOMED);
printColor("^gYou poisoned %N!\n", target);
target->printColor( "^g^#%M poisoned you!!\n", this);
broadcast(getSock(), target->getSock(), target->getRoomParent(), "%M poisoned %N!", this, target);
unsigned int dur = standardPoisonDuration(weapon->getEffectDuration(), target->constitution.getCur());
if(weapon->getEffectStrength()) {
dmg = (mrand(1,3) + (weapon->getEffectStrength()/10));
printColor("^gThe poison did ^G%d^g onset damage!\n", dmg);
target->printColor("^gYou take ^G%d^g damage as the poison takes effect!\n", dmg);
}
target->poison(this, weapon->getEffectStrength(), dur);
}
return(dmg);
}
//*********************************************************************
// cmdAmbush
//*********************************************************************
// This function allows rogues to ambush their opponents from
// hiding. It is similar to backstab, but somewhat weakened and
// more limited. -- TC
int cmdAmbush(Player* player, cmd* cmnd) {
Player *pCreature=0;
Creature* creature=0;
if(!player->ableToDoCommand())
return(0);
if(!player->knowsSkill("ambush")) {
player->print("You don't know how to ambush people!\n");
return(0);
}
if(player->isBlind()) {
player->print("Ambush whom?\n");
return(0);
}
if(!player->isCt()) {
if( !player->ready[WIELD-1] ||
!player->ready[HELD-1] ||
player->ready[HELD-1]->getType() != WEAPON
) {
player->print("You must have two weapons wielded to properly ambush.\n");
return(0);
}
if(!player->flagIsSet(P_HIDDEN) && !player->isEffected("mist") && !player->isCt()) {
player->print("How do you expect to ambush when you aren't hiding?\n");
return(0);
}
if(!player->checkAttackTimer())
return(0);
if(player->checkHeavyRestrict("ambush"))
return(0);
}
if(!(creature = player->findVictim(cmnd, 1, true, false, "Ambush whom?\n", "You don't see that here.\n")))
return(0);
if(creature)
pCreature = creature->getAsPlayer();
if(!pCreature && creature->getAsMonster()->isEnemy(player) && !player->isCt()) {
player->print("Not while you're already fighting %s.\n",
creature->himHer());
return(0);
}
if(!player->canAttack(creature))
return(0);
player->smashInvis();
if(creature->isMonster()) {
creature->getAsMonster()->addEnemy(player);
}
player->updateAttackTimer();
if(player->dexterity.getCur() > 180)
player->modifyAttackDelay(-10);
player->print("You ambush %N!\n", creature);
broadcast(player->getSock(), creature->getSock(), player->getRoomParent(), "%M ambushes %N!", player, creature);
creature->print("%M ambushes you!\n", player);
player->unhide();
player->attackCreature(creature, ATTACK_AMBUSH);
return(0);
}
//*********************************************************************
// cmdPickLock
//*********************************************************************
// This function is called when a thief or assassin attempts to pick a
// lock. If the lock is pickable, there is a chance (depending on the
// player's level) that the lock will be picked.
int cmdPickLock(Player* player, cmd* cmnd) {
Exit *exit=0;
long i=0, t=0;
int chance=0;
player->clearFlag(P_AFK);
if(!player->ableToDoCommand())
return(0);
if(!player->knowsSkill("pick")) {
player->print("You don't know how to pick locks.\n");
return(0);
}
if(!player->isCt()) {
if(player->flagIsSet(P_SITTING)) {
player->print("You have to stand up first.\n");
return(0);
}
if(player->isBlind()) {
player->printColor("^CHow can you pick a lock when blind?\n");
return(0);
}
}
if(cmnd->num < 2) {
player->print("Pick what?\n");
return(0);
}
exit = findExit(player, cmnd);
if(!exit) {
player->print("You don't see that here.\n");
return(0);
}
const Monster* guard = player->getRoomParent()->getGuardingExit(exit, player);
if(guard && !player->checkStaff("%M %s.\n", guard, guard->flagIsSet(M_FACTION_NO_GUARD) ? "doesn't like you enough to let you pick it" : "won't let you pick it"))
return(0);
if(!exit->flagIsSet(X_LOCKED)) {
player->print("It's not locked.\n");
return(0);
}
if(exit->isWall("wall-of-force")) {
player->printColor("The %s^x is blocked by a wall of force.\n", exit->getCName());
return(0);
}
// were they killed by exit effect damage?
if(exit->doEffectDamage(player))
return(0);
player->unhide();
if(!player->isCt()) {
i = LT(player, LT_PICKLOCK);
t = time(0);
if(t < i) {
player->pleaseWait(i-t);
return(0);
}
player->lasttime[LT_PICKLOCK].ltime = t;
player->lasttime[LT_PICKLOCK].interval = (player->getClass() == ROGUE ? 20 : 10);
}
int level = (int)player->getSkillLevel("pick");
if(player->getClass() == THIEF)
chance = 10*(level - exit->getLevel()) + (2*bonus((int)player->dexterity.getCur()));
else if(player->getSecondClass() == THIEF && (player->getClass() == MAGE || player->getClass() == FIGHTER) )
chance = 5*(level - exit->getLevel()) + (2*bonus((int)player->dexterity.getCur()));
else if(player->getClass() == ROGUE || (player->getClass() == THIEF && player->getSecondClass() == MAGE))
chance = 7*(level - exit->getLevel()) + (2*bonus((int)player->dexterity.getCur()));
else
chance = 2*(level - exit->getLevel()) + (2*bonus((int)player->dexterity.getCur()));
if(exit->flagIsSet(X_UNPICKABLE))
chance = 0;
if((exit->getLevel() > level) && !player->isCt()) {
player->print("The lock's mechanism is currently beyond your experience.\n");
chance = 0;
}
chance = MAX(0, chance);
if(player->isCt())
chance = 101;
broadcast(player->getSock(), player->getParent(), "%M attempts to pick the %s^x.", player, exit->getCName());
if(mrand(1,100) <= chance) {
log_immort(false, player, "%s picked the %s in room %s.\n", player->getCName(), exit->getCName(),
player->getRoomParent()->fullName().c_str());
player->print("You successfully picked the lock.\n");
player->checkImprove("pick", true);
exit->clearFlag(X_LOCKED);
broadcast(player->getSock(), player->getParent(), "%s succeeded.", player->upHeShe());
Hooks::run(player, "succeedPickExit", exit, "succeedPickByCreature");
} else {
player->print("You failed.\n");
player->checkImprove("pick", false);
Hooks::run(player, "failPickExit", exit, "failPickByCreature");
}
return(0);
}
//*********************************************************************
// cmdPeek
//*********************************************************************
// This function allows a thief to peek at the inventory of
// another player. If successful, they will be able to see it and
// another roll is made to see if they get caught.
int cmdPeek(Player* player, cmd* cmnd) {
Creature* creature=0;
Player *pCreature=0;
Monster* mCreature=0;
bstring str = "";
long i=0, t=0;
int chance=0, goldchance=0, ok=0;
player->clearFlag(P_AFK);
if(!player->ableToDoCommand())
return(0);
if(!player->isStaff() && !player->knowsSkill("peek")) {
player->print("You don't know how to peek at other people's inventories.\n");
return(0);
}
if(cmnd->num < 2) {
player->print("Peek at who?\n");
return(0);
}
int level = (int)player->getSkillLevel("peek");
if(player->isBlind()) {
player->printColor("^CYou can't do that! You're blind!\n");
return(0);
}
creature = player->getParent()->findCreature(player, cmnd);
if(!creature || creature == player) {
player->print("That person is not here.\n");
return(0);
}
mCreature = creature->getAsMonster();
pCreature = creature->getAsPlayer();
if(player->getClass() == BUILDER) {
if(pCreature) {
player->print("You cannot peek players.\n");
return(0);
}
if(!player->canBuildMonsters() && !player->canBuildObjects())
return(cmdNoAuth(player));
if(!player->checkBuilder(player->getUniqueRoomParent())) {
player->print("Error: Room number not inside any of your alotted ranges.\n");
return(0);
}
if(mCreature && mCreature->info.id && !player->checkBuilder(mCreature->info)) {
player->print("Error: Monster not inside any of your alotted ranges.\n");
return(0);
}
}
if(!pCreature && player->isStaff())
return(dmMobInventory(player, cmnd));
if(pCreature && pCreature->isEffected("mist")) {
player->print("You cannot peek at the inventory of a mist.\n");
return(0);
}
if(player->checkHeavyRestrict("peek"))
return(0);
i = LT(player, LT_PEEK);
t = time(0);
if(i > t && !player->isStaff()) {
player->pleaseWait(i-t);
return(0);
}
player->lasttime[LT_PEEK].ltime = t;
player->lasttime[LT_PEEK].interval = 5;
if(!player->isCt() && creature->isStaff()) {
player->print("You failed.\n");
return(0);
}
if(!pCreature && (creature->flagIsSet(M_CANT_BE_STOLEN_FROM) || creature->flagIsSet(M_TRADES) || creature->flagIsSet(M_CAN_PURCHASE_FROM)) && !player->isDm()) {
player->print("%M manages to keep %s inventory hidden from you.\n", creature, creature->hisHer());
return(0);
}
if(cmnd->num > 2 && pCreature && (player->getClass() == THIEF || player->isCt())) {
peek_bag(player, pCreature, cmnd, 0);
return(0);
}
if(!ok && (player->getClass() == THIEF || player->isStaff()))
chance = (25 + level*10)-(creature->getLevel()*5);
else
chance = (level*10)-(creature->getLevel()*5);
if(chance<0)
chance=0;
if(creature->getLevel() >= level + 6)
chance = 0;
if(player->isStaff())
chance=100;
if(mrand(1,100) > chance) {
player->print("You failed.\n");
if(mrand(1,50) <= 50)
player->unhide();
player->checkImprove("peek", false);
return(0);
}
chance = MIN(90, (player->getClass() == ASSASSIN ? (level*4):(15 + level*5)));
if(mrand(1,100) > chance && !player->isStaff()) {
creature->print("%M peeked at your inventory.\n", player);
broadcast(player->getSock(), creature->getSock(), player->getRoomParent(),
"%M peeked at %N's inventory.", player, creature);
player->print("%s noticed!\n", creature->upHeShe());
} else {
player->print("%s's oblivious to your rummaging.\n",
creature->upHeShe());
}
player->checkImprove("peek", true);
str = creature->listObjects(player, player->isStaff());
if(str != "")
player->printColor("%s is carrying: %s.\n", creature->upHeShe(), str.c_str());
else
player->print("%s isn't holding anything.\n", creature->upHeShe());
goldchance = (5+(level*5)) - creature->getLevel();
goldchance = MIN(MAX(1,goldchance),90);
if(player->isCt())
goldchance = 101;
if(mrand(1,100) <= goldchance && !pCreature && !creature->flagIsSet(M_PERMENANT_MONSTER))
player->print("%s has %ld gold coins.\n", creature->upHeShe(), creature->coins[GOLD]);
return(0);
}
//*********************************************************************
// peek_bag
//*********************************************************************
int peek_bag(Player* player, Player* target, cmd* cmnd, int inv) {
Object *container=0;
bstring str = "";
int chance=0;
if(!player->isStaff()) {
int level = (int)player->getSkillLevel("peek");
if(level < 10) {
player->print("You are are not experienced enough at peeking to do that.\n");
return(0);
}
chance = (5 + level*10)-(target->getLevel()*5);
if(chance<0)
chance=0;
if(target->getLevel() >= level + 6)
chance = 0;
if(mrand(1,100) > chance) {
player->print("You failed.\n");
player->checkImprove("peek", false);
return(0);
}
}
container = target->findObject(player, cmnd, 2);
if(!container) {
player->print("%s doesn't have that.\n", target->upHeShe());
return(0);
}
if(container->getType() != CONTAINER) {
player->print("That isn't a container.\n");
return(0);
}
if(!inv) {
if(mrand(1,100) > chance && !player->isStaff()) {
player->print("You manage to peek inside %N's %s.\n", target, container->getCName());
target->print("%M managed to peek in your %s!\n", player, container->getCName());
broadcast(player->getSock(), target->getSock(), player->getParent(), "%M peeked at %N's inventory.",
player, target);
player->print("%s noticed!\n", target->upHeShe());
} else {
player->print("You manage to peek inside %N's %s.\n", target, container->getCName());
player->print("%s's oblivious to your rummaging.\n", target->upHeShe());
}
player->checkImprove("peek", true);
}
if(container->getType() == CONTAINER) {
str = container->listObjects(player, false);
if(str != "")
player->printColor("It contains: %s.\n", str.c_str());
else
player->print("It is empty.\n");
}
return(0);
}