void CHeroHandler::loadHeroSkills(CHero * hero, const JsonNode & node) { for(const JsonNode &set : node["skills"].Vector()) { int skillLevel = boost::range::find(NSecondarySkill::levels, set["level"].String()) - std::begin(NSecondarySkill::levels); if (skillLevel < SecSkillLevel::LEVELS_SIZE) { size_t currentIndex = hero->secSkillsInit.size(); hero->secSkillsInit.push_back(std::make_pair(SecondarySkill(-1), skillLevel)); VLC->modh->identifiers.requestIdentifier("skill", set["skill"], [=](si32 id) { hero->secSkillsInit[currentIndex].first = SecondarySkill(id); }); } else { logGlobal->errorStream() << "Unknown skill level: " <<set["level"].String(); } } // spellbook is considered present if hero have "spellbook" entry even when this is an empty set (0 spells) hero->haveSpellBook = !node["spellbook"].isNull(); for(const JsonNode & spell : node["spellbook"].Vector()) { VLC->modh->identifiers.requestIdentifier("spell", spell, [=](si32 spellID) { hero->spells.insert(SpellID(spellID)); }); } }
void CGHeroInstance::recreateSecondarySkillsBonuses() { auto secondarySkillsBonuses = getBonuses(Selector::sourceType(Bonus::SECONDARY_SKILL)); for(auto bonus : *secondarySkillsBonuses) removeBonus(bonus); for(auto skill_info : secSkills) updateSkill(SecondarySkill(skill_info.first), skill_info.second); }
void CGSeerHut::completeQuest (const CGHeroInstance * h) const //reward { switch (rewardType) { case EXPERIENCE: { TExpType expVal = h->calculateXp(rVal); cb->changePrimSkill(h, PrimarySkill::EXPERIENCE, expVal, false); break; } case MANA_POINTS: { cb->setManaPoints(h->id, h->mana+rVal); break; } case MORALE_BONUS: case LUCK_BONUS: { Bonus hb(Bonus::ONE_WEEK, (rewardType == 3 ? Bonus::MORALE : Bonus::LUCK), Bonus::OBJECT, rVal, h->id.getNum(), "", -1); GiveBonus gb; gb.id = h->id.getNum(); gb.bonus = hb; cb->giveHeroBonus(&gb); } break; case RESOURCES: cb->giveResource(h->getOwner(), static_cast<Res::ERes>(rID), rVal); break; case PRIMARY_SKILL: cb->changePrimSkill(h, static_cast<PrimarySkill::PrimarySkill>(rID), rVal, false); break; case SECONDARY_SKILL: cb->changeSecSkill(h, SecondarySkill(rID), rVal, false); break; case ARTIFACT: cb->giveHeroNewArtifact(h, VLC->arth->artifacts[rID],ArtifactPosition::FIRST_AVAILABLE); break; case SPELL: { std::set<SpellID> spell; spell.insert (SpellID(rID)); cb->changeSpells(h, true, spell); } break; case CREATURE: { CCreatureSet creatures; creatures.setCreature(SlotID(0), CreatureID(rID), rVal); cb->giveCreatures(this, h, creatures, false); } break; default: break; } }
bool TradeOnMarketplace::applyGh( CGameHandler *gh ) { //market must be owned or visited const IMarket *m = IMarket::castFrom(market); if(!m) COMPLAIN_AND_RETURN("market is not-a-market! :/"); ui8 player = market->tempOwner; if(player >= GameConstants::PLAYER_LIMIT) player = gh->getTile(market->visitablePos())->visitableObjects.back()->tempOwner; if(player >= GameConstants::PLAYER_LIMIT) COMPLAIN_AND_RETURN("No player can use this market!"); if(hero && (player != hero->tempOwner || hero->visitablePos() != market->visitablePos())) COMPLAIN_AND_RETURN("This hero can't use this marketplace!"); ERROR_IF_NOT(player); switch(mode) { case EMarketMode::RESOURCE_RESOURCE: return gh->tradeResources(m, val, player, r1, r2); case EMarketMode::RESOURCE_PLAYER: return gh->sendResources(val, player, static_cast<Res::ERes>(r1), static_cast<TPlayerColor>(r2)); case EMarketMode::CREATURE_RESOURCE: if(!hero) COMPLAIN_AND_RETURN("Only hero can sell creatures!"); return gh->sellCreatures(val, m, hero, r1, static_cast<Res::ERes>(r2)); case EMarketMode::RESOURCE_ARTIFACT: if(!hero) COMPLAIN_AND_RETURN("Only hero can buy artifacts!"); return gh->buyArtifact(m, hero, static_cast<Res::ERes>(r1), ArtifactID(r2)); case EMarketMode::ARTIFACT_RESOURCE: if(!hero) COMPLAIN_AND_RETURN("Only hero can sell artifacts!"); return gh->sellArtifact(m, hero, ArtifactInstanceID(r1), static_cast<Res::ERes>(r2)); case EMarketMode::CREATURE_UNDEAD: return gh->transformInUndead(m, hero, r1); case EMarketMode::RESOURCE_SKILL: return gh->buySecSkill(m, hero, SecondarySkill(r2)); case EMarketMode::CREATURE_EXP: return gh->sacrificeCreatures(m, hero, r1, val); case EMarketMode::ARTIFACT_EXP: return gh->sacrificeArtifact(m, hero, ArtifactPosition(r1)); default: COMPLAIN_AND_RETURN("Unknown exchange mode!"); } }
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; }
std::vector<SecondarySkill> CGHeroInstance::getLevelUpProposedSecondarySkills() const { std::vector<SecondarySkill> obligatorySkills; //hero is offered magic school or wisdom if possible if (!skillsInfo.wisdomCounter) { if (cb->isAllowed(2, SecondarySkill::WISDOM) && !getSecSkillLevel(SecondarySkill::WISDOM)) obligatorySkills.push_back(SecondarySkill::WISDOM); } if (!skillsInfo.magicSchoolCounter) { std::vector<SecondarySkill> ss = { SecondarySkill::FIRE_MAGIC, SecondarySkill::AIR_MAGIC, SecondarySkill::WATER_MAGIC, SecondarySkill::EARTH_MAGIC }; std::shuffle(ss.begin(), ss.end(), skillsInfo.rand.getStdGenerator()); for (auto skill : ss) { if (cb->isAllowed(2, skill) && !getSecSkillLevel(skill)) //only schools hero doesn't know yet { obligatorySkills.push_back(skill); break; //only one } } } std::vector<SecondarySkill> skills; //picking sec. skills for choice std::set<SecondarySkill> basicAndAdv, expert, none; for(int i=0;i<GameConstants::SKILL_QUANTITY;i++) if (cb->isAllowed(2,i)) none.insert(SecondarySkill(i)); for(auto & elem : secSkills) { if(elem.second < SecSkillLevel::EXPERT) basicAndAdv.insert(elem.first); else expert.insert(elem.first); none.erase(elem.first); } for (auto s : obligatorySkills) //don't duplicate them { none.erase (s); basicAndAdv.erase (s); expert.erase (s); } //first offered skill: // 1) give obligatory skill // 2) give any other new skill // 3) upgrade existing if (canLearnSkill() && obligatorySkills.size() > 0) { skills.push_back (obligatorySkills[0]); } else if(none.size() && canLearnSkill()) //hero have free skill slot { skills.push_back(type->heroClass->chooseSecSkill(none, skillsInfo.rand)); //new skill none.erase(skills.back()); } else if(!basicAndAdv.empty()) { skills.push_back(type->heroClass->chooseSecSkill(basicAndAdv, skillsInfo.rand)); //upgrade existing basicAndAdv.erase(skills.back()); } //second offered skill: //1) upgrade existing //2) give obligatory skill //3) give any other new skill if(!basicAndAdv.empty()) { SecondarySkill s = type->heroClass->chooseSecSkill(basicAndAdv, skillsInfo.rand);//upgrade existing skills.push_back(s); basicAndAdv.erase(s); } else if (canLearnSkill() && obligatorySkills.size() > 1) { skills.push_back (obligatorySkills[1]); } else if(none.size() && canLearnSkill()) { skills.push_back(type->heroClass->chooseSecSkill(none, skillsInfo.rand)); //give new skill none.erase(skills.back()); } if (skills.size() == 2) // Fix for #1868 to avoid changing logic (possibly causing bugs in process) std::swap(skills[0], skills[1]); return skills; }