void Monster::updateRetargeting(Duration elapsedTime) { assert(elapsedTime >= Duration::zero()); if (_retargetDelay <= Duration::zero()) { return; } if (attackedCreature == nullptr) { return; } if (hasMaster()) { return; } if (elapsedTime >= _retargetDelay) { _retargetDelay = _type->retargetInterval; if (_type->retargetChance < random_range(1, 100)) { return; } retarget(); } else { _retargetDelay -= elapsedTime; } }
bool Monster::canAttack(const Creature& creature) const { if (!creature.isAlive()) { return false; } if (hasMaster()) { return _master->canAttack(creature); } // TODO name "isAttackable" is too broad if (!creature.isAttackable()) { return false; } // TODO merge with isAttackable? if (creature.getZone() == ZONE_PROTECTION) { return false; } if (creature.isInvisible() && !canSeeInvisibility()) { return false; } PlayerPC controller = creature.getController(); if (controller != nullptr) { if (controller->isGhost()) { return false; } } return true; }
void Monster::onAttackedCreature(Creature* target) { Creature::onAttackedCreature(target); if (hasMaster()) { _master->onSummonAttackedCreature(this, target); } }
void Monster::onAttackedCreatureDrain(Creature* target, int32_t points) { Creature::onAttackedCreatureDrain(target, points); if (hasMaster()) { _master->onSummonAttackedCreatureDrain(this, target, points); } }
// // All unlocking activity ultimately funnels through this method. // This unlocks a DbCommon using the secrets setup in its crypto core // component, and performs all the housekeeping needed to represent // the state change. // Returns true if unlock was successful, false if it failed due to // invalid/insufficient secrets. Throws on other errors. // bool KeychainDbCommon::unlockDb(DbBlob *blob, void **privateAclBlob) { try { // Tell the cryptocore to (try to) decode itself. This will fail // in an astonishing variety of ways if the passphrase is wrong. assert(hasMaster()); decodeCore(blob, privateAclBlob); secdebug("KCdb", "%p unlock successful", this); } catch (...) { secdebug("KCdb", "%p unlock failed", this); return false; } // get the database parameters only if we haven't got them yet if (!mValidParams) { mParams = blob->params; n2hi(mParams.idleTimeout); mValidParams = true; // sticky } bool isLocked = mIsLocked; setUnlocked(); // mark unlocked if (isLocked) { // broadcast unlock notification, but only if we were previously locked notify(kNotificationEventUnlocked); SECURITYD_KEYCHAIN_UNLOCK(this, (char*)this->dbName()); } return true; }
void Monster::notifyMasterChanged(const CreatureP& previousMaster) { SpectatorList spectators; if (isAlive()) { Game& game = server.game(); game.getSpectators(spectators, getPosition(), false, true); if (hasMaster()) { game.getSpectators(spectators, _master->getPosition(), true, true); } if (previousMaster != nullptr && std::find(spectators.begin(), spectators.end(), previousMaster) == spectators.end()) { spectators.push_back(previousMaster); } if (_master != nullptr && std::find(spectators.begin(), spectators.end(), _master) == spectators.end()) { spectators.push_back(_master); } } else { if (previousMaster != nullptr) { spectators.push_back(previousMaster); } if (_master != nullptr) { spectators.push_back(_master); } } for (const auto& spectator : spectators) { spectator->onMonsterMasterChanged(this, previousMaster); } }
void Monster::getPathSearchParams(const Creature* creature, FindPathParams& fpp) const { Creature::getPathSearchParams(creature, fpp); fpp.minTargetDist = 1; fpp.maxTargetDist = _type->targetDistance; if(hasMaster()) { if(getMaster() == creature) { fpp.maxTargetDist = 2; fpp.fullPathSearch = true; } else if(_type->targetDistance <= 1) fpp.fullPathSearch = true; else fpp.fullPathSearch = !canUseAttack(getPosition(), creature); } else if(isFleeing()) { //Distance should be higher than the client view range (Map::maxClientViewportX/Map::maxClientViewportY) fpp.maxTargetDist = Map::maxViewportX; fpp.clearSight = fpp.fullPathSearch = false; fpp.keepDistance = true; } else if(_type->targetDistance <= 1) fpp.fullPathSearch = true; else fpp.fullPathSearch = !canUseAttack(getPosition(), creature); }
uint32_t Monster::getPreferredFollowDistance() const { if (getAttackedCreature() != nullptr) { return std::max(_type->targetDistance, 1u); } if (hasMaster()) { return 2; } return Creature::getPreferredFollowDistance(); }
void KeychainDbCommon::makeNewSecrets() { // we already have a master key (right?) assert(hasMaster()); // tell crypto core to generate the use keys DatabaseCryptoCore::generateNewSecrets(); // we're now officially "unlocked"; set the timer setUnlocked(); }
void Monster::updateFollowing() { if (attackedCreature != nullptr) { startFollowing(attackedCreature); } else if (hasMaster()) { startFollowing(_master.get()); } else { stopFollowing(); } }
bool Monster::shouldTeleportToMaster() const { if (!hasMaster()) { return false; } ConfigManager& config = server.configManager(); if (!config.getBool(ConfigManager::TELEPORT_SUMMONS) && (_master->getPlayer() == nullptr || !config.getBool(ConfigManager::TELEPORT_PLAYER_SUMMONS))) { return false; } if (isMasterInRange()) { return false; } return true; }
bool Monster::teleportToMaster() { if (!hasMaster()) { return false; } Game& game = server.game(); Position initialPosition = getPosition(); if (game.internalTeleport(this, game.getClosestFreeTile(this, _master->getPosition(), true), false) != RET_NOERROR) { return false; } game.addMagicEffect(initialPosition, MAGIC_EFFECT_POFF); game.addMagicEffect(getPosition(), MAGIC_EFFECT_TELEPORT); return true; }
DbBlob *KeychainDbCommon::encode(KeychainDatabase &db) { assert(!isLocked()); // must have been unlocked by caller // export database ACL to blob form CssmData pubAcl, privAcl; db.acl().exportBlob(pubAcl, privAcl); // tell the cryptocore to form the blob DbBlob form; form.randomSignature = identifier(); form.sequence = sequence; form.params = mParams; h2ni(form.params.idleTimeout); assert(hasMaster()); DbBlob *blob = encodeCore(form, pubAcl, privAcl); // clean up and go db.acl().allocator.free(pubAcl); db.acl().allocator.free(privAcl); return blob; }
bool Monster::isEnemy(const Creature& creature) const { if (hasMaster()) { return _master->isEnemy(creature); } if (!isHostile()) { return false; } PlayerPC controller = creature.getController(); if (controller == nullptr) { return false; } if (controller->hasFlag(PlayerFlag_IgnoredByMonsters)) { return false; } if (controller->hasCondition(CONDITION_GAMEMASTER, GAMEMASTER_SNEAKY, false)) { return false; } return true; }
void Monster::updateTarget() { if (hasMaster()) { target(_master->getAttackedCreature()); } else { CreatureP attackedCreature = this->attackedCreature; if (attackedCreature == nullptr || !canAttack(*attackedCreature)) { retarget(); } else { // retarget if target is out of sight + we cannot walk there auto& game = server.game(); if (!game.isSightClear(getPosition(), attackedCreature->getPosition(), true)) { FindPathParams parameters; getPathSearchParams(attackedCreature.get(), parameters); DirectionRoute route; if (!server.game().getPathToEx(this, attackedCreature->getPosition(), route, parameters)) { retarget(); } } } } }
bool Monster::canBeConvincedBy(const CreatureP& convincer, bool forced) const { if (convincer == this) { return false; } if (!forced) { Player* convincingPlayer = convincer->getPlayer(); if (convincingPlayer != nullptr && !_type->isConvinceable && !convincingPlayer->hasFlag(PlayerFlag_CanConvinceAll)) { return false; } } if (hasMaster()) { if (!forced && _master->hasController()) { return false; } if (convincer == _master) { return false; } } return true; }
void Monster::onThinkDefense(uint32_t interval) { auto& game = server.game(); resetTicks = true; defenseTicks += interval; for(SpellList::iterator it = _type->spellDefenseList.begin(); it != _type->spellDefenseList.end(); ++it) { if(it->speed > defenseTicks) { if(resetTicks) resetTicks = false; continue; } if(defenseTicks % it->speed >= interval) //already used this spell for this round continue; if((it->chance >= (uint32_t)random_range(1, 100))) { minCombatValue = it->minCombatValue; maxCombatValue = it->maxCombatValue; it->spell->castSpell(this, this); } } if(!hasMaster()) { if(_type->maxSummons < 0 || (int32_t)summons.size() < _type->maxSummons) { for(SummonList::iterator it = _type->summonList.begin(); it != _type->summonList.end(); ++it) { if((int32_t)summons.size() >= _type->maxSummons) break; if(it->interval > defenseTicks) { if(resetTicks) resetTicks = false; continue; } if(defenseTicks % it->interval >= interval) continue; uint32_t typeCount = 0; for(MonsterList::iterator cit = summons.begin(); cit != summons.end(); ++cit) { if(!(*cit)->isAlive() && (*cit)->getMonster() && (*cit)->getMonster()->getName() == it->name) typeCount++; } if(typeCount >= it->amount) continue; if((it->chance >= (uint32_t)random_range(1, 100))) { if (game.placeSummon(this, it->name)) { game.addMagicEffect(getPosition(), MAGIC_EFFECT_WRAPS_BLUE); } } } } } if(resetTicks) defenseTicks = 0; }
bool Monster::isMasterInRange() const { return (hasMaster() && canSee(_master->getPosition())); }