mirror of
https://github.com/vcmi/vcmi.git
synced 2025-11-25 22:42:04 +02:00
Merge pull request #5947 from Laserlicht/skill
Setting to increase selectable skills on levelup
This commit is contained in:
@@ -404,23 +404,39 @@ void CSplitWindow::sliderMoved(int to)
|
|||||||
|
|
||||||
CLevelWindow::CLevelWindow(const CGHeroInstance * hero, PrimarySkill pskill, std::vector<SecondarySkill> & skills, std::function<void(ui32)> callback)
|
CLevelWindow::CLevelWindow(const CGHeroInstance * hero, PrimarySkill pskill, std::vector<SecondarySkill> & skills, std::function<void(ui32)> callback)
|
||||||
: CWindowObject(PLAYER_COLORED, ImagePath::builtin("LVLUPBKG")),
|
: CWindowObject(PLAYER_COLORED, ImagePath::builtin("LVLUPBKG")),
|
||||||
cb(callback)
|
cb(callback),
|
||||||
|
skills(skills),
|
||||||
|
hero(hero),
|
||||||
|
skillViewOffset(0)
|
||||||
{
|
{
|
||||||
OBJECT_CONSTRUCTION;
|
OBJECT_CONSTRUCTION;
|
||||||
|
|
||||||
GAME->interface()->showingDialog->setBusy();
|
GAME->interface()->showingDialog->setBusy();
|
||||||
|
|
||||||
if(!skills.empty())
|
sortedSkills = skills;
|
||||||
{
|
std::sort(sortedSkills.begin(), sortedSkills.end(), [hero](auto a, auto b) {
|
||||||
std::vector<std::shared_ptr<CSelectableComponent>> comps;
|
if(hero->getSecSkillLevel(a) == hero->getSecSkillLevel(b))
|
||||||
for(auto & skill : skills)
|
return LIBRARY->skillh->getById(a)->getNameTranslated() < LIBRARY->skillh->getById(b)->getNameTranslated();
|
||||||
{
|
return hero->getSecSkillLevel(a) > hero->getSecSkillLevel(b);
|
||||||
auto comp = std::make_shared<CSelectableComponent>(ComponentType::SEC_SKILL, skill, hero->getSecSkillLevel(SecondarySkill(skill))+1, CComponent::medium);
|
});
|
||||||
comp->onChoose = std::bind(&CLevelWindow::close, this);
|
|
||||||
comps.push_back(comp);
|
|
||||||
}
|
|
||||||
|
|
||||||
box = std::make_shared<CComponentBox>(comps, Rect(75, 300, pos.w - 150, 100));
|
createSkillBox();
|
||||||
|
if(skills.size() > 3)
|
||||||
|
{
|
||||||
|
buttonLeft = std::make_shared<CButton>(Point(23, 309), AnimationPath::builtin("HSBTNS3"), CButton::tooltip(), [this, skills](){
|
||||||
|
if(skillViewOffset > 0)
|
||||||
|
skillViewOffset--;
|
||||||
|
else
|
||||||
|
skillViewOffset = skills.size() - 1;
|
||||||
|
createSkillBox();
|
||||||
|
}, EShortcut::MOVE_LEFT);
|
||||||
|
buttonRight = std::make_shared<CButton>(Point(pos.w - 45, 309), AnimationPath::builtin("HSBTNS5"), CButton::tooltip(), [this, skills](){
|
||||||
|
if(skillViewOffset < skills.size() - 1)
|
||||||
|
skillViewOffset++;
|
||||||
|
else
|
||||||
|
skillViewOffset = 0;
|
||||||
|
createSkillBox();
|
||||||
|
}, EShortcut::MOVE_RIGHT);
|
||||||
}
|
}
|
||||||
|
|
||||||
portrait = std::make_shared<CHeroArea>(170, 66, hero);
|
portrait = std::make_shared<CHeroArea>(170, 66, hero);
|
||||||
@@ -444,11 +460,53 @@ CLevelWindow::CLevelWindow(const CGHeroInstance * hero, PrimarySkill pskill, std
|
|||||||
skillValue = std::make_shared<CLabel>(192, 253, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, LIBRARY->generaltexth->primarySkillNames[pskill.getNum()] + " +1");
|
skillValue = std::make_shared<CLabel>(192, 253, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, LIBRARY->generaltexth->primarySkillNames[pskill.getNum()] + " +1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<SecondarySkill> getSkillsToShow(const std::vector<SecondarySkill>& skills, int offset, int count)
|
||||||
|
{
|
||||||
|
std::vector<SecondarySkill> result;
|
||||||
|
|
||||||
|
int size = skills.size();
|
||||||
|
if (size == 0 || count <= 0) return result;
|
||||||
|
|
||||||
|
offset = offset % size; // ensure offset is within bounds
|
||||||
|
for (int i = 0; i < std::min(count, size); ++i)
|
||||||
|
{
|
||||||
|
int index = (offset + i) % size; // ring buffer like
|
||||||
|
result.push_back(skills[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLevelWindow::createSkillBox()
|
||||||
|
{
|
||||||
|
OBJECT_CONSTRUCTION;
|
||||||
|
|
||||||
|
std::vector<SecondarySkill> skillsToShow = skills.size() > 3 ? getSkillsToShow(sortedSkills, skillViewOffset, 3) : sortedSkills;
|
||||||
|
if(!skillsToShow.empty())
|
||||||
|
{
|
||||||
|
std::vector<std::shared_ptr<CSelectableComponent>> comps;
|
||||||
|
for(auto & skill : skillsToShow)
|
||||||
|
{
|
||||||
|
auto comp = std::make_shared<CSelectableComponent>(ComponentType::SEC_SKILL, skill, hero->getSecSkillLevel(SecondarySkill(skill))+1, CComponent::medium);
|
||||||
|
comp->onChoose = std::bind(&CLevelWindow::close, this);
|
||||||
|
comps.push_back(comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
box = std::make_shared<CComponentBox>(comps, Rect(75, 300, pos.w - 150, 100));
|
||||||
|
}
|
||||||
|
|
||||||
|
setRedrawParent(true);
|
||||||
|
redraw();
|
||||||
|
}
|
||||||
|
|
||||||
void CLevelWindow::close()
|
void CLevelWindow::close()
|
||||||
{
|
{
|
||||||
//FIXME: call callback if there was nothing to select?
|
//FIXME: call callback if there was nothing to select?
|
||||||
if (box && box->selectedIndex() != -1)
|
if (box && box->selectedIndex() != -1)
|
||||||
cb(box->selectedIndex());
|
{
|
||||||
|
auto it = std::find(skills.begin(), skills.end(), sortedSkills[(box->selectedIndex() + skillViewOffset) % skills.size()]);
|
||||||
|
cb(std::distance(skills.begin(), it));
|
||||||
|
}
|
||||||
|
|
||||||
GAME->interface()->showingDialog->setFree();
|
GAME->interface()->showingDialog->setFree();
|
||||||
|
|
||||||
|
|||||||
@@ -152,7 +152,16 @@ class CLevelWindow : public CWindowObject
|
|||||||
std::shared_ptr<CComponentBox> box; //skills to select
|
std::shared_ptr<CComponentBox> box; //skills to select
|
||||||
std::function<void(ui32)> cb;
|
std::function<void(ui32)> cb;
|
||||||
|
|
||||||
|
int skillViewOffset = 0;
|
||||||
|
std::shared_ptr<CButton> buttonLeft;
|
||||||
|
std::shared_ptr<CButton> buttonRight;
|
||||||
|
|
||||||
|
std::vector<SecondarySkill> skills;
|
||||||
|
std::vector<SecondarySkill> sortedSkills;
|
||||||
|
const CGHeroInstance * hero;
|
||||||
|
|
||||||
void selectionChanged(unsigned to);
|
void selectionChanged(unsigned to);
|
||||||
|
void createSkillBox();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CLevelWindow(const CGHeroInstance *hero, PrimarySkill pskill, std::vector<SecondarySkill> &skills, std::function<void(ui32)> callback);
|
CLevelWindow(const CGHeroInstance *hero, PrimarySkill pskill, std::vector<SecondarySkill> &skills, std::function<void(ui32)> callback);
|
||||||
|
|||||||
@@ -527,7 +527,12 @@
|
|||||||
/// Strength of generic secondary skill specialties ( "secondary" : "skillName" ) per level
|
/// Strength of generic secondary skill specialties ( "secondary" : "skillName" ) per level
|
||||||
"specialtySecondarySkillGrowth" : 5,
|
"specialtySecondarySkillGrowth" : 5,
|
||||||
/// Strength of generic creature specialties ( "creature" : "creatureName" ) per level
|
/// Strength of generic creature specialties ( "creature" : "creatureName" ) per level
|
||||||
"specialtyCreatureGrowth" : 5
|
"specialtyCreatureGrowth" : 5,
|
||||||
|
|
||||||
|
/// Maximum amount of skills that can be offered to hero on levelup
|
||||||
|
"levelupTotalSkillsAmount" : 2,
|
||||||
|
/// Maximum amount of advanced or expert skills that can be offered to hero on levelup
|
||||||
|
"levelupUpgradedSkillsAmount" : 1
|
||||||
},
|
},
|
||||||
|
|
||||||
"towns":
|
"towns":
|
||||||
|
|||||||
@@ -38,20 +38,22 @@
|
|||||||
"type" : "object",
|
"type" : "object",
|
||||||
"additionalProperties" : false,
|
"additionalProperties" : false,
|
||||||
"properties" : {
|
"properties" : {
|
||||||
"perPlayerOnMapCap" : { "type" : "number" },
|
"perPlayerOnMapCap" : { "type" : "number" },
|
||||||
"perPlayerTotalCap" : { "type" : "number" },
|
"perPlayerTotalCap" : { "type" : "number" },
|
||||||
"retreatOnWinWithoutTroops" : { "type" : "boolean" },
|
"retreatOnWinWithoutTroops" : { "type" : "boolean" },
|
||||||
"startingStackChances" : { "type" : "array" },
|
"startingStackChances" : { "type" : "array" },
|
||||||
"backpackSize" : { "type" : "number" },
|
"backpackSize" : { "type" : "number" },
|
||||||
"tavernInvite" : { "type" : "boolean" },
|
"tavernInvite" : { "type" : "boolean" },
|
||||||
"minimalPrimarySkills" : { "type" : "array" },
|
"minimalPrimarySkills" : { "type" : "array" },
|
||||||
"movementCostBase" : { "type" : "number" },
|
"movementCostBase" : { "type" : "number" },
|
||||||
"movementPointsLand" : { "type" : "array" },
|
"movementPointsLand" : { "type" : "array" },
|
||||||
"movementPointsSea" : { "type" : "array" },
|
"movementPointsSea" : { "type" : "array" },
|
||||||
"skillPerHero" : { "type" : "number" },
|
"skillPerHero" : { "type" : "number" },
|
||||||
"specialtyCreatureGrowth" : { "type" : "number" },
|
"specialtyCreatureGrowth" : { "type" : "number" },
|
||||||
"specialtySecondarySkillGrowth" : { "type" : "number" },
|
"specialtySecondarySkillGrowth" : { "type" : "number" },
|
||||||
"baseScoutingRange" : { "type" : "number" }
|
"baseScoutingRange" : { "type" : "number" },
|
||||||
|
"levelupTotalSkillsAmount" : { "type" : "number" },
|
||||||
|
"levelupUpgradedSkillsAmount" : { "type" : "number" }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"towns" : {
|
"towns" : {
|
||||||
|
|||||||
@@ -86,6 +86,8 @@ const std::vector<GameSettings::SettingOption> GameSettings::settingProperties =
|
|||||||
{EGameSettings::HEROES_SKILL_PER_HERO, "heroes", "skillPerHero" },
|
{EGameSettings::HEROES_SKILL_PER_HERO, "heroes", "skillPerHero" },
|
||||||
{EGameSettings::HEROES_SPECIALTY_CREATURE_GROWTH, "heroes", "specialtyCreatureGrowth" },
|
{EGameSettings::HEROES_SPECIALTY_CREATURE_GROWTH, "heroes", "specialtyCreatureGrowth" },
|
||||||
{EGameSettings::HEROES_SPECIALTY_SECONDARY_SKILL_GROWTH, "heroes", "specialtySecondarySkillGrowth" },
|
{EGameSettings::HEROES_SPECIALTY_SECONDARY_SKILL_GROWTH, "heroes", "specialtySecondarySkillGrowth" },
|
||||||
|
{EGameSettings::LEVEL_UP_TOTAL_SKILLS_AMOUNT, "heroes", "levelupTotalSkillsAmount" },
|
||||||
|
{EGameSettings::LEVEL_UP_UPGRADED_SKILLS_AMOUNT, "heroes", "levelupUpgradedSkillsAmount" },
|
||||||
{EGameSettings::MAP_FORMAT_ARMAGEDDONS_BLADE, "mapFormat", "armageddonsBlade" },
|
{EGameSettings::MAP_FORMAT_ARMAGEDDONS_BLADE, "mapFormat", "armageddonsBlade" },
|
||||||
{EGameSettings::MAP_FORMAT_CHRONICLES, "mapFormat", "chronicles" },
|
{EGameSettings::MAP_FORMAT_CHRONICLES, "mapFormat", "chronicles" },
|
||||||
{EGameSettings::MAP_FORMAT_HORN_OF_THE_ABYSS, "mapFormat", "hornOfTheAbyss" },
|
{EGameSettings::MAP_FORMAT_HORN_OF_THE_ABYSS, "mapFormat", "hornOfTheAbyss" },
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ enum class EGameSettings
|
|||||||
HEROES_SKILL_PER_HERO,
|
HEROES_SKILL_PER_HERO,
|
||||||
HEROES_SPECIALTY_CREATURE_GROWTH,
|
HEROES_SPECIALTY_CREATURE_GROWTH,
|
||||||
HEROES_SPECIALTY_SECONDARY_SKILL_GROWTH,
|
HEROES_SPECIALTY_SECONDARY_SKILL_GROWTH,
|
||||||
|
LEVEL_UP_TOTAL_SKILLS_AMOUNT,
|
||||||
|
LEVEL_UP_UPGRADED_SKILLS_AMOUNT,
|
||||||
INTERFACE_PLAYER_COLORED_BACKGROUND,
|
INTERFACE_PLAYER_COLORED_BACKGROUND,
|
||||||
MAP_FORMAT_ARMAGEDDONS_BLADE,
|
MAP_FORMAT_ARMAGEDDONS_BLADE,
|
||||||
MAP_FORMAT_CHRONICLES,
|
MAP_FORMAT_CHRONICLES,
|
||||||
|
|||||||
@@ -1422,26 +1422,16 @@ std::vector<SecondarySkill> CGHeroInstance::getLevelupSkillCandidates(IGameRando
|
|||||||
basicAndAdv.insert(elem.first);
|
basicAndAdv.insert(elem.first);
|
||||||
none.erase(elem.first);
|
none.erase(elem.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!basicAndAdv.empty())
|
int maxUpgradedSkills = cb->getSettings().getInteger(EGameSettings::LEVEL_UP_UPGRADED_SKILLS_AMOUNT);
|
||||||
|
while (skills.size() < maxUpgradedSkills && !basicAndAdv.empty())
|
||||||
{
|
{
|
||||||
skills.push_back(gameRandomizer.rollSecondarySkillForLevelup(this, basicAndAdv));
|
skills.push_back(gameRandomizer.rollSecondarySkillForLevelup(this, basicAndAdv));
|
||||||
basicAndAdv.erase(skills.back());
|
basicAndAdv.erase(skills.back());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!none.empty())
|
int maxTotalSkills = cb->getSettings().getInteger(EGameSettings::LEVEL_UP_TOTAL_SKILLS_AMOUNT);
|
||||||
{
|
while (skills.size() < maxTotalSkills && !none.empty())
|
||||||
skills.push_back(gameRandomizer.rollSecondarySkillForLevelup(this, none));
|
|
||||||
none.erase(skills.back());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!basicAndAdv.empty() && skills.size() < 2)
|
|
||||||
{
|
|
||||||
skills.push_back(gameRandomizer.rollSecondarySkillForLevelup(this, basicAndAdv));
|
|
||||||
basicAndAdv.erase(skills.back());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!none.empty() && skills.size() < 2)
|
|
||||||
{
|
{
|
||||||
skills.push_back(gameRandomizer.rollSecondarySkillForLevelup(this, none));
|
skills.push_back(gameRandomizer.rollSecondarySkillForLevelup(this, none));
|
||||||
none.erase(skills.back());
|
none.erase(skills.back());
|
||||||
|
|||||||
Reference in New Issue
Block a user