1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-23 22:37:55 +02:00

Merge pull request #5815 from Laserlicht/cheats

add skill cheat / allow more than 8 skills
This commit is contained in:
Ivan Savenko
2025-06-20 17:56:41 +03:00
committed by GitHub
18 changed files with 151 additions and 18 deletions

View File

@@ -20,6 +20,7 @@
#include "../../../lib/CCreatureHandler.h"
#include "../../../lib/GameLibrary.h"
#include "../../../lib/StartInfo.h"
#include "../../../lib/GameSettings.h"
#include "../../../lib/filesystem/Filesystem.h"
#include "../Goals/ExecuteHeroChain.h"
#include "../Goals/BuildThis.h"
@@ -581,7 +582,7 @@ float RewardEvaluator::evaluateWitchHutSkillScore(const CGObjectInstance * hut,
return role == HeroRole::SCOUT ? 2 : 0;
if(hero->getSecSkillLevel(skill) != MasteryLevel::NONE
|| hero->secSkills.size() >= GameConstants::SKILL_PER_HERO)
|| static_cast<int>(hero->secSkills.size()) >= cb->getSettings().getInteger(EGameSettings::HEROES_SKILL_PER_HERO))
return 0;
auto score = ai->heroManager->evaluateSecSkill(skill, hero);

View File

@@ -435,6 +435,9 @@
"vcmi.townWindow.upgradeAll.notAllUpgradable" : "Not enough resources to upgrade all creatures. Do you want to upgrade following creatures?",
"vcmi.townWindow.upgradeAll.notUpgradable" : "Not enough resources to upgrade any creature.",
"vcmi.kingdomOverview.secSkillOverflow.hover" : "More skills",
"vcmi.kingdomOverview.secSkillOverflow.help" : "{More skills}\n\nThis hero has more skills.\nYou can see all of them in the hero overview.",
"vcmi.logicalExpressions.anyOf" : "Any of the following:",
"vcmi.logicalExpressions.allOf" : "All of the following:",
"vcmi.logicalExpressions.noneOf" : "None of the following:",

View File

@@ -425,6 +425,9 @@
"vcmi.townWindow.upgradeAll.notAllUpgradable" : "Nicht genügend Ressourcen um alle Kreaturen aufzurüsten. Folgende Kreaturen aufrüsten?",
"vcmi.townWindow.upgradeAll.notUpgradable" : "Nicht genügend Ressourcen um mindestens eine Kreatur aufzurüsten.",
"vcmi.kingdomOverview.secSkillOverflow.hover" : "Mehr skills",
"vcmi.kingdomOverview.secSkillOverflow.help" : "{Mehr skills}\n\nDieser Held hat mehr Skills.\nIn der Heldenübersicht können alle eingesehen werden.",
"vcmi.logicalExpressions.anyOf" : "Eines der folgenden:",
"vcmi.logicalExpressions.allOf" : "Alles der folgenden:",
"vcmi.logicalExpressions.noneOf" : "Keines der folgenden:",

View File

@@ -81,7 +81,7 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2,
primSkillValues[leftRight].push_back(std::make_shared<CLabel>(352 + (qeLayout ? 96 : 93) * leftRight, (qeLayout ? 22 : 35) + (qeLayout ? 26 : 36) * m, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE));
for(int m=0; m < hero->secSkills.size(); ++m)
for(int m=0; m < std::min(static_cast<int>(hero->secSkills.size()), 8); ++m)
secSkills[leftRight].push_back(std::make_shared<CSecSkillPlace>(Point(32 + 36 * m + 454 * leftRight, qeLayout ? 83 : 88), CSecSkillPlace::ImageSize::SMALL,
hero->secSkills[m].first, hero->secSkills[m].second));
@@ -377,6 +377,10 @@ void CExchangeWindow::questLogShortcut()
void CExchangeWindow::update()
{
const bool qeLayout = isQuickExchangeLayoutAvailable();
OBJECT_CONSTRUCTION;
CWindowWithArtifacts::update();
for(size_t leftRight : {0, 1})
@@ -389,8 +393,27 @@ void CExchangeWindow::update()
primSkillValues[leftRight][m]->setText(std::to_string(value));
}
for(int m=0; m < hero->secSkills.size(); ++m)
int slots = 8;
bool isMoreSkillsThanSlots = hero->secSkills.size() > slots;
for(int m=0; m < std::min(static_cast<int>(hero->secSkills.size()), 8); ++m)
{
if(m == slots - 1)
{
if(isMoreSkillsThanSlots)
{
Rect r(Point(32 + 36 * m + 454 * leftRight, qeLayout ? 83 : 88), Point(34, 28));
secSkillsFull[leftRight] = std::make_shared<CMultiLineLabel>(r, EFonts::FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, "...");
secSkillsFullArea[leftRight] = std::make_shared<LRClickableAreaWText>(r, LIBRARY->generaltexth->translate("vcmi.kingdomOverview.secSkillOverflow.hover"), LIBRARY->generaltexth->translate("vcmi.kingdomOverview.secSkillOverflow.help"));
secSkills[leftRight][m]->setSkill(SecondarySkill::NONE);
continue;
}
else
{
secSkillsFull[leftRight].reset();
secSkillsFullArea[leftRight].reset();
}
}
int id = hero->secSkills[m].first;
int level = hero->secSkills[m].second;

View File

@@ -13,6 +13,8 @@
#include "../widgets/CExchangeController.h"
class CGarrisonSlot;
class CMultiLineLabel;
class LRClickableAreaWText;
class CExchangeWindow : public CStatusbarWindow, public IGarrisonHolder, public CWindowWithArtifacts
{
@@ -27,6 +29,8 @@ class CExchangeWindow : public CStatusbarWindow, public IGarrisonHolder, public
std::vector<std::shared_ptr<LRClickableAreaWTextComp>> primSkillAreas;
std::array<std::vector<std::shared_ptr<CSecSkillPlace>>, 2> secSkills;
std::array<std::shared_ptr<CMultiLineLabel>, 2> secSkillsFull;
std::array<std::shared_ptr<LRClickableAreaWText>, 2> secSkillsFullArea;
std::array<std::shared_ptr<CHeroArea>, 2> heroAreas;
std::array<std::shared_ptr<LRClickableAreaWText>, 2> specialtyAreas;

View File

@@ -28,6 +28,7 @@
#include "../widgets/CGarrisonInt.h"
#include "../widgets/TextControls.h"
#include "../widgets/Buttons.h"
#include "../widgets/Slider.h"
#include "../render/IRenderHandler.h"
#include "../lib/CConfigHandler.h"
@@ -148,16 +149,27 @@ CHeroWindow::CHeroWindow(const CGHeroInstance * hero)
expValue = std::make_shared<CLabel>(68, 252);
manaValue = std::make_shared<CLabel>(211, 252);
if(hero->secSkills.size() > 8)
{
auto divisionRoundUp = [](int x, int y){ return (x + (y - 1)) / y; };
int lines = divisionRoundUp(hero->secSkills.size(), 2);
secSkillSlider = std::make_shared<CSlider>(Point(284, 276), 189, [this](int val){ CHeroWindow::update(); }, 4, lines, 0, Orientation::VERTICAL, CSlider::BROWN);
secSkillSlider->setPanningStep(48);
secSkillSlider->setScrollBounds(Rect(-266, 0, secSkillSlider->pos.x - pos.x + secSkillSlider->pos.w, secSkillSlider->pos.h));
}
for(int i = 0; i < std::min<size_t>(hero->secSkills.size(), 8u); ++i)
{
Rect r = Rect(i%2 == 0 ? 18 : 162, 276 + 48 * (i/2), 136, 42);
bool isSmallBox = (secSkillSlider && i%2 == 1);
Rect r = Rect(i%2 == 0 ? 18 : 162, 276 + 48 * (i/2), isSmallBox ? 120 : 136, 42);
secSkills.emplace_back(std::make_shared<CSecSkillPlace>(r.topLeft(), CSecSkillPlace::ImageSize::MEDIUM));
int x = (i % 2) ? 212 : 68;
int y = 280 + 48 * (i/2);
int width = isSmallBox ? 71 : 87;
secSkillValues.push_back(std::make_shared<CLabel>(x, y, FONT_SMALL, ETextAlignment::TOPLEFT));
secSkillNames.push_back(std::make_shared<CLabel>(x, y+20, FONT_SMALL, ETextAlignment::TOPLEFT));
secSkillValues.push_back(std::make_shared<CLabel>(x, y, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, "", width));
secSkillNames.push_back(std::make_shared<CLabel>(x, y+20, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, "", width));
}
// various texts
@@ -175,6 +187,8 @@ CHeroWindow::CHeroWindow(const CGHeroInstance * hero)
void CHeroWindow::update()
{
OBJECT_CONSTRUCTION;
CWindowWithArtifacts::update();
auto & heroscrn = LIBRARY->generaltexth->heroscrn;
assert(curHero);
@@ -195,7 +209,6 @@ void CHeroWindow::update()
portraitImage->setFrame(curHero->getIconIndex());
{
OBJECT_CONSTRUCTION;
if(!garr)
{
bool removableTroops = curHero->getOwner() == GAME->interface()->playerID;
@@ -235,7 +248,15 @@ void CHeroWindow::update()
//secondary skills support
for(size_t g=0; g < secSkills.size(); ++g)
{
SecondarySkill skill = curHero->secSkills[g].first;
int offset = secSkillSlider ? secSkillSlider->getValue() * 2 : 0;
if(curHero->secSkills.size() < g + offset + 1)
{
secSkillNames[g]->setText("");
secSkillValues[g]->setText("");
secSkills[g]->setSkill(SecondarySkill::NONE);
break;
}
SecondarySkill skill = curHero->secSkills[g + offset].first;
int level = curHero->getSecSkillLevel(skill);
std::string skillName = skill.toEntity(LIBRARY)->getNameTranslated();
std::string skillValue = LIBRARY->generaltexth->levels[level-1];

View File

@@ -33,6 +33,7 @@ class CToggleGroup;
class CGStatusBar;
class CTextBox;
class CGarrisonInt;
class CSlider;
/// Button which switches hero selection
class CHeroSwitcher : public CIntObject
@@ -77,6 +78,7 @@ class CHeroWindow : public CStatusbarWindow, public IGarrisonHolder, public CWin
std::vector< std::shared_ptr<CSecSkillPlace>> secSkills;
std::vector<std::shared_ptr<CLabel>> secSkillNames;
std::vector<std::shared_ptr<CLabel>> secSkillValues;
std::shared_ptr<CSlider> secSkillSlider;
std::shared_ptr<CButton> quitButton;
std::shared_ptr<CTextBox> dismissLabel;

View File

@@ -41,7 +41,8 @@
#include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/mapObjects/CGTownInstance.h"
#include "../../lib/mapObjects/MiscObjects.h"
#include "../../lib/texts/CGeneralTextHandler.h"
#include "texts/CGeneralTextHandler.h"
#include "../../lib/GameSettings.h"
static const std::string OVERVIEW_BACKGROUND = "OvCast.pcx";
static const size_t OVERVIEW_SIZE = 4;
@@ -983,8 +984,17 @@ CHeroItem::CHeroItem(const CGHeroInstance * Hero)
heroInfo.push_back(std::make_shared<InfoBox>(Point(78+(int)i*36, 26), InfoBox::POS_DOWN, InfoBox::SIZE_SMALL, data));
}
for(size_t i=0; i<GameConstants::SKILL_PER_HERO; i++)
int slots = 8;
bool isMoreSkillsThanSlots = hero->secSkills.size() > slots;
for(size_t i=0; i<slots; i++)
{
if(isMoreSkillsThanSlots && i == slots - 1)
{
Rect r(Point(410+(int)i*36, 5), Point(34, 28));
heroInfoFull = std::make_shared<CMultiLineLabel>(r, EFonts::FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, "...");
heroInfoFullArea = std::make_shared<LRClickableAreaWText>(r, LIBRARY->generaltexth->translate("vcmi.kingdomOverview.secSkillOverflow.hover"), LIBRARY->generaltexth->translate("vcmi.kingdomOverview.secSkillOverflow.help"));
continue;
}
auto data = std::make_shared<InfoBoxHeroData>(IInfoBoxData::HERO_SECONDARY_SKILL, hero, (int)i);
heroInfo.push_back(std::make_shared<InfoBox>(Point(410+(int)i*36, 5), InfoBox::POS_NONE, InfoBox::SIZE_SMALL, data));
}

View File

@@ -32,6 +32,7 @@ class CTabbedInt;
class CGStatusBar;
class CGarrisonInt;
class CMultiLineLabel;
class LRClickableAreaWText;
class CKingdHeroList;
class CKingdTownList;
@@ -310,6 +311,8 @@ class CHeroItem : public CIntObject, public IGarrisonHolder
std::shared_ptr<CToggleGroup> artButtons;
std::vector<std::shared_ptr<InfoBox>> heroInfo;
std::shared_ptr<CMultiLineLabel> heroInfoFull;
std::shared_ptr<LRClickableAreaWText> heroInfoFullArea;
std::shared_ptr<MoraleLuckBox> morale;
std::shared_ptr<MoraleLuckBox> luck;

View File

@@ -508,6 +508,9 @@
/// movement points hero can get on start of the turn when on sea, depending on speed of slowest creature (0-based list)
"movementPointsSea" : [ 1500 ],
/// maximal secondary skills per hero
"skillPerHero" : 8,
/// Base scouting range for hero without any range modifiers
"baseScoutingRange" : 5,

View File

@@ -48,6 +48,7 @@
"movementCostBase" : { "type" : "number" },
"movementPointsLand" : { "type" : "array" },
"movementPointsSea" : { "type" : "array" },
"skillPerHero" : { "type" : "number" },
"specialtyCreatureGrowth" : { "type" : "number" },
"specialtySecondarySkillGrowth" : { "type" : "number" },
"baseScoutingRange" : { "type" : "number" }

View File

@@ -10,6 +10,17 @@ To use cheat code, press `Tab` key or click/tap on status bar to open game chat
`nwcthereisnospoon`, `nwcmidichlorians`, `nwctim`, `vcmiistari` or `vcmispells` - give a spell book, all spells and 999 mana to currently selected hero. Also allows casting spell up to 100 times per combat round
### Secondary Skills
`vcmiskill <skillID> <mastery>` - give a secondary skill to currently selected hero
Examples:
`vcmiskill learning` - give expert level learning skill
`vcmiskill leadership 2` - give advanced level leadership skill
`vcmiskill wisdom 0` - remove wisdom skill
`vcmiskill every` - give all skills on expert level
`vcmiskill every 0` - remove all skills
### Army
`nwctrinity`, `nwcpadme`, `nwcavertingoureyes`, `vcmiainur` or `vcmiarchangel` - give 5 Archangels in every empty slot (to currently selected hero)

View File

@@ -87,6 +87,7 @@ const std::vector<GameSettings::SettingOption> GameSettings::settingProperties =
{EGameSettings::HEROES_MOVEMENT_COST_BASE, "heroes", "movementCostBase" },
{EGameSettings::HEROES_MOVEMENT_POINTS_LAND, "heroes", "movementPointsLand" },
{EGameSettings::HEROES_MOVEMENT_POINTS_SEA, "heroes", "movementPointsSea" },
{EGameSettings::HEROES_SKILL_PER_HERO, "heroes", "skillPerHero" },
{EGameSettings::HEROES_SPECIALTY_CREATURE_GROWTH, "heroes", "specialtyCreatureGrowth" },
{EGameSettings::HEROES_SPECIALTY_SECONDARY_SKILL_GROWTH, "heroes", "specialtySecondarySkillGrowth" },
{EGameSettings::MAP_FORMAT_ARMAGEDDONS_BLADE, "mapFormat", "armageddonsBlade" },

View File

@@ -60,6 +60,7 @@ enum class EGameSettings
HEROES_MOVEMENT_COST_BASE,
HEROES_MOVEMENT_POINTS_LAND,
HEROES_MOVEMENT_POINTS_SEA,
HEROES_SKILL_PER_HERO,
HEROES_SPECIALTY_CREATURE_GROWTH,
HEROES_SPECIALTY_SECONDARY_SKILL_GROWTH,
INTERFACE_PLAYER_COLORED_BACKGROUND,

View File

@@ -33,7 +33,6 @@ namespace GameConstants
constexpr int BATTLE_SHOOTING_PENALTY_DISTANCE = 10; //if the distance is > than this, then shooting stack has distance penalty
constexpr int BATTLE_SHOOTING_RANGE_DISTANCE = std::numeric_limits<uint8_t>::max(); // used when shooting stack has no shooting range limit
constexpr int ARMY_SIZE = 7;
constexpr int SKILL_PER_HERO = 8;
constexpr ui32 HERO_HIGH_LEVEL = 10; // affects primary skill upgrade order
constexpr int SKILL_QUANTITY=28;

View File

@@ -31,6 +31,7 @@
#include "../CCreatureHandler.h"
#include "../mapping/CMap.h"
#include "../StartInfo.h"
#include "../GameSettings.h"
#include "CGTownInstance.h"
#include "../entities/artifact/ArtifactUtils.h"
#include "../entities/artifact/CArtifact.h"
@@ -167,7 +168,7 @@ int3 CGHeroInstance::convertFromVisitablePos(const int3 & position) const
bool CGHeroInstance::canLearnSkill() const
{
return secSkills.size() < GameConstants::SKILL_PER_HERO;
return secSkills.size() < cb->getSettings().getInteger(EGameSettings::HEROES_SKILL_PER_HERO);
}
bool CGHeroInstance::canLearnSkill(const SecondarySkill & which) const

View File

@@ -17,7 +17,9 @@
#include "../TurnTimerHandler.h"
#include "../../lib/CPlayerState.h"
#include "../../lib/CSkillHandler.h"
#include "../../lib/StartInfo.h"
#include "../../lib/constants/Enumerations.h"
#include "../../lib/entities/artifact/CArtHandler.h"
#include "../../lib/entities/building/CBuilding.h"
#include "../../lib/entities/hero/CHeroHandler.h"
@@ -694,6 +696,46 @@ void PlayerMessageProcessor::cheatMaxMorale(PlayerColor player, const CGHeroInst
gameHandler->giveHeroBonus(&gb);
}
void PlayerMessageProcessor::cheatSkill(PlayerColor player, const CGHeroInstance * hero, std::vector<std::string> words)
{
if (!hero)
return;
std::string identifier;
try
{
identifier = words.at(0);
}
catch(std::logic_error&)
{
return;
}
MasteryLevel::Type mastery;
try
{
mastery = static_cast<MasteryLevel::Type>(std::stoi(words.at(1)));
}
catch(std::logic_error&)
{
mastery = MasteryLevel::Type::EXPERT;
}
if(identifier == "every")
{
for(const auto & skill : LIBRARY->skillh->objects)
gameHandler->changeSecSkill(hero, SecondarySkill(skill->getId()), mastery, ChangeValueMode::ABSOLUTE);
return;
}
std::optional<int32_t> skillId = LIBRARY->identifiers()->getIdentifier(ModScope::scopeGame(), "skill", identifier, false);
if(!skillId.has_value())
return;
auto skill = SecondarySkill(skillId.value());
gameHandler->changeSecSkill(hero, skill, mastery, ChangeValueMode::ABSOLUTE);
}
bool PlayerMessageProcessor::handleCheatCode(const std::string & cheat, PlayerColor player, ObjectInstanceID currObj)
{
std::vector<std::string> words;
@@ -736,7 +778,8 @@ bool PlayerMessageProcessor::handleCheatCode(const std::string & cheat, PlayerCo
"vcmiluck", "nwcfollowthewhiterabbit", "nwccastleanthrax",
"vcmimorale", "nwcmorpheus", "nwcmuchrejoicing",
"vcmigod", "nwctheone",
"vcmiscrolls"
"vcmiscrolls",
"vcmiskill"
};
if(vstd::contains(localCheats, cheatName))
@@ -829,7 +872,8 @@ void PlayerMessageProcessor::executeCheatCode(const std::string & cheatName, Pla
cheatMovement(player, hero, { });
cheatFly(player, hero);
};
const auto & doColorSchemeChange = [&](ColorScheme filter) { cheatColorSchemeChange(player, filter); };
const auto & doCheatColorSchemeChange = [&](ColorScheme filter) { cheatColorSchemeChange(player, filter); };
const auto & doCheatSkill = [&]() { cheatSkill(player, hero, words); };
std::map<std::string, std::function<void()>> callbacks = {
{"vcmiainur", [&] () {doCheatGiveArmyFixed({ "archangel", "5" });} },
@@ -909,9 +953,10 @@ void PlayerMessageProcessor::executeCheatCode(const std::string & cheatName, Pla
{"vcmigod", doCheatTheOne },
{"nwctheone", doCheatTheOne },
{"vcmiscrolls", doCheatGiveScrolls },
{"vcmicolor", [&] () {doColorSchemeChange(ColorScheme::H2_SCHEME);} },
{"nwcphisherprice", [&] () {doColorSchemeChange(ColorScheme::H2_SCHEME);} },
{"vcmigray", [&] () {doColorSchemeChange(ColorScheme::GRAYSCALE);} },
{"vcmicolor", [&] () {doCheatColorSchemeChange(ColorScheme::H2_SCHEME);} },
{"nwcphisherprice", [&] () {doCheatColorSchemeChange(ColorScheme::H2_SCHEME);} },
{"vcmigray", [&] () {doCheatColorSchemeChange(ColorScheme::GRAYSCALE);} },
{"vcmiskill", doCheatSkill },
};
assert(callbacks.count(cheatName));

View File

@@ -59,6 +59,7 @@ class PlayerMessageProcessor
void cheatMaxLuck(PlayerColor player, const CGHeroInstance * hero);
void cheatMaxMorale(PlayerColor player, const CGHeroInstance * hero);
void cheatFly(PlayerColor player, const CGHeroInstance * hero);
void cheatSkill(PlayerColor player, const CGHeroInstance * hero, std::vector<std::string> words);
void commandExit(PlayerColor player, const std::vector<std::string> & words);
void commandKick(PlayerColor player, const std::vector<std::string> & words);