/* * combatSystem.cpp * Functions for the new combat mechanics * ____ _ * | _ \ ___ __ _| |_ __ ___ ___ * | |_) / _ \/ _` | | '_ ` _ \/ __| * | _ < __/ (_| | | | | | | \__ \ * |_| \_\___|\__,_|_|_| |_| |_|___/ * * This software is distributed in accordance with 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 <sstream> #include "commands.h" #include "specials.h" #include "effects.h" //********************************************************************** // computeAttackPower //********************************************************************** int Player::computeAttackPower() { attackPower = 0; switch(cClass) { case FIGHTER: case DUNGEONMASTER: if(cClass2 == MAGE) attackPower = (strength.getCur() * 2) + (level); else if(cClass2 == THIEF) attackPower = (strength.getCur() * 2) + (level * 4); else attackPower = (strength.getCur() * 2) + (level * 8); break; case BERSERKER: attackPower = (strength.getCur() * 2) + (level * 8); break; case PALADIN: case DEATHKNIGHT: // TODO: This might need some tweaking with pray attackPower = strength.getCur() + piety.getCur() + (level * 4); break; case BARD: case WEREWOLF: case PUREBLOOD: attackPower = (strength.getCur() * 2) + (level * 4); break; case RANGER: case THIEF: case ASSASSIN: case ROGUE: case MONK: attackPower = strength.getCur() + dexterity.getCur() + (level * 4); break; case DRUID: attackPower = strength.getCur() + (level * 2); break; case CLERIC: attackPower = strength.getCur(); if(cClass2 == ASSASSIN) attackPower += dexterity.getCur(); switch(deity) { case CERIS: attackPower += (level); break; case ARES: attackPower += (level * 8); break; case ENOCH: case GRADIUS: case ARAMON: case ARACHNUS: default: attackPower += (level * 2); break; } break; case LICH: case MAGE: attackPower = strength.getCur(); if(cClass2 == THIEF || cClass2 == ASSASSIN) attackPower += dexterity.getCur() + (level*2); break; default: attackPower = strength.getCur(); break; } // Check equipment here for higher attack power return(attackPower); } //**************************************************************************** // getBaseDamage //**************************************************************************** unsigned int Creature::getBaseDamage() const { return(getAttackPower()/15); } //**************************************************************************** // getDamageReduction //**************************************************************************** // Returns the damage reduction factor when attacking the given target float Creature::getDamageReduction(const Creature* target) const { float reduction = 0.0; float targetArmor = target->getArmor(); float myLevel = level == 1 && isPlayer() ? 2 : level; reduction = ((targetArmor)/((targetArmor) + 43.24 + 18.38*myLevel)); reduction = MIN(.75, reduction); // Max of 75% reduction return(reduction); } //********************************************************************** // getWeaponType //********************************************************************** const bstring Object::getWeaponType() const { if(!this || type != WEAPON) return("none"); else return(subType); } //********************************************************************** // getArmorType //********************************************************************** const bstring Object::getArmorType() const { if(!this || type != ARMOR) return("none"); else return(subType); } //********************************************************************** // getWeaponCategory //********************************************************************** const bstring Object::getWeaponCategory() const { if(!this) return("none"); SkillInfo* weaponSkill = gConfig->getSkill(subType); if(type != WEAPON || !weaponSkill) return("none"); bstring category = weaponSkill->getGroup(); // Erase the 'weapons-' to leave the category category.erase(0, 8); return(category); } //********************************************************************** // getWeaponVerb //********************************************************************** const bstring Object::getWeaponVerb() const { const bstring category = getWeaponCategory(); if(category == "crushing") return("crush"); else if(category == "piercing") return("stab"); else if(category == "slashing") return("slash"); else if(category == "ranged") return("shoot"); else if(category == "chopping") return("chop"); else return("thwack"); } //********************************************************************** // getWeaponVerbPlural //********************************************************************** const bstring Object::getWeaponVerbPlural() const { const bstring category = getWeaponCategory(); if(category == "crushing") return("crushes"); else if(category == "piercing") return("stabs"); else if(category == "slashing") return("slashes"); else if(category == "ranged") return("shoots"); else if(category == "chopping") return("chops"); else return("thwacks"); } //********************************************************************** // getWeaponVerbPast //********************************************************************** const bstring Object::getWeaponVerbPast() const { const bstring category = getWeaponCategory(); if(category == "crushing") { if(mrand(0,1)) return("crushed"); else return("bludgeoned"); } else if(category == "piercing") { if(mrand(0,1)) return("impaled"); else return("stabbed"); } else if(category == "slashing") { if(mrand(0,1)) return("gutted"); else return("slashed"); } else if(category == "ranged") return("shot"); else if(category == "chopping") { if(mrand(0,1)) return("chopped"); else return("cleaved"); } else return("hit"); } //********************************************************************** // needsTwoHands //********************************************************************** bool Object::needsTwoHands() const { const bstring weaponType = getWeaponType(); if(flagIsSet(O_TWO_HANDED)) return(true); if( weaponType == "great-axe" || weaponType == "great-mace" || weaponType == "great-hammer" || weaponType == "staff" || weaponType == "great-sword" || weaponType == "polearm" ) return(true); if((weaponType == "bow" || weaponType == "crossbow") && !flagIsSet(O_SMALL_BOW)) return(true); return(false); } //********************************************************************** // setWeaponType //********************************************************************** bool Object::setWeaponType(const bstring& newType) { SkillInfo* weaponSkill = gConfig->getSkill(newType); bstring category = ""; if(weaponSkill) { category = weaponSkill->getGroup(); category = category.left(6); } if(!weaponSkill || category != "weapon") { printf("Invalid weapon type: %s!\n", newType.c_str()); return(false); } subType = newType; return(true); } //********************************************************************** // setArmorType //********************************************************************** bool Object::setArmorType(const bstring& newType) { // Armor type must be ring, shield, or one of the armor type skills if(newType != "ring" && newType != "shield") { SkillInfo* weaponSkill = gConfig->getSkill(newType); bstring category = ""; if(weaponSkill) { category = weaponSkill->getGroup(); category = category.left(5); } if(!weaponSkill || category != "armor") { printf("Invalid armor type: %s!\n", newType.c_str()); return(false); } } subType = newType; return(true); } //********************************************************************** // setSubType //********************************************************************** bool Object::setSubType(const bstring& newType) { // These must be set with the appropriate function if(type == ARMOR || type == WEAPON) return(false); subType = newType; return(true); } //********************************************************************** // getWeaponSkill //********************************************************************** int Monster::getWeaponSkill(const Object* weapon) const { // Same skills for all weapons return(weaponSkill); } //********************************************************************** // getWeaponSkill //********************************************************************** int Player::getWeaponSkill(const Object* weapon) const {; int bonus = 0; // Bless improves your chance to hit if(isEffected("bless")) bonus += 10; // print("Looking at weapon skill for %s.\n", weapon ? weapon->getCName() : "null object"); bstring weaponType; if(weapon) weaponType = weapon->getWeaponType(); else weaponType = getUnarmedWeaponSkill(); // we're very confused about what type of weapon this is if(weaponType == "") weaponType = "bare-hand"; Skill* weaponSkill = getSkill(weaponType); if(!weaponSkill) return(0); else return(weaponSkill->getGained() + bonus); } //********************************************************************** // getDefenseSkill //********************************************************************** int Monster::getDefenseSkill() const { return(defenseSkill); } //********************************************************************** // getDefenseSkill //********************************************************************** int Player::getDefenseSkill() const { int bonus = 0; // Protection makes you harder to hit if(isEffected("protection")) bonus += 10; Skill* defenseSkill = getSkill("defense"); if(!defenseSkill) return(-1); else return(defenseSkill->getGained() + bonus); } //********************************************************************** // AttackResult //********************************************************************** // defense - skill // 295 - 300 = -5 * .01% less chance to miss // 320 - 300 = 20 * .01% more chance to miss AttackResult Creature::getAttackResult(Creature* victim, const Object* weapon, int resultFlags, int altSkillLevel) { /* int pFd; if(isPlayer()) pFd = fd; else pFd = victim->getSock(); */ int mySkill=0; if(altSkillLevel != -1) { // For when we want to look at an alternative skill, like kick mySkill = altSkillLevel; } else { mySkill = getWeaponSkill(weapon); } int defense = victim->getDefenseSkill(); int difference = defense - mySkill; double missChance=0; double dodgeChance=0; double parryChance=0; double glancingChance=0; double blockChance=0; double criticalChance=0; double fumbleChance=0; //double hitChance=0; EffectInfo* effect=0; missChance = victim->getMissChance(difference); if(victim->isEffected("blur") && !isEffected("true-sight")) missChance += victim->getEffect("blur")->getStrength(); if(victim->getRoomParent()->isEffected("dense-fog")) { effect = victim->getRoomParent()->getEffect("dense-fog"); if(!effect->isOwner(this)) missChance += effect->getStrength(); } if(resultFlags & DOUBLE_MISS) missChance *= 2; int missCutoff = (int)(missChance * 100); if(resultFlags & NO_DODGE) dodgeChance = 0; else dodgeChance = victim->getDodgeChance(this, difference); int dodgeCutoff = (int)(missCutoff + (dodgeChance * 100)); if(resultFlags & NO_PARRY) parryChance = 0; else parryChance = victim->getParryChance(this, difference); int parryCutoff = (int)(dodgeCutoff + (parryChance * 100)); if(resultFlags & NO_GLANCING) glancingChance = 0; else glancingChance = victim->getGlancingBlowChance(this, difference); int glancingCutoff = (int)(parryCutoff + (glancingChance * 100)); if(resultFlags & NO_BLOCK) blockChance = 0; else blockChance = victim->getBlockChance(this, difference); int blockCutoff = (int)(glancingCutoff + (blockChance * 100)); if((resultFlags & NO_CRITICAL) || victim->immuneCriticals()) criticalChance = 0; else criticalChance = getCriticalChance(difference); int criticalCutoff = (int)(blockCutoff + (criticalChance * 100)); if(resultFlags & NO_FUMBLE) fumbleChance = 0; else fumbleChance = getFumbleChance(weapon); int fumbleCutoff = (int)(criticalCutoff + (fumbleChance * 100)); //hitChance = MAX(0, 100.0 - missChance - dodgeChance - parryChance - glancingChance - blockChance - criticalChance - fumbleChance); //int hitCutoff = (int)(fumbleCutoff + (hitChance * 100)); // Auto miss with an always crit weapon vs a no crit monster if(weapon && weapon->flagIsSet(O_ALWAYS_CRITICAL)) { if(victim->mFlagIsSet(M_NO_AUTO_CRIT) || victim->immuneCriticals()) missCutoff = 10000; else { missCutoff = dodgeCutoff = parryCutoff = glancingCutoff = blockCutoff = -1; criticalCutoff = 10000; } } // printColor("^y%d attack skill %d defense skill, %d difference.\n", mySkill, defense, difference); // printColor("^cChances: ^C%f^c miss, ^C%f^c dodge, ^C%f^c parry, ^C%f^c glancing, ^C%f^c block, ^C%f^c critical, ^C%f^c fumble, ^C%f^c leftover\n", // missChance, dodgeChance, parryChance, glancingChance, blockChance, criticalChance, fumbleChance, hitChance); // printColor("^gCutoffs: ^G%d^g miss, ^G%d^g dodge, ^G%d^g parry, ^G%d^g glancing, ^G%d^g block, ^G%d^g critical ^G%d^g fumble ^G%d^g hit.\n", // missCutoff, dodgeCutoff, parryCutoff, glancingCutoff, blockCutoff, criticalCutoff, fumbleCutoff, hitCutoff); // int randNum = mrand(0, 10000); AttackResult result; if(randNum < missCutoff) result = (ATTACK_MISS); else if(randNum < dodgeCutoff) result = (ATTACK_DODGE); else if(randNum < parryCutoff) result = (ATTACK_PARRY); else if(randNum < glancingCutoff) result = (ATTACK_GLANCING); else if(randNum < blockCutoff) result = (ATTACK_BLOCK); else if(randNum < criticalCutoff) result = (ATTACK_CRITICAL); else if(randNum < fumbleCutoff) result = (ATTACK_FUMBLE); else result = (ATTACK_HIT); if(result == ATTACK_HIT || result == ATTACK_CRITICAL) if(victim->kamiraLuck(this)) result = ATTACK_MISS; return(result); } //********************************************************************** // adjustChance //********************************************************************** int Creature::adjustChance(const int &difference) { double adjustment = 0; // defense - skill // 295 - 300 = -5 * .01% less chance to miss == negative difference, defense LOWER than weapon // 320 - 300 = 20 * .01% more chance to miss == positive difference, defense HIGHER than weapon // Now adjust it based on weapon skill & defense if(difference > 0) { // Defense is HIGHER than weapon skill if(isMonster()) { // Mobs get more out of higher defence than players if(difference > 10) adjustment = 0.4; else adjustment = 0.1; } else { adjustment = 0.1; } } else { // Rating < 0 // Weapon skill is HIGHER than defense skill if(isPlayer()) adjustment = 0.04; else adjustment = 0.04; } return((int)(difference * adjustment)); //chance += (difference * adjustment); } //********************************************************************** // getFumbleChance //********************************************************************** double Creature::getFumbleChance(const Object* weapon) { double chance = 2.0; if(!weapon || weapon->flagIsSet(O_CURSED) || isDm()) { chance = 0.0; } else { double weaponSkill = getSkillLevel(weapon->getWeaponType()); // Spread the reduction over all 300 skill points, leaving a .01 chance to fumble // even at 300 skill chance -= (weaponSkill / 151.0); chance = MAX(0.0, chance); } return(chance); } //********************************************************************** // getCriticalChance //********************************************************************** double Creature::getCriticalChance(const int& difference) { double chance = 5.0; chance -= adjustChance(difference); chance = MAX(0.0, chance); return(chance); } //********************************************************************** // getBlockChance //********************************************************************** double Creature::getBlockChance(Creature* attacker, const int& difference) { if(isPlayer()) { if(!knowsSkill("block")) return(0); // Players need a shield to block if(!ready[SHIELD-1]) return(0); } double chance = 5.0; chance += adjustChance(difference); // Max chance of 5.0% if a monster if(isMonster()) chance = MIN(5.0, chance); chance = MAX(0.0, chance); return(chance); } //********************************************************************** // getGlancingBlowChance //********************************************************************** // A glancing blow is the opposite of a critical hit; reduced damage // when a player or pet is attacking a monster but only against // monsters of the same level or higher double Creature::getGlancingBlowChance(Creature* attacker, const int& difference) { if(isPlayer() || isPet() || (attacker->isMonster() && !attacker->isPet())) return(0); if(level < attacker->getLevel()) return(0); double chance = 10.0 + (difference * 0.5); chance = MAX(0.0, chance); return(chance); } //********************************************************************** // getParryChance //********************************************************************** // Chance that they will parry or riposte an attack. // 50% of parry will be a riposte double Creature::getParryChance(Creature* attacker, const int& difference) { if(isPlayer()) { if(!canDodge(attacker) || !canParry(attacker)) return(0); if(!knowsSkill("parry")) return(0); } else { // if(level < 7) // return(false); } double chance = (dexterity.getCur() - 80) * .03; chance += adjustChance(difference); // Riposte is harder against some attacker types if( attacker->type == DRAGON || attacker->type == DEMON || attacker->type == DEVIL || attacker->type == GIANTKIN || attacker->type == DEVA || attacker->type == DINOSAUR || attacker->type == ELEMENTAL ) chance /= 2; int numEnm = 0; for(Monster* mons : getRoomParent()->monsters) { if(mons->isPet()) continue; if(mons->isEnemy(this)) if(numEnm++ > 1) chance -= .02; } chance = MAX(0.0, chance); return(chance); } //********************************************************************** // canParry //********************************************************************** bool Creature::canParry(Creature* attacker) { if(attacker->isDm()) return(false); if(isMonster()) return(true); if(!ready[WIELD - 1]) return(false); // must have a weapon wielded in order to riposte. bstring weaponCategory = ready[WIELD - 1]->getWeaponCategory(); bstring weaponType = ready[WIELD - 1]->getWeaponType(); if((weaponCategory != "piercing" && weaponCategory != "slashing") || weaponType == "polearm") return(false); // must use a piercing or slashing weapon to riposte if(ready[WIELD-1]->needsTwoHands()) return(false); // Cannot riposte with a two-handed weapon. int combatPercent=0; // If a this is overwhelmed by mass amounts of enemies, it is difficult if // not impossible to find an opening to move enough to do a parry. The following // code will calculate the chance of failure into a "combat percent". If a check // against this percent fails, the this did not find enough of an opening to // try a parry, so parry returns 0 without going off. -TC // +3% fail for every monster mad at this besides the one he's currently hitting combatPercent = 3*(tMAX(0,numEnemyMonInRoom(this)-1)); // Group members are assumed to fight together to help one another. // -2% fail for every member in the this's group besides themself in the same room if(getGroup()) combatPercent -= 2*(tMAX(0,getGroup()->getNumInSameRoom(this))); combatPercent = MAX(0,combatPercent); if(mrand(1,100) <= combatPercent) return(0); // Did not find a parry opening due to excessive combat. No parry. // Parry is not possible against some types of creatures if( attacker->type == INSECT || attacker->type == AVIAN || attacker->type == FISH || attacker->type == REPTILE || attacker->type == PLANT || attacker->type == ETHEREAL || attacker->type == ASTRAL || attacker->type == GASEOUS || attacker->type == ENERGY || attacker->type == PUDDING || attacker->type == SLIME ) return(false); long t = time(0); long i = LT(this, LT_RIPOSTE); if(t < i) { return(false); } if(ready[WIELD-1]->getShotsCur() < 1) // weapon must not be broken return(false); return(true); } //********************************************************************** // canDodge //********************************************************************** bool Creature::canDodge(Creature* attacker) { if(attacker->isDm()) return(false); if(attacker->isMonster()) { if(attacker->flagIsSet(M_UN_DODGEABLE)) return(false); if(attacker->flagIsSet(M_UNKILLABLE) && !isCt()) return(false); } if(isPlayer()) { if(flagIsSet(P_UNCONSCIOUS)) // Can't dodge while unconscious return(false); if(getRoomParent()->isUnderwater() && !flagIsSet(P_FREE_ACTION)) return(false); if(getRoomParent()->flagIsSet(R_NO_DODGE)) return(false); // Players cannot dodge if stunned in any way. // Circle, bash, maul, bite, and stun will all // prevent dodge from functioning. if(flagIsSet(P_STUNNED)) return(false); // Can't dodge what you cannot see if(!canSee(attacker)) return(false); // Players waiting and hiding will not dodge, now so they won't auto unhide themselves. if(flagIsSet(P_HIDDEN)) return(false); } return(true); } //********************************************************************** // getDodgeChance //********************************************************************** // What chance do we have to dodge the attacker? double Creature::getDodgeChance(Creature* attacker, const int& difference) { if(!canDodge(attacker)) return(false); // if(!knowsSkill("dodge")) // return(0); // double chance = 0.0; Player* pPlayer = getAsPlayer(); // Find the base chance if(pPlayer) { switch(cClass) { case RANGER: chance += (dexterity.getCur() * .06); break; case THIEF: chance += (dexterity.getCur() * .075); break; case ASSASSIN: chance += (dexterity.getCur() * .06); break; case ROGUE: chance += (dexterity.getCur() * .08); break; case FIGHTER: case BERSERKER: if(pPlayer->getSecondClass() == THIEF || pPlayer->getSecondClass() == ASSASSIN) chance += (dexterity.getCur() * .07); else chance += (1.0 + (dexterity.getCur() * .045)); break; case BARD: case PALADIN: case DEATHKNIGHT: case WEREWOLF: case PUREBLOOD: case MONK: chance += (2.0 + (dexterity.getCur() * .05)); break; case CLERIC: if(pPlayer->getSecondClass() == ASSASSIN) { chance += (dexterity.getCur() * .06); break; } switch(deity) { case KAMIRA: case ARACHNUS: chance += (piety.getCur() * .05); break; case CERIS: case ARES: case ENOCH: case GRADIUS: default: chance += (2.0 + dexterity.getCur() * .05); break; } break; case LICH: case MAGE: if(pPlayer->getSecondClass() == THIEF || pPlayer->getSecondClass() == ASSASSIN) chance += (dexterity.getCur() * .07); else chance += (1.0 + dexterity.getCur() * .06); break; default: break; } } else { // Not a player chance = 5.0; } // TODO: Adjust dodge based on armor weight chance += adjustChance(difference); chance = MAX(0.0, chance); return(chance); } //********************************************************************** // getMissChance //********************************************************************** // What chance do we have to be missed? double Creature::getMissChance(const int& difference) { // Base chance of 5% to miss double chance; if(difference > 10) chance = 7.0; else chance = 5.0; chance += adjustChance(difference); Player* pPlayer = getAsPlayer(); // Class modification for miss if(pPlayer) { switch(cClass) { case RANGER: case THIEF: case ASSASSIN: case ROGUE: chance *= 1.0; break; case FIGHTER: case BERSERKER: chance *= 0.7; break; case MONK: case PALADIN: case DEATHKNIGHT: case WEREWOLF: chance *= 0.8; break; case PUREBLOOD: case BARD: chance *= 0.9; break; case CLERIC: switch(deity) { case ARES: chance *= 0.8; break; case ARACHNUS: case KAMIRA: case CERIS: case ENOCH: case GRADIUS: chance *= 1.0; default: chance *= 1.0; break; } break; case LICH: case MAGE: if(pPlayer->getSecondClass() == THIEF || pPlayer->getSecondClass() == ASSASSIN) chance *= 0.8; else chance *= 1.0; break; } } else { // Not a player chance *= 1.0; } chance = MAX(0.0, chance); return(chance); } //********************************************************************** // setWeaponSkill //********************************************************************** void Monster::setWeaponSkill(int amt) { weaponSkill = amt; } //********************************************************************** // setDefenseSkill //********************************************************************** void Monster::setDefenseSkill(int amt) { defenseSkill = amt; } //********************************************************************** // canHit //********************************************************************** bool Creature::canHit(Creature* victim, Object* weapon, bool glow, bool showFail) { //Monster* mVictim = victim->getMonster(); Player* pVictim = victim->getAsPlayer(); if(isMonster()) { } else { // Player attacking something bool silver = false; bool focused = false; int enchant = 0; if(cClass == MONK && !weapon && flagIsSet(P_FOCUSED)) focused = true; if(cClass == MONK && !weapon && !ready[HELD-1] && ready[HANDS-1]) { enchant = abs(ready[HANDS-1]->getAdjustment()); weapon = ready[HANDS-1]; } else if(weapon) { enchant = abs(weapon->getAdjustment()); if(weapon->flagIsSet(O_SILVER_OBJECT)) silver = true; } if(pVictim) { // It's a player // DMs can hit vampires if(!isDm()) { if(pVictim->isEffected("mist")) { if(weapon) { if( !weapon->flagIsSet(O_CAN_HIT_MIST) && !flagIsSet(P_MISTBANE) ) { if(showFail) printColor("Your cannot hit a misted creature with your %1P.\n", weapon); return(false); } } else if(!flagIsSet(P_MISTBANE)) { if(showFail) print("You cannot physically hit a misted creature.\n"); return(false); } } } if(!isDm() && (// pVictim is a werewolf and higher than 10 and no silver (pVictim->isEffected("lycanthropy") && pVictim->getLevel() >=10 && !silver) || // or pVictim is a lich and higher than 7 (pVictim->getClass() == LICH && pVictim->getLevel() >=7 ) || // or they have enchant only flag set pVictim->flagIsSet(P_ENCHANT_ONLY) ) && // and the attacker is // Not a werewolf !isEffected("lycanthropy") && // and no weapon and not focused ( (!weapon && !focused) || // or weapon and has no adjustment (weapon && enchant < 1) ) ) { if(showFail) { if(weapon) { if(weapon->getType() == WEAPON) print("Your %s has no effect on %N.\n", weapon->getCName(), pVictim); else print("Your %s have no effect on %N.\n", weapon->getCName(), pVictim); } else print("Your attack has no effect on %N.\n", pVictim); pVictim->print("%M's attack on you was uneffective.\n", this); } return(false); } } else { // Player vs Monster // Check if we can hit the monster if( victim->flagIsSet(M_ENCHANTED_WEAPONS_ONLY) || victim->flagIsSet(M_PLUS_TWO) || victim->flagIsSet(M_PLUS_THREE) ) { // At night level 19+ wolves can hit if( isEffected("lycanthropy") && !isDay() && level > 19 && victim->flagIsSet(M_ENCHANTED_WEAPONS_ONLY) ) { if(glow) printColor("^WYour claws glow radiantly in the night against %N.\n", victim); } else if(cClass == MONK && flagIsSet(P_FOCUSED) && ( (level >= 16 && victim->flagIsSet(M_ENCHANTED_WEAPONS_ONLY)) || (level >= 16 && victim->flagIsSet(M_PLUS_TWO)) ) ) { if(glow) printColor("^WYour fists glow with power against %N.\n", victim); } else if(weapon && ( (victim->flagIsSet(M_ENCHANTED_WEAPONS_ONLY) && enchant > 0) || (victim->flagIsSet(M_PLUS_TWO) && enchant > 1) || (victim->flagIsSet(M_PLUS_THREE) && enchant > 2) ) ) { if(glow && weapon) { if(weapon->getType() == WEAPON) printColor("^WYour %s^W glows with power against %N.\n", weapon->getCName(), victim); else printColor("^WYour^W %s glow with power against %N.\n", weapon->getCName(), victim); } } else if(isDm()) { if(glow) printColor("^WYou ignore the laws of the land and attack %N anyway.\n", victim); } else { if(showFail) { if(weapon) { if(weapon->getType() == WEAPON) printColor("^cYour %s has no effect on %N.\n", weapon->getCName(), victim); else printColor("^cYour %s have no effect on %N.\n", weapon->getCName(), victim); } else printColor("^cYour attack has no effect on %N.\n", victim); } return(false); } } } } return(true); } //******************************************************************************** // computeDamage //******************************************************************************** // Returns 0 on normal computation, 1 if the weapon was shattered int Player::computeDamage(Creature* victim, Object* weapon, AttackType attackType, AttackResult& result, Damage& attackDamage, bool computeBonus, int& drain, float multiplier) { int retVal = 0; Damage bonusDamage; bstring weaponCategory = weapon->getWeaponCategory(); drain = 0; if(attackType == ATTACK_KICK) { if(computeBonus) bonusDamage.set(getBaseDamage()/2); attackDamage.set(mrand(2,6) + ::bonus(strength.getCur())); if((cClass == FIGHTER || cClass == MONK) && !cClass2) { attackDamage.add((int)(getSkillLevel("kick") / 4)); } } else if(attackType == ATTACK_MAUL) { if(computeBonus) bonusDamage.set(getBaseDamage()/2); attackDamage.set(( mrand( (int)(level/2), (int)(level + 1)) + (strength.getCur()/10))); attackDamage.add(mrand(2, 4)); } else { // Any non kick attack for now if(computeBonus) bonusDamage.set(getBaseDamage()); if(!weapon) { if(cClass == MONK) { attackDamage.set(mrand(1,2) + level/3 + mrand((1+level/4),(1+level)/2)); if(strength.getCur() < 90) { attackDamage.set(attackDamage.get() - (90-strength.getCur())/10); attackDamage.set(MAX(1,attackDamage.get())); } } else { attackDamage.set(damage.roll()); bonusDamage.set(bonusDamage.get() * 3 / 4); } } // TODO: Add in modifier based on weapon skill } if(weapon) attackDamage.add(weapon->damage.roll() + weapon->getAdjustment()); if(isEffected("lycanthropy")) attackDamage.add(packBonus()); attackDamage.set(MAX(1, attackDamage.get())); // Damage reduction on every hit if(cClass == PALADIN) { if(deity == GRADIUS) { if(getAdjustedAlignment() < PINKISH || getAdjustedAlignment() > BLUISH) { multiplier /= 2; print("Your dissonance with earth reduces your damage.\n"); } } else { if(getAlignment() < 0) { multiplier /= 2; print("Your evilness reduces your damage.\n"); } } } if(cClass == DEATHKNIGHT) { if(getAdjustedAlignment() > NEUTRAL) { multiplier /= 2; print("Your goodness reduces your damage.\n"); } } // Bonus spread out over entire series of attack for multi attack weapons if(computeBonus) { if(cClass == PALADIN) { int goodDmg = mrand(1, 1 + level / 3); if(deity == GRADIUS) { if(alignInOrder() && victim->getRace() != DWARF && victim->getDeity() != GRADIUS) { bonusDamage.add(goodDmg); print("Your attunement with earth increased your damage by %d.\n", goodDmg); } } else { if(getAdjustedAlignment() >= BLUISH && victim->getAlignment() <= 0) { bonusDamage.add(goodDmg); print("Your goodness increased your damage by %d.\n", goodDmg); } } } if(cClass == DEATHKNIGHT) { // Only drain on 1st attack if a multi weapon if(getAdjustedAlignment() <= REDDISH && victim->getAdjustedAlignment() >= NEUTRAL) drain = mrand(1, 1 + level / 3); } if( ( cClass == BERSERKER || (cClass == CLERIC && deity == ARES) || isStaff() ) && isEffected("berserk")) { // Only do a bonus if not a multi weapon, or is a multi weapon and it's the first attack if(weapon && weaponCategory != "ranged") bonusDamage.add((int)(attackDamage.get()/2)); } if( ( (cClass == DEATHKNIGHT && getAdjustedAlignment() <= REDDISH) || (cClass == PALADIN && getAdjustedAlignment() == ROYALBLUE) ) && (isEffected("pray") || isEffected("dkpray")) ) bonusDamage.add(mrand(1,3)); if((isEffected("lycanthropy") || isCt()) && isEffected("frenzy")) bonusDamage.add(mrand(3,5)); if((cClass == MONK || isCt()) && flagIsSet(P_FOCUSED)) bonusDamage.add(mrand(1,3)); } // If this is a monk, and we're not wielding a monk weapon // Or this is a werewolf, and we're not wielding a werewolf weapon or a claw weapon**** // **** Although I'm pretty sure werewolves can only use claw weapons now...but put here // in case this changes in the future if(attackType != ATTACK_KICK && (cClass == MONK || isEffected("lycanthropy"))) { if( weapon && ( (cClass == MONK && !weapon->flagIsSet(O_SEL_MONK)) || (isEffected("lycanthropy") && ( !weapon->flagIsSet(O_SEL_WEREWOLF) || weapon->getWeaponType() != "claw") ) ) ) { if(cClass == MONK) print("How can you attack well with your hands full?\n"); else print("How can you attack well with your paws full?\n"); multiplier /= 2; } } if(multiplier > 0.0) { attackDamage.set((int)((float)(attackDamage.get() * multiplier))); if(computeBonus) { if(attackType != ATTACK_BACKSTAB) bonusDamage.set((int)((float)bonusDamage.get() * multiplier)); // else // bonus = static_cast<int>(static_cast<float>(bonus) * (multiplier/2.0)); } } if(result == ATTACK_CRITICAL) { int mult=0; char atk[25]; switch(attackType) { case ATTACK_BASH: strcpy(atk, "bash"); break; case ATTACK_KICK: strcpy(atk, "kick"); break; default: strcpy(atk, "hit"); break; } printColor("^gCRITICAL %s!\n", atk); broadcast(getSock(), getRoomParent(), "^g%M made a critical %s.", this, atk); mult = mrand(3, 5); attackDamage.set(attackDamage.get() * mult); drain *= mult; // We'll go with the assumption that if you critical on the first hit, the remaining // ones in that series will hurt a little more, so this will work just fine // if the first of a series of hits is a critical if(computeBonus) bonusDamage.set(bonusDamage.get() * mult); broadcastGroup(false, victim, "^g%M made a critical %s!\n", this, atk); if( attackType != ATTACK_KICK && weapon && !isDm() && ( weapon->flagIsSet(O_ALWAYS_CRITICAL) || ( !weapon->flagIsSet(O_NEVER_SHATTER) && !weapon->flagIsSet(O_STARTING) && // using half-percentages here: // +0 = 3.5%, +1 = 2.5%, +2 = 1.5%, +3 = 0.5% mrand(1, 200) <= (7 - weapon->getAdjustment()*2) ) ) ) { printColor("^YYour %s shatters.\n", weapon->getCName()); broadcast(getSock(), getRoomParent(),"^Y%s %s shattered.", upHisHer(), weapon->getCName()); retVal = 1; } } else if(result == ATTACK_GLANCING) { printColor("^CYou only managed to score a glancing blow!\n"); broadcast(getSock(), getRoomParent(), "^C%M scored a glancing blow!", this); if(mrand(1,2)) { attackDamage.set(attackDamage.get() / 2); drain /= 2; if(computeBonus) bonusDamage.set(bonusDamage.get() / 2); } else { attackDamage.set(attackDamage.get() * 2 / 3); drain = (drain * 2) / 3; if(computeBonus) bonusDamage.set(bonusDamage.get() * 2 / 3); } } else if(result == ATTACK_BLOCK) { attackDamage.set(victim->computeBlock(attackDamage.get())); if(computeBonus) bonusDamage.set(victim->computeBlock(bonusDamage.get())); } victim->modifyDamage(this, PHYSICAL, attackDamage, NO_REALM, weapon, 0, computeBonus ? OFFGUARD_NOREMOVE : OFFGUARD_REMOVE); // Don't forget to modify the bonus if(computeBonus) { // Make bonus proportional to weapon speed, otherwise it's too much with fast weapons, and // too little with slow weapons. (Bonus / 3) * weapon speed bonusDamage.set((int)(((float)bonusDamage.get()/(float)DEFAULT_WEAPON_DELAY) * weapon->getWeaponDelay())); victim->modifyDamage(this, PHYSICAL, bonusDamage, NO_REALM, weapon, 0, OFFGUARD_NOPRINT, true); attackDamage.setBonus(bonusDamage); } if(retVal != 1) { // If we didn't shatter, minimum of 1 damage attackDamage.set(MAX(attackDamage.get(), 1)); } return(retVal); } //********************************************************************** // computeDamage //********************************************************************** int Monster::computeDamage(Creature* victim, Object* weapon, AttackType attackType, AttackResult& result, Damage& attackDamage, bool computeBonus, int& drain, float multiplier) { Damage bonusDamage; if(computeBonus) bonusDamage.set(getBaseDamage()); if(ready[WIELD-1]) attackDamage.add(ready[WIELD-1]->damage.roll() + ready[WIELD-1]->getAdjustment()); else attackDamage.add(damage.roll()); attackDamage.add(::bonus(strength.getCur())); if(result == ATTACK_CRITICAL) { broadcast(NULL, getRoomParent(), "%M made a critical hit.", this); int mult = mrand(2, 5); attackDamage.set(attackDamage.get() * mult); drain *= mult; // No shatter for mobs :) } else if(result == ATTACK_BLOCK) { attackDamage.set(victim->computeBlock(attackDamage.get())); if(computeBonus) { bonusDamage.set(victim->computeBlock(bonusDamage.get())); } } victim->modifyDamage(this, PHYSICAL, attackDamage); victim->modifyDamage(this, PHYSICAL, bonusDamage); attackDamage.setBonus(bonusDamage); attackDamage.set(MAX(1, attackDamage.get())); return(0); } //********************************************************************** // computeBlock //********************************************************************** int Creature::computeBlock(int dmg) { // TODO: Tweak amount of blockage based on the shield // for now, just half the damage dmg /= 2; return(dmg); } //********************************************************************** // dodge //********************************************************************** // This function prints the messages for dodging, chance is now // computed elsewhere int Creature::dodge(Creature* target) { int i = mrand(1,10); unhide(); smashInvis(); if(isPlayer()) { Player* player = getAsPlayer(); player->statistics.dodge(); player->increaseFocus(FOCUS_DODGE); } if(target->isPlayer()) target->getAsPlayer()->statistics.miss(); if(i == 1) { printColor("^cYou barely manage to dodge %N's attack.\n", target); broadcast(getSock(), target->getSock(), getRoomParent(), "%M barely dodges %N's attack.", this, target); if(target->isPlayer()) target->printColor("^c%M somehow manages to dodge your attack.\n", this); } else if(i == 2) { printColor("^cYou deftly dodge %N's attack.\n", target); broadcast(getSock(), target->getSock(), getRoomParent(), "%M deftly dodges %N's attack.", this, target); if(target->isPlayer()) target->printColor("^c%M deftly dodges your attack.\n", this); } else if(i == 3 && (target->isPlayer())) { printColor("^cYou side-step %N's attack and smack %s on the back of the head.\n", target, target->himHer()); broadcast(getSock(), target->getSock(), getRoomParent(), "%M side-steps %N's attack and smacks %s on the back of the head.", this, target, target->himHer()); if(target->isPlayer()) target->printColor("^c%M side-steps your attack and smacks you on the back of the head.\n", this); } else if(i > 3 && i < 6) { printColor("^cYou dance gracefully around %N's attack.\n", target); broadcast(getSock(), target->getSock(), getRoomParent(), "%M dances gracefully around %N's attack.", this, target); if(target->isPlayer()) target->printColor("^c%M dances gracefully around your attack.\n", this); } else if(i >= 6 && i < 8) { printColor("^cYou easily dodge %N's attack.\n", target); broadcast(getSock(), target->getSock(), getRoomParent(), "%M easily dodges %N's attack.", this, target); if(target->isPlayer()) target->printColor("^c%M easily dodges your attack.\n",this); } else if(i >= 8 && i < 9) { printColor("^cYou easily duck under %N's pitifully executed attack.\n", target); broadcast(getSock(), target->getSock(), getRoomParent(), "%M easily ducks under %N's pitifully executed attack.", this, target); if(target->isPlayer()) target->printColor("^c%M easily ducks under your pitifully executed attack.\n",this); } else if(i >= 9 && i <= 10) { printColor("^cYou laugh at %N as you easily dodge %s attack.\n", target, target->hisHer()); broadcast(getSock(), target->getSock(), getRoomParent(), "%M laughs as %s easily dodges %N's attack.", this, heShe(), target); if(target->isPlayer()) target->printColor("^c%M laughs as %s dodges your attack.\n", this, heShe()); } else { printColor("^cYou deftly dodge %N's attack.\n", target); broadcast(getSock(), target->getSock(), getRoomParent(), "%M deftly dodges %N's attack.", this, target); if(target->isPlayer()) target->printColor("^c%M deftly dodges your attack.\n",this); } return(1); } //********************************************************************** // canRiposte //********************************************************************** bool Creature::canRiposte() const { if(isPlayer()) return(true); if(flagIsSet(M_CAN_RIPOSTE)) return(true); return(level >= 15 && ( cClass == FIGHTER || cClass == BERSERKER || cClass == ASSASSIN || cClass == THIEF || cClass == ROGUE )); } //********************************************************************* // parry //********************************************************************* // This function allows a creature to either parry a blow, or to // strike back after parry. It was made for the Rogue class. -- TC // Returns 2 on death of person being reposited, returns 1 on non-death sucess // This is only called when success has already been determined int Creature::parry(Creature* target) { long t=0; Object* weapon = ready[WIELD-1]; Damage attackDamage; if(!weapon && isPlayer()) { broadcast(::isDm, "*** Parry error: called with null weapon for %s.\n", getCName()); return(0); } t = time(0); //i = LT(this, LT_RIPOSTE); if(isPlayer()) { t=time(0); lasttime[LT_RIPOSTE].ltime = t; switch(cClass) { case THIEF: case ASSASSIN: case FIGHTER: lasttime[LT_RIPOSTE].interval = 9L; break; default: lasttime[LT_RIPOSTE].interval = 6L; break; } } if(isMonster()) { unhide(); smashInvis(); } // If we get a hit, it's a riposte, otherwise just a parry AttackResult result; // If we're a player, or a monster that's over 15 and is a certain class, // we have a chance to riposte, otherwise only do a parry if(canRiposte()) result = getAttackResult(target, weapon, DOUBLE_MISS|NO_DODGE|NO_PARRY|NO_BLOCK|NO_CRITICAL|NO_FUMBLE|NO_GLANCING); else result = ATTACK_MISS; // Parry //checkImprove("parry", true); // if result == miss || can't hit target, parry if(result == ATTACK_MISS || !canHit(target, weapon, true, false)) { printColor("^cYou parry %N's attack.\n", target); broadcast(getSock(), target->getSock(), getRoomParent(), "%M parries %N's attack.", this, target); if(target->isPlayer()) target->printColor("^c%M parries your attack.\n", this); if(isPlayer()) { getAsPlayer()->increaseFocus(FOCUS_PARRY); } } else { // We have a riposte, calculate damage and such bstring verb = weapon->getWeaponVerb(); bstring verbPlural = weapon->getWeaponVerbPlural(); int drain=0; bool wasKilled = false, freeTarget = false, meKilled = false; //int enchant = 0; //if(weapon) // enchant = abs(weapon->adjustment); computeDamage(target, weapon, ATTACK_NORMAL, result, attackDamage, true, drain); attackDamage.add(attackDamage.getBonus()); // So mob riposte isn't soo mean if(isMonster()) { attackDamage.set(attackDamage.get() / 2); attackDamage.set(MAX(1, attackDamage.get())); } switch(mrand(1,7)) { case 1: printColor("^cYou side-step ^M%N's^c attack and %s %s for %s%d^c damage.\n", target, verb.c_str(), target->himHer(), customColorize("*CC:DAMAGE*").c_str(), attackDamage.get()); broadcast(getSock(), target->getSock(), getRoomParent(), "%M side-steps %N's attack and %s %s.", this, target, verbPlural.c_str(), target->himHer()); if(target->isPlayer()) target->printColor("^M%M^x side-steps your attack and %s you for %s%d^x damage.\n", this, verbPlural.c_str(), target->customColorize("*CC:DAMAGE*").c_str(), attackDamage.get()); break; case 2: printColor("^cYou lunge and viciously %s ^M%N^c for %s%d^c damage.\n", verb.c_str(), target, customColorize("*CC:DAMAGE*").c_str(), attackDamage.get()); broadcast(getSock(), target->getSock(), getRoomParent(), "%M lunges and viciously %s %N.", this, verbPlural.c_str(), target ); if(target->isPlayer()) target->printColor("^M%M^x lunges at you and viciously %s you for %s%d^x damage.\n", this, verbPlural.c_str(), target->customColorize("*CC:DAMAGE*").c_str(), attackDamage.get()); break; case 3: printColor("^cYou slide around ^M%N's^c attack and %s %s for %s%d^c damage.\n", target, verb.c_str(), target->himHer(), customColorize("*CC:DAMAGE*").c_str(), attackDamage.get()); broadcast(getSock(), target->getSock(), getRoomParent(), "%M slides around %N's attack and %s %s.", this, target, verbPlural.c_str(), target->himHer()); if(target->isPlayer()) target->printColor("^M%M^x slides around your attack and %s you for %s%d^x damage.\n", this, verbPlural.c_str(), target->customColorize("*CC:DAMAGE*").c_str(), attackDamage.get()); break; case 4: printColor("^cYou spin away from ^M%N^c's attack and %s %s for %s%d^c damage.\n", target, verb.c_str(), target->himHer(), customColorize("*CC:DAMAGE*").c_str(), attackDamage.get()); broadcast(getSock(), target->getSock(), getRoomParent(), "%M spins away from %N's attack and %s %s.", this, target, verbPlural.c_str(), target->himHer()); if(target->isPlayer()) target->printColor("^M%M^x spins away from your attack and %s you for %s%d^x damage.\n", this, verbPlural.c_str(), target->customColorize("*CC:DAMAGE*").c_str(), attackDamage.get()); break; case 5: printColor("^cYou duck under ^M%N's^c attack and %s %s for %s%d^c damage.\n", target, verb.c_str(), target->himHer(), customColorize("*CC:DAMAGE*").c_str(), attackDamage.get()); broadcast(getSock(), target->getSock(), getRoomParent(), "%M ducks under %N's attack and %s %s.", this, target, verbPlural.c_str(), target->himHer() ); if(target->isPlayer()) target->printColor("^M%M^x ducks under your attack and %s you for %s%d^x damage.\n", this, verbPlural.c_str(), target->customColorize("*CC:DAMAGE*").c_str(), attackDamage.get()); break; case 6: printColor("^cYou laugh at ^M%N^c and quickly %s %s for %s%d^c damage.\n", target, verb.c_str(), target->himHer(), customColorize("*CC:DAMAGE*").c_str(), attackDamage.get()); broadcast(getSock(), target->getSock(), getRoomParent(), "%M laughs at %N and quickly %s %s.", this, target, verbPlural.c_str(), target->himHer() ); if(target->isPlayer()) target->printColor("^M%M^x laughs at you and quickly %s you for %s%d^x damage.\n", this, verbPlural.c_str(), target->customColorize("*CC:DAMAGE*").c_str(), attackDamage.get()); break; case 7: printColor("^cYou riposte ^M%N's^c attack and %s %s for %s%d^c damage.\n", target, verb.c_str(), target->himHer(), customColorize("*CC:DAMAGE*").c_str(), attackDamage.get()); broadcast(getSock(), target->getSock(), getRoomParent(), "%M ripostes %N's attack and %s %s.", this, target, verbPlural.c_str(), target->himHer() ); if(target->isPlayer()) target->printColor("^M%M^x ripostes your attack and %s you for %s%d^x damage.\n", this, verbPlural.c_str(), target->customColorize("*CC:DAMAGE*").c_str(), attackDamage.get()); break; } if(weapon) { if(!mrand(0, 3)) weapon->decShotsCur(); // die check moved right before return. if(weapon->getShotsCur() <= 0) { printColor("%O just broke.\n", weapon); broadcast(getSock(), target->getSock(), getRoomParent(), "%M's just broke %P.", this, weapon); unequip(WIELD); } } if(target->isPlayer()) { Player* pCrt = target->getAsPlayer(); pCrt->updateAttackTimer(); } if(isPlayer()) { getAsPlayer()->increaseFocus(FOCUS_RIPOSTE, attackDamage.get(), target); } meKilled = doReflectionDamage(attackDamage, target); if( doDamage(target, attackDamage.get(), CHECK_DIE, PHYSICAL_DMG, freeTarget) || wasKilled || meKilled) { Creature::simultaneousDeath(this, target, false, freeTarget); return(2); } } return(1); } //********************************************************************** // autoAttackEnabled //********************************************************************** bool Player::autoAttackEnabled() const { return(!flagIsSet(P_NO_AUTO_ATTACK)); } //********************************************************************** // setFleeing //********************************************************************** void Player::setFleeing(bool pFleeing) { fleeing = pFleeing; } //********************************************************************** // autoAttackEnabled //********************************************************************** bool Player::isFleeing() const { return(fleeing); } //********************************************************************* // isHidden //********************************************************************* bool Creature::isHidden() const { return(flagIsSet(isPlayer() ? P_HIDDEN : M_HIDDEN)); } //********************************************************************* // negAuraRepel //********************************************************************* bool Creature::negAuraRepel() const { return(cClass == LICH && !isEffected("resist-magic")); } //********************************************************************* // doResistMagic //********************************************************************* int Creature::doResistMagic(int dmg, Creature* enemy) { float resist=0; dmg = MAX(1, dmg); if(negAuraRepel() && enemy && this != enemy) { resist = (10 + mrand(1,3) + level + bonus((int) constitution.getCur())) - (bonus((int)enemy->intelligence.getCur()) + bonus((int)enemy->piety.getCur())); resist /= 100; // percentage resist *= dmg; dmg -= (int)resist; } if(isEffected("resist-magic")) { resist = (piety.getCur() / 10 + intelligence.getCur() / 10) * 2; resist = MAX(50, MIN(resist, 100)); resist /= 100; // percentage resist *= dmg; dmg -= (int)resist; } return(MAX(0, dmg)); } //********************************************************************* // updateAttackTimer //********************************************************************* void Creature::updateAttackTimer(bool setDelay, int delay) { if(!setDelay) { attackTimer.update(); } else { if(delay != 0) attackTimer.update(delay); else { if(ready[HELD-1] && ready[HELD-1]->getWearflag() == WIELD) attackTimer.update(MAX(ready[WIELD-1]->getWeaponDelay(), ready[HELD-1]->getWeaponDelay())); else attackTimer.update(ready[WIELD-1]->getWeaponDelay()); } } } //********************************************************************* // getAttackDelay //********************************************************************* int Creature::getAttackDelay() const { return(attackTimer.getDelay()); } //********************************************************************* // checkAttackTimer //********************************************************************* // Return: true if the attack timer has expired, false if not bool Creature::checkAttackTimer(bool displayFail) { long i; if(!((i = attackTimer.getTimeLeft()) == 0) && !isDm()) { if(displayFail) pleaseWait(i/10.0); return(false); } return(true); } //********************************************************************* // modifyAttackDelay //********************************************************************* void Creature::modifyAttackDelay(int amt) { attackTimer.modifyDelay(amt); } //********************************************************************* // setAttackDelay //********************************************************************* void Creature::setAttackDelay(int newDelay) { attackTimer.setDelay(newDelay); } //********************************************************************* // pleaseWait //********************************************************************* void Creature::pleaseWait(long duration) const { if(duration < 0) duration *= -1; if(duration == 1) print("Please wait 1 more second.\n"); else if(duration > 60) print("Please wait %d:%02d minutes.\n", duration / 60L, duration % 60L); else print("Please wait %d more seconds.\n", duration); } void Creature::pleaseWait(int duration) const { pleaseWait((long)duration); } void Creature::pleaseWait(double duration) const { // Floating point equality is unreliable if(!(duration > 1) && !(duration < 1)) print("Please wait 1.0 more second.\n"); else print("Please wait %.1f more seconds.\n", duration); } //********************************************************************* // getLTAttack //********************************************************************* time_t Creature::getLTAttack() const { return(attackTimer.getLT()); } /* Web Editor * _ _ ____ _______ ______ * | \ | |/ __ \__ __| ____| * | \| | | | | | | | |__ * | . ` | | | | | | | __| * | |\ | |__| | | | | |____ * |_| \_|\____/ |_| |______| * * If you change anything here, make sure the changes are reflected in the web * editor! Either edit the PHP yourself or tell Dominus to make the changes. */ //********************************************************************* // getTypeModifier //********************************************************************* float Object::getTypeModifier() const { bstring armorType = getArmorType(); if(armorType == "plate" || armorType == "shield") return(42.89); else if(armorType == "chain") return(34.14); else if(armorType == "leather") return(24.46); else if(armorType == "cloth") return(12.25); else return(0.00); //Cloth-> Crayola sent, "how about 15.04, corresponding to 45%?" } //********************************************************************* // getLocationModifier //********************************************************************* float Object::getLocationModifier() const { switch(wearflag) { case BODY: return(0.24); case ARMS: return(0.08); case LEGS: return(0.12); case NECK: return(0.08); case BELT: return(0.06); case HANDS: return(0.04); case HEAD: return(0.08); case FEET: return(0.06); case FINGER: return(0.00); case SHIELD: return(0.12); case FACE: return(0.08); // case BRACER: // return(0.04); default: return(0.00); } } //********************************************************************* // adjustArmor //********************************************************************* int Object::adjustArmor() { if(type != ARMOR || quality <= 0 || wearflag == WIELD || wearflag == FINGER) return(-1); return(armor = (short)(getTypeModifier() * ((quality/10.0) + 2.3525) * getLocationModifier())); } //********************************************************************* // adjustWeapon //********************************************************************* int Object::adjustWeapon() { if(type != WEAPON || quality <= 0 || wearflag != WIELD) { damage.setMean(0); return(-1); } // Quality for weapons directly affects damage. If any of these factors change, // we need to readjust quality. // attack delay // numAttacks // For a given quality, convert to damage-per-second double dps = (quality / 11.4 + 5) / 2.6; short num = (numAttacks ? numAttacks : 1); // For a given damage-per-second, convert it to average damage. // This is the equation from Object::statObj backwards. double avg = ((getWeaponDelay()/10.0) * dps) / ((1.0 + num) / 2.0); // Set the average damage for this weapon damage.clear(); damage.setMean(avg); return(avg); }