CArtifactInstance *CArtifactInstance::createScroll(SpellID sid) { auto ret = new CArtifactInstance(VLC->arth->artifacts[ArtifactID::SPELL_SCROLL]); auto b = std::make_shared<Bonus>(Bonus::PERMANENT, Bonus::SPELL, Bonus::ARTIFACT_INSTANCE, -1, ArtifactID::SPELL_SCROLL, sid); ret->addNewBonus(b); return ret; }
CArtifactInstance * CArtifactInstance::createScroll( const CSpell *s) { auto ret = new CArtifactInstance(VLC->arth->artifacts[1]); auto b = new Bonus(Bonus::PERMANENT, Bonus::SPELL, Bonus::ARTIFACT_INSTANCE, -1, 1, s->id); ret->addNewBonus(b); return ret; }
CArtifactInstance * CArtifactInstance::createNewArtifactInstance(CArtifact *Art) { if(!Art->constituents) { auto ret = new CArtifactInstance(Art); if (dynamic_cast<CGrowingArtifact *>(Art)) { auto bonus = std::make_shared<Bonus>(); bonus->type = Bonus::LEVEL_COUNTER; bonus->val = 0; ret->addNewBonus (bonus); } return ret; } else { auto ret = new CCombinedArtifactInstance(Art); ret->createConstituents(); return ret; } }
void CArmedInstance::updateMoraleBonusFromArmy() { if(!validTypes(false)) //object not randomized, don't bother return; auto b = getExportedBonusList().getFirst(Selector::sourceType(Bonus::ARMY).And(Selector::type(Bonus::MORALE))); if(!b) { b = std::make_shared<Bonus>(Bonus::PERMANENT, Bonus::MORALE, Bonus::ARMY, 0, -1); addNewBonus(b); } //number of alignments and presence of undead std::set<TFaction> factions; bool hasUndead = false; for(auto slot : Slots()) { const CStackInstance * inst = slot.second; const CCreature * creature = VLC->creh->creatures[inst->getCreatureID()]; factions.insert(creature->faction); // Check for undead flag instead of faction (undead mummies are neutral) hasUndead |= inst->hasBonusOfType(Bonus::UNDEAD); } size_t factionsInArmy = factions.size(); //town garrison seems to take both sets into account // Take Angelic Alliance troop-mixing freedom of non-evil units into account. if (hasBonusOfType(Bonus::NONEVIL_ALIGNMENT_MIX)) { size_t mixableFactions = 0; for(TFaction f : factions) { if (VLC->townh->factions[f]->alignment != EAlignment::EVIL) mixableFactions++; } if (mixableFactions > 0) factionsInArmy -= mixableFactions - 1; } if(factionsInArmy == 1) { b->val = +1; b->description = VLC->generaltexth->arraytxt[115]; //All troops of one alignment +1 b->description = b->description.substr(0, b->description.size()-3);//trim "+1" } else if (!factions.empty()) // no bonus from empty garrison { b->val = 2 - factionsInArmy; b->description = boost::str(boost::format(VLC->generaltexth->arraytxt[114]) % factionsInArmy % b->val); //Troops of %d alignments %d b->description = b->description.substr(0, b->description.size()-2);//trim value } boost::algorithm::trim(b->description); CBonusSystemNode::treeHasChanged(); //-1 modifier for any Undead unit in army const ui8 UNDEAD_MODIFIER_ID = -2; auto undeadModifier = getExportedBonusList().getFirst(Selector::source(Bonus::ARMY, UNDEAD_MODIFIER_ID)); if(hasUndead) { if(!undeadModifier) { undeadModifier = std::make_shared<Bonus>(Bonus::PERMANENT, Bonus::MORALE, Bonus::ARMY, -1, UNDEAD_MODIFIER_ID, VLC->generaltexth->arraytxt[116]); undeadModifier->description = undeadModifier->description.substr(0, undeadModifier->description.size()-2);//trim value addNewBonus(undeadModifier); } } else if(undeadModifier) removeBonus(undeadModifier); }
void CGHeroInstance::updateSkill(SecondarySkill which, int val) { if(which == SecondarySkill::LEADERSHIP || which == SecondarySkill::LUCK) { //luck-> VLC->generaltexth->arraytxt[73+luckSkill]; VLC->generaltexth->arraytxt[104+moraleSkill] bool luck = which == SecondarySkill::LUCK; Bonus::BonusType type[] = {Bonus::MORALE, Bonus::LUCK}; Bonus *b = getBonusLocalFirst(Selector::type(type[luck]).And(Selector::sourceType(Bonus::SECONDARY_SKILL))); if(!b) { b = new Bonus(Bonus::PERMANENT, type[luck], Bonus::SECONDARY_SKILL, +val, which, which, Bonus::BASE_NUMBER); addNewBonus(b); } else b->val = +val; } else if(which == SecondarySkill::DIPLOMACY) //surrender discount: 20% per level { if(Bonus *b = getBonusLocalFirst(Selector::type(Bonus::SURRENDER_DISCOUNT).And(Selector::sourceType(Bonus::SECONDARY_SKILL)))) b->val = +val; else addNewBonus(new Bonus(Bonus::PERMANENT, Bonus::SURRENDER_DISCOUNT, Bonus::SECONDARY_SKILL, val * 20, which)); } int skillVal = 0; switch (which) { case SecondarySkill::ARCHERY: switch (val) { case 1: skillVal = 10; break; case 2: skillVal = 25; break; case 3: skillVal = 50; break; } break; case SecondarySkill::LOGISTICS: skillVal = 10 * val; break; case SecondarySkill::NAVIGATION: skillVal = 50 * val; break; case SecondarySkill::MYSTICISM: skillVal = val; break; case SecondarySkill::EAGLE_EYE: skillVal = 30 + 10 * val; break; case SecondarySkill::NECROMANCY: skillVal = 10 * val; break; case SecondarySkill::LEARNING: skillVal = 5 * val; break; case SecondarySkill::OFFENCE: skillVal = 10 * val; break; case SecondarySkill::ARMORER: skillVal = 5 * val; break; case SecondarySkill::INTELLIGENCE: skillVal = 25 << (val-1); break; case SecondarySkill::SORCERY: skillVal = 5 * val; break; case SecondarySkill::RESISTANCE: skillVal = 5 << (val-1); break; case SecondarySkill::FIRST_AID: skillVal = 25 + 25*val; break; case SecondarySkill::ESTATES: skillVal = 125 << (val-1); break; } Bonus::ValueType skillValType = skillVal ? Bonus::BASE_NUMBER : Bonus::INDEPENDENT_MIN; if(Bonus * b = getBonusList().getFirst(Selector::typeSubtype(Bonus::SECONDARY_SKILL_PREMY, which) .And(Selector::sourceType(Bonus::SECONDARY_SKILL)))) //only local hero bonus { b->val = skillVal; b->valType = skillValType; } else { auto bonus = new Bonus(Bonus::PERMANENT, Bonus::SECONDARY_SKILL_PREMY, Bonus::SECONDARY_SKILL, skillVal, id.getNum(), which, skillValType); bonus->source = Bonus::SECONDARY_SKILL; addNewBonus(bonus); } }
void CGHeroInstance::initObj() { blockVisit = true; auto hs = new HeroSpecial(); hs->setNodeType(CBonusSystemNode::SPECIALTY); attachTo(hs); //do we ever need to detach it? if(!type) initHero(); //TODO: set up everything for prison before specialties are configured skillsInfo.rand.setSeed(cb->gameState()->getRandomGenerator().nextInt()); skillsInfo.resetMagicSchoolCounter(); skillsInfo.resetWisdomCounter(); if (ID != Obj::PRISON) { auto customApp = VLC->objtypeh->getHandlerFor(ID, type->heroClass->id)->getOverride(cb->gameState()->getTile(visitablePos())->terType, this); if (customApp) appearance = customApp.get(); } for(const auto &spec : type->spec) //TODO: unfity with bonus system { auto bonus = new Bonus(); bonus->val = spec.val; bonus->sid = id.getNum(); //from the hero, specialty has no unique id bonus->duration = Bonus::PERMANENT; bonus->source = Bonus::HERO_SPECIAL; switch (spec.type) { case 1:// creature specialty { hs->growsWithLevel = true; const CCreature &specCreature = *VLC->creh->creatures[spec.additionalinfo]; //creature in which we have specialty //int creLevel = specCreature.level; //if(!creLevel) //{ // if(spec.additionalinfo == 146) // creLevel = 5; //treat ballista as 5-level // else // { // logGlobal->warnStream() << "Warning: unknown level of " << specCreature.namePl; // continue; // } //} //bonus->additionalInfo = spec.additionalinfo; //creature id, should not be used again - this works only with limiter bonus->limiter.reset(new CCreatureTypeLimiter (specCreature, true)); //with upgrades bonus->type = Bonus::PRIMARY_SKILL; bonus->valType = Bonus::ADDITIVE_VALUE; bonus->subtype = PrimarySkill::ATTACK; hs->addNewBonus(bonus); bonus = new Bonus(*bonus); bonus->subtype = PrimarySkill::DEFENSE; hs->addNewBonus(bonus); //values will be calculated later bonus = new Bonus(*bonus); bonus->type = Bonus::STACKS_SPEED; bonus->val = 1; //+1 speed hs->addNewBonus(bonus); } break; case 2://secondary skill hs->growsWithLevel = true; bonus->type = Bonus::SPECIAL_SECONDARY_SKILL; //needs to be recalculated with level, based on this value bonus->valType = Bonus::BASE_NUMBER; // to receive nonzero value bonus->subtype = spec.subtype; //skill id bonus->val = spec.val; //value per level, in percent hs->addNewBonus(bonus); bonus = new Bonus(*bonus); switch (spec.additionalinfo) { case 0: //normal bonus->valType = Bonus::PERCENT_TO_BASE; break; case 1: //when it's navigation or there's no 'base' at all bonus->valType = Bonus::PERCENT_TO_ALL; break; } bonus->type = Bonus::SECONDARY_SKILL_PREMY; //value will be calculated later hs->addNewBonus(bonus); break; case 3://spell damage bonus, level dependent but calculated elsewhere bonus->type = Bonus::SPECIAL_SPELL_LEV; bonus->subtype = spec.subtype; hs->addNewBonus(bonus); break; case 4://creature stat boost switch (spec.subtype) { case 1://attack bonus->type = Bonus::PRIMARY_SKILL; bonus->subtype = PrimarySkill::ATTACK; break; case 2://defense bonus->type = Bonus::PRIMARY_SKILL; bonus->subtype = PrimarySkill::DEFENSE; break; case 3: bonus->type = Bonus::CREATURE_DAMAGE; bonus->subtype = 0; //both min and max break; case 4://hp bonus->type = Bonus::STACK_HEALTH; break; case 5: bonus->type = Bonus::STACKS_SPEED; break; default: continue; } bonus->additionalInfo = spec.additionalinfo; //creature id bonus->valType = Bonus::ADDITIVE_VALUE; bonus->limiter.reset(new CCreatureTypeLimiter (*VLC->creh->creatures[spec.additionalinfo], true)); hs->addNewBonus(bonus); break; case 5://spell damage bonus in percent bonus->type = Bonus::SPECIFIC_SPELL_DAMAGE; bonus->valType = Bonus::BASE_NUMBER; // current spell system is screwed bonus->subtype = spec.subtype; //spell id hs->addNewBonus(bonus); break; case 6://damage bonus for bless (Adela) bonus->type = Bonus::SPECIAL_BLESS_DAMAGE; bonus->subtype = spec.subtype; //spell id if you ever wanted to use it otherwise bonus->additionalInfo = spec.additionalinfo; //damage factor hs->addNewBonus(bonus); break; case 7://maxed mastery for spell bonus->type = Bonus::MAXED_SPELL; bonus->subtype = spec.subtype; //spell i hs->addNewBonus(bonus); break; case 8://peculiar spells - enchantments bonus->type = Bonus::SPECIAL_PECULIAR_ENCHANT; bonus->subtype = spec.subtype; //spell id bonus->additionalInfo = spec.additionalinfo;//0, 1 for Coronius hs->addNewBonus(bonus); break; case 9://upgrade creatures { const auto &creatures = VLC->creh->creatures; bonus->type = Bonus::SPECIAL_UPGRADE; bonus->subtype = spec.subtype; //base id bonus->additionalInfo = spec.additionalinfo; //target id hs->addNewBonus(bonus); bonus = new Bonus(*bonus); for(auto cre_id : creatures[spec.subtype]->upgrades) { bonus->subtype = cre_id; //propagate for regular upgrades of base creature hs->addNewBonus(bonus); bonus = new Bonus(*bonus); } vstd::clear_pointer(bonus); break; } case 10://resource generation bonus->type = Bonus::GENERATE_RESOURCE; bonus->subtype = spec.subtype; hs->addNewBonus(bonus); break; case 11://starting skill with mastery (Adrienne) setSecSkillLevel(SecondarySkill(spec.val), spec.additionalinfo, true); break; case 12://army speed bonus->type = Bonus::STACKS_SPEED; hs->addNewBonus(bonus); break; case 13://Dragon bonuses (Mutare) bonus->type = Bonus::PRIMARY_SKILL; bonus->valType = Bonus::ADDITIVE_VALUE; switch (spec.subtype) { case 1: bonus->subtype = PrimarySkill::ATTACK; break; case 2: bonus->subtype = PrimarySkill::DEFENSE; break; } bonus->limiter.reset(new HasAnotherBonusLimiter(Bonus::DRAGON_NATURE)); hs->addNewBonus(bonus); break; default: logGlobal->warnStream() << "Unexpected hero specialty " << type; } } specialty.push_back(hs); //will it work? for (auto hs2 : type->specialty) //copy active (probably growing) bonuses from hero prootype to hero object { auto hs = new HeroSpecial(); attachTo(hs); //do we ever need to detach it? hs->setNodeType(CBonusSystemNode::SPECIALTY); for (auto bonus : hs2.bonuses) { hs->addNewBonus (bonus); } hs->growsWithLevel = hs2.growsWithLevel; specialty.push_back(hs); //will it work? } //initialize bonuses recreateSecondarySkillsBonuses(); Updatespecialty(); mana = manaLimit(); //after all bonuses are taken into account, make sure this line is the last one type->name = name; }
void CGHeroInstance::pushPrimSkill( PrimarySkill::PrimarySkill which, int val ) { assert(!hasBonus(Selector::typeSubtype(Bonus::PRIMARY_SKILL, which) .And(Selector::sourceType(Bonus::HERO_BASE_SKILL)))); addNewBonus(new Bonus(Bonus::PERMANENT, Bonus::PRIMARY_SKILL, Bonus::HERO_BASE_SKILL, val, id.getNum(), which)); }