1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-12 02:28:11 +02:00

Scholar is now configurable object (partial)

This commit is contained in:
Ivan Savenko 2023-10-04 19:32:16 +03:00
parent ca368f606f
commit e10de0594e
10 changed files with 113 additions and 165 deletions

View File

@ -1,7 +1,7 @@
{
"scholar" : {
"index" :81,
"handler" : "scholar",
"handler" : "configurable",
"base" : {
"sounds" : {
"visit" : ["GAZEBO"],
@ -9,13 +9,89 @@
}
},
"types" : {
"object" : {
"scholar" : {
"index" : 0,
"aiValue" : 1500,
"rmg" : {
"value" : 1500,
"rarity" : 100
}
},
"compatibilityIdentifiers" : [ "object" ],
"visitMode" : "unlimited",
"blockedVisitable" : true,
"variables" : {
"spell" : {
"gainedSpell" : { // Note: this variable name is used by engine for H3M loading
}
},
"secondarySkill" : {
"gainedSkill" : { // Note: this variable name is used by engine for H3M loading
}
},
"primarySkill" : {
"gainedStat" : { // Note: this variable name is used by engine for H3M loading
}
}
},
"selectMode" : "selectFirst",
"rewards" : [
{
"appearChance" : { "min" : 0, "max" : 33 },
"message" : 115,
"limiter" : {
"canLearnSpell" : [
"@gainedSpell"
]
},
"spells" : [
"@gainedSpell"
]
"removeObject" : true
},
{
"appearChance" : { "min" : 33, "max" : 66 },
"message" : 115,
"limiter" : {
// Hero does not have this skill at expert
"noneOf" : [
{
"secondarySkill" : {
"@gainedSkill" : 3
}
}
],
// And have either free skill slot or this skill
"anyOf" : [
{
"canLearnSkills" : true
},
{
"noneOf" : [
{
"secondarySkill" : {
"@gainedSkill" : 1
}
}
]
}
]
},
"secondary" : {
"@gainedSkill" : 1
},
"removeObject" : true
},
{
// Always present - fallback if hero can't learn secondary / spell
"message" : 115,
"primary" : {
"@gainedStat" : 1
},
"removeObject" : true
}
]
}
}
}

View File

@ -280,8 +280,19 @@ namespace JsonRandom
return ret;
}
PrimarySkill loadPrimary(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
{
std::set<PrimarySkill> defaultSkills{
PrimarySkill::ATTACK,
PrimarySkill::DEFENSE,
PrimarySkill::SPELL_POWER,
PrimarySkill::KNOWLEDGE
};
std::set<PrimarySkill> potentialPicks = filterKeys(value, defaultSkills, variables);
return *RandomGeneratorUtil::nextItem(potentialPicks, rng);
}
std::vector<si32> loadPrimary(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
std::vector<si32> loadPrimaries(const JsonNode & value, CRandomGenerator & rng, const Variables & variables)
{
std::vector<si32> ret;
if(value.isStruct())

View File

@ -37,7 +37,8 @@ namespace JsonRandom
DLL_LINKAGE TResources loadResources(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
DLL_LINKAGE TResources loadResource(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
DLL_LINKAGE std::vector<si32> loadPrimary(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
DLL_LINKAGE PrimarySkill loadPrimary(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
DLL_LINKAGE std::vector<si32> loadPrimaries(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
DLL_LINKAGE SecondarySkill loadSecondary(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);
DLL_LINKAGE std::map<SecondarySkill, si32> loadSecondaries(const JsonNode & value, CRandomGenerator & rng, const Variables & variables);

View File

@ -84,7 +84,6 @@ CObjectClassesHandler::CObjectClassesHandler()
SET_HANDLER("pandora", CGPandoraBox);
SET_HANDLER("prison", CGHeroInstance);
SET_HANDLER("questGuard", CGQuestGuard);
SET_HANDLER("scholar", CGScholar);
SET_HANDLER("seerHut", CGSeerHut);
SET_HANDLER("sign", CGSignBottle);
SET_HANDLER("siren", CGSirens);

View File

@ -874,132 +874,6 @@ void CGSignBottle::serializeJsonOptions(JsonSerializeFormat& handler)
handler.serializeStruct("text", message);
}
void CGScholar::onHeroVisit( const CGHeroInstance * h ) const
{
EBonusType type = bonusType;
int bid = bonusID;
//check if the bonus if applicable, if not - give primary skill (always possible)
int ssl = h->getSecSkillLevel(SecondarySkill(bid)); //current sec skill level, used if bonusType == 1
if((type == SECONDARY_SKILL && ((ssl == 3) || (!ssl && !h->canLearnSkill()))) ////hero already has expert level in the skill or (don't know skill and doesn't have free slot)
|| (type == SPELL && !h->canLearnSpell(SpellID(bid).toSpell())))
{
type = PRIM_SKILL;
bid = CRandomGenerator::getDefault().nextInt(GameConstants::PRIMARY_SKILLS - 1);
}
InfoWindow iw;
iw.type = EInfoWindowMode::AUTO;
iw.player = h->getOwner();
iw.text.appendLocalString(EMetaText::ADVOB_TXT,115);
switch (type)
{
case PRIM_SKILL:
cb->changePrimSkill(h,static_cast<PrimarySkill>(bid),+1);
iw.components.emplace_back(Component::EComponentType::PRIM_SKILL, bid, +1, 0);
break;
case SECONDARY_SKILL:
cb->changeSecSkill(h,SecondarySkill(bid),+1);
iw.components.emplace_back(Component::EComponentType::SEC_SKILL, bid, ssl + 1, 0);
break;
case SPELL:
{
std::set<SpellID> hlp;
hlp.insert(SpellID(bid));
cb->changeSpells(h,true,hlp);
iw.components.emplace_back(Component::EComponentType::SPELL, bid, 0, 0);
}
break;
default:
logGlobal->error("Error: wrong bonus type (%d) for Scholar!\n", static_cast<int>(type));
return;
}
cb->showInfoDialog(&iw);
cb->removeObject(this, h->getOwner());
}
void CGScholar::initObj(CRandomGenerator & rand)
{
blockVisit = true;
if(bonusType == RANDOM)
{
bonusType = static_cast<EBonusType>(rand.nextInt(2));
switch(bonusType)
{
case PRIM_SKILL:
bonusID = rand.nextInt(GameConstants::PRIMARY_SKILLS -1);
break;
case SECONDARY_SKILL:
bonusID = rand.nextInt(static_cast<int>(VLC->skillh->size()) - 1);
break;
case SPELL:
std::vector<SpellID> possibilities;
cb->getAllowedSpells (possibilities);
bonusID = *RandomGeneratorUtil::nextItem(possibilities, rand);
break;
}
}
}
void CGScholar::serializeJsonOptions(JsonSerializeFormat & handler)
{
if(handler.saving)
{
std::string value;
switch(bonusType)
{
case PRIM_SKILL:
value = NPrimarySkill::names[bonusID];
handler.serializeString("rewardPrimSkill", value);
break;
case SECONDARY_SKILL:
value = CSkillHandler::encodeSkill(bonusID);
handler.serializeString("rewardSkill", value);
break;
case SPELL:
value = SpellID::encode(bonusID);
handler.serializeString("rewardSpell", value);
break;
case RANDOM:
break;
}
}
else
{
//TODO: unify
const JsonNode & json = handler.getCurrent();
bonusType = RANDOM;
if(!json["rewardPrimSkill"].String().empty())
{
auto raw = VLC->identifiers()->getIdentifier(ModScope::scopeBuiltin(), "primSkill", json["rewardPrimSkill"].String());
if(raw)
{
bonusType = PRIM_SKILL;
bonusID = raw.value();
}
}
else if(!json["rewardSkill"].String().empty())
{
auto raw = VLC->identifiers()->getIdentifier(ModScope::scopeBuiltin(), "skill", json["rewardSkill"].String());
if(raw)
{
bonusType = SECONDARY_SKILL;
bonusID = raw.value();
}
}
else if(!json["rewardSpell"].String().empty())
{
auto raw = VLC->identifiers()->getIdentifier(ModScope::scopeBuiltin(), "spell", json["rewardSpell"].String());
if(raw)
{
bonusType = SPELL;
bonusID = raw.value();
}
}
}
}
void CGGarrison::onHeroVisit (const CGHeroInstance *h) const
{
auto relations = cb->gameState()->getPlayerRelations(h->tempOwner, tempOwner);

View File

@ -57,26 +57,6 @@ protected:
void serializeJsonOptions(JsonSerializeFormat & handler) override;
};
class DLL_LINKAGE CGScholar : public CGObjectInstance
{
public:
enum EBonusType {PRIM_SKILL, SECONDARY_SKILL, SPELL, RANDOM = 255};
EBonusType bonusType;
ui16 bonusID; //ID of skill/spell
CGScholar() : bonusType(EBonusType::RANDOM),bonusID(0){};
void onHeroVisit(const CGHeroInstance * h) const override;
void initObj(CRandomGenerator & rand) override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<CGObjectInstance&>(*this);
h & bonusType;
h & bonusID;
}
protected:
void serializeJsonOptions(JsonSerializeFormat & handler) override;
};
class DLL_LINKAGE CGGarrison : public CArmedInstance
{
public:

View File

@ -1179,11 +1179,21 @@ CGObjectInstance * CMapLoaderH3M::readWitchHut(const int3 & position, std::share
return object;
}
CGObjectInstance * CMapLoaderH3M::readScholar()
CGObjectInstance * CMapLoaderH3M::readScholar(const int3 & position, std::shared_ptr<const ObjectTemplate> objectTemplate)
{
auto * object = new CGScholar();
object->bonusType = static_cast<CGScholar::EBonusType>(reader->readUInt8());
object->bonusID = reader->readUInt8();
enum class ScholarBonusType : uint8_t {
PRIM_SKILL = 0,
SECONDARY_SKILL = 1,
SPELL = 2,
RANDOM = 255
};
auto * object = readGeneric(position, objectTemplate);
//auto * rewardable = dynamic_cast<CRewardableObject*>(object);
/*uint8_t bonusTypeRaw =*/ reader->readUInt8();
/*auto bonusType = static_cast<ScholarBonusType>(bonusTypeRaw);*/
/*auto bonusID =*/ reader->readUInt8();
reader->skipZero(6);
return object;
}
@ -1491,7 +1501,7 @@ CGObjectInstance * CMapLoaderH3M::readObject(std::shared_ptr<const ObjectTemplat
case Obj::WITCH_HUT:
return readWitchHut(mapPosition, objectTemplate);
case Obj::SCHOLAR:
return readScholar();
return readScholar(mapPosition, objectTemplate);
case Obj::GARRISON:
case Obj::GARRISON2:

View File

@ -166,7 +166,7 @@ private:
CGObjectInstance * readTown(const int3 & position, std::shared_ptr<const ObjectTemplate> objTempl);
CGObjectInstance * readSign(const int3 & position);
CGObjectInstance * readWitchHut(const int3 & position, std::shared_ptr<const ObjectTemplate> objectTemplate);
CGObjectInstance * readScholar();
CGObjectInstance * readScholar(const int3 & position, std::shared_ptr<const ObjectTemplate> objectTemplate);
CGObjectInstance * readGarrison(const int3 & mapPosition);
CGObjectInstance * readArtifact(const int3 & position, std::shared_ptr<const ObjectTemplate> objTempl);
CGObjectInstance * readResource(const int3 & position, std::shared_ptr<const ObjectTemplate> objTempl);

View File

@ -54,7 +54,6 @@ void registerTypesMapObjects1(Serializer &s)
s.template registerType<CGMonolith, CGSubterraneanGate>();
s.template registerType<CGMonolith, CGWhirlpool>();
s.template registerType<CGObjectInstance, CGSignBottle>();
s.template registerType<CGObjectInstance, CGScholar>();
s.template registerType<CGObjectInstance, CGKeys>();
s.template registerType<CGKeys, CGKeymasterTent>();
s.template registerType<CGKeys, CGBorderGuard>(); s.template registerType<IQuestObject, CGBorderGuard>();
@ -131,7 +130,6 @@ void registerTypesMapObjectTypes(Serializer &s)
REGISTER_GENERIC_HANDLER(CGPandoraBox);
REGISTER_GENERIC_HANDLER(CGQuestGuard);
REGISTER_GENERIC_HANDLER(CGResource);
REGISTER_GENERIC_HANDLER(CGScholar);
REGISTER_GENERIC_HANDLER(CGSeerHut);
REGISTER_GENERIC_HANDLER(CGShipyard);
REGISTER_GENERIC_HANDLER(CGSignBottle);

View File

@ -115,7 +115,7 @@ void Rewardable::Info::configureLimiter(Rewardable::Configuration & object, CRan
limiter.resources = JsonRandom::loadResources(source["resources"], rng, variables);
limiter.primary = JsonRandom::loadPrimary(source["primary"], rng, variables);
limiter.primary = JsonRandom::loadPrimaries(source["primary"], rng, variables);
limiter.secondary = JsonRandom::loadSecondaries(source["secondary"], rng, variables);
limiter.artifacts = JsonRandom::loadArtifacts(source["artifacts"], rng, variables);
limiter.spells = JsonRandom::loadSpells(source["spells"], rng, variables);
@ -150,7 +150,7 @@ void Rewardable::Info::configureReward(Rewardable::Configuration & object, CRand
reward.removeObject = source["removeObject"].Bool();
reward.bonuses = JsonRandom::loadBonuses(source["bonuses"]);
reward.primary = JsonRandom::loadPrimary(source["primary"], rng, variables);
reward.primary = JsonRandom::loadPrimaries(source["primary"], rng, variables);
reward.secondary = JsonRandom::loadSecondaries(source["secondary"], rng, variables);
reward.artifacts = JsonRandom::loadArtifacts(source["artifacts"], rng, variables);
@ -221,9 +221,8 @@ void Rewardable::Info::configureVariables(Rewardable::Configuration & object, CR
// if (category.first == "resource")
// value = JsonRandom::loadResource(input, rng, object.variables.values).getNum();
// TODO
// if (category.first == "primarySkill")
// value = JsonRandom::loadCreature(input, rng, object.variables.values).getNum();
if (category.first == "primarySkill")
value = static_cast<int>(JsonRandom::loadPrimary(input, rng, object.variables.values));
if (category.first == "secondarySkill")
value = JsonRandom::loadSecondary(input, rng, object.variables.values).getNum();