mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-24 08:32:34 +02:00
Merge pull request #2882 from Nordsoft91/ai-cheating
Add basic system to give more advantages for ai player
This commit is contained in:
commit
a36450e2d1
70
config/difficulty.json
Normal file
70
config/difficulty.json
Normal file
@ -0,0 +1,70 @@
|
||||
//Configured difficulty
|
||||
{
|
||||
"human":
|
||||
{
|
||||
"pawn":
|
||||
{
|
||||
"resources": { "wood" : 30, "mercury": 15, "ore": 30, "sulfur": 15, "crystal": 15, "gems": 15, "gold": 30000, "mithril": 0 },
|
||||
"globalBonuses": [],
|
||||
"battleBonuses": []
|
||||
},
|
||||
"knight":
|
||||
{
|
||||
"resources": { "wood" : 20, "mercury": 10, "ore": 20, "sulfur": 10, "crystal": 10, "gems": 10, "gold": 20000, "mithril": 0 },
|
||||
"globalBonuses": [],
|
||||
"battleBonuses": []
|
||||
},
|
||||
"rook":
|
||||
{
|
||||
"resources": { "wood" : 15, "mercury": 7, "ore": 15, "sulfur": 7, "crystal": 7, "gems": 7, "gold": 15000, "mithril": 0 },
|
||||
"globalBonuses": [],
|
||||
"battleBonuses": []
|
||||
},
|
||||
"queen":
|
||||
{
|
||||
"resources": { "wood" : 10, "mercury": 4, "ore": 10, "sulfur": 4, "crystal": 4, "gems": 4, "gold": 10000, "mithril": 0 },
|
||||
"globalBonuses": [],
|
||||
"battleBonuses": []
|
||||
},
|
||||
"king":
|
||||
{
|
||||
"resources": { "wood" : 0, "mercury": 0, "ore": 0 , "sulfur": 0, "crystal": 0, "gems": 0, "gold": 0, "mithril": 0 },
|
||||
"globalBonuses": [],
|
||||
"battleBonuses": []
|
||||
}
|
||||
},
|
||||
"ai":
|
||||
{
|
||||
"pawn":
|
||||
{
|
||||
"resources": { "wood" : 5, "mercury": 2, "ore": 5, "sulfur": 2, "crystal": 2, "gems": 2, "gold": 5000, "mithril": 0 },
|
||||
"globalBonuses": [],
|
||||
"battleBonuses": []
|
||||
},
|
||||
"knight":
|
||||
{
|
||||
"resources": { "wood" : 10, "mercury": 4, "ore": 10, "sulfur": 4, "crystal": 4, "gems": 4, "gold": 7500, "mithril": 0 },
|
||||
"globalBonuses": [],
|
||||
"battleBonuses": []
|
||||
},
|
||||
"rook":
|
||||
{
|
||||
"resources": { "wood" : 15, "mercury": 7, "ore": 15, "sulfur": 7, "crystal": 7, "gems": 7, "gold": 10000, "mithril": 0 },
|
||||
"globalBonuses": [],
|
||||
"battleBonuses": []
|
||||
},
|
||||
"queen":
|
||||
{
|
||||
"resources": { "wood" : 15, "mercury": 7, "ore": 15, "sulfur": 7, "crystal": 7, "gems": 7, "gold": 10000, "mithril": 0 },
|
||||
"globalBonuses": [],
|
||||
"battleBonuses": []
|
||||
},
|
||||
"king":
|
||||
{
|
||||
"resources": { "wood" : 15, "mercury": 7, "ore": 15, "sulfur": 7, "crystal": 7, "gems": 7, "gold": 10000, "mithril": 0 },
|
||||
"globalBonuses": [],
|
||||
"battleBonuses": []
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -1,31 +0,0 @@
|
||||
// Starting resources, ordered by difficulty level (0 to 4)
|
||||
{
|
||||
"difficulty":
|
||||
[
|
||||
{
|
||||
"human": { "wood" : 30, "mercury": 15, "ore": 30, "sulfur": 15, "crystal": 15, "gems": 15, "gold": 30000, "mithril": 0 },
|
||||
"ai": { "wood" : 5, "mercury": 2, "ore": 5, "sulfur": 2, "crystal": 2, "gems": 2, "gold": 5000, "mithril": 0 }
|
||||
},
|
||||
|
||||
{
|
||||
"human": { "wood" : 20, "mercury": 10, "ore": 20, "sulfur": 10, "crystal": 10, "gems": 10, "gold": 20000, "mithril": 0 },
|
||||
"ai": { "wood" : 10, "mercury": 4, "ore": 10, "sulfur": 4, "crystal": 4, "gems": 4, "gold": 7500, "mithril": 0 }
|
||||
},
|
||||
|
||||
{
|
||||
"human": { "wood" : 15, "mercury": 7, "ore": 15, "sulfur": 7, "crystal": 7, "gems": 7, "gold": 15000, "mithril": 0 },
|
||||
"ai": { "wood" : 15, "mercury": 7, "ore": 15, "sulfur": 7, "crystal": 7, "gems": 7, "gold": 10000, "mithril": 0 }
|
||||
},
|
||||
|
||||
{
|
||||
"human": { "wood" : 10, "mercury": 4, "ore": 10, "sulfur": 4, "crystal": 4, "gems": 4, "gold": 10000, "mithril": 0 },
|
||||
"ai": { "wood" : 15, "mercury": 7, "ore": 15, "sulfur": 7, "crystal": 7, "gems": 7, "gold": 10000, "mithril": 0 }
|
||||
},
|
||||
|
||||
{
|
||||
"human": { "wood" : 0, "mercury": 0, "ore": 0 , "sulfur": 0, "crystal": 0, "gems": 0, "gold": 0, "mithril": 0 },
|
||||
"ai": { "wood" : 15, "mercury": 7, "ore": 15, "sulfur": 7, "crystal": 7, "gems": 7, "gold": 10000, "mithril": 0 }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
67
docs/modders/Difficulty.md
Normal file
67
docs/modders/Difficulty.md
Normal file
@ -0,0 +1,67 @@
|
||||
< [Documentation](../Readme.md) / [Modding](Readme.md) / Difficulty
|
||||
|
||||
|
||||
Since VCMI 1.4.0 there are more capabilities to configure difficulty parameters.
|
||||
It means, that modders can give different bonuses to AI or human players depending on selected difficulty
|
||||
|
||||
Difficulty configuration is located in [config/difficulty.json](../config/difficulty.json) file and can be overriden by mods.
|
||||
|
||||
## Format summary
|
||||
|
||||
``` javascript
|
||||
{
|
||||
"human": //parameters impacting human players only
|
||||
{
|
||||
"pawn": //parameters for specific difficulty
|
||||
{
|
||||
//starting resources
|
||||
"resources": { "wood" : 30, "mercury": 15, "ore": 30, "sulfur": 15, "crystal": 15, "gems": 15, "gold": 30000, "mithril": 0 },
|
||||
//bonuses will be given to player globaly
|
||||
"globalBonuses": [],
|
||||
//bonuses will be given to player every battle
|
||||
"battleBonuses": []
|
||||
},
|
||||
"knight": {},
|
||||
"rook": {},
|
||||
"queen": {},
|
||||
"king": {},
|
||||
},
|
||||
"ai": //parameters impacting AI players only
|
||||
{
|
||||
"pawn": {}, //parameters for specific difficulty
|
||||
"knight": {},
|
||||
"rook": {},
|
||||
"queen": {},
|
||||
"king": {},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Bonuses
|
||||
|
||||
It's possible to specify bonuses of two types: `globalBonuses` and `battleBonuses`.
|
||||
|
||||
Both are arrays containing any amount of bonuses, each can be described as usual bonus. See details in [bonus documenation](Bonus_Format.md).
|
||||
|
||||
`globalBonuses` are given to player on the begining and depending on bonus configuration, it can behave diffierently.
|
||||
|
||||
`battleBonuses` are given to player during the battles, but *only for battles with neutral forces*. So it won't be provided to player for PvP battles and battles versus AI heroes/castles/garrisons. To avoid cumulative effects or unexpected behavior it's recommended to specify bonus `duration` as `ONE_BATTLE`.
|
||||
|
||||
For both types of bonuses, `source` should be specified as `OTHER`.
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
{ //will give 150% extra health to all players' creatures if specified in "battleBonuses" array
|
||||
"type" : "STACK_HEALTH",
|
||||
"val" : 150,
|
||||
"valueType" : "PERCENT_TO_ALL",
|
||||
"duration" : "ONE_BATTLE",
|
||||
"sourceType" : "OTHER"
|
||||
},
|
||||
```
|
||||
|
||||
## Compatibility
|
||||
|
||||
Starting from VCMI 1.4 `startres.json` is not available anymore and will be ignored if present in any mod.
|
||||
Thus, `Resourceful AI` mod of version 1.2 won't work anymore.
|
@ -39,6 +39,7 @@ PlayerState::PlayerState(PlayerState && other) noexcept:
|
||||
std::swap(towns, other.towns);
|
||||
std::swap(dwellings, other.dwellings);
|
||||
std::swap(quests, other.quests);
|
||||
std::swap(battleBonuses, other.battleBonuses);
|
||||
}
|
||||
|
||||
PlayerState::~PlayerState() = default;
|
||||
|
@ -37,6 +37,7 @@ public:
|
||||
std::vector<ConstTransitivePtr<CGTownInstance> > towns;
|
||||
std::vector<ConstTransitivePtr<CGDwelling> > dwellings; //used for town growth
|
||||
std::vector<QuestInfo> quests; //store info about all received quests
|
||||
std::vector<Bonus> battleBonuses; //additional bonuses to be added during battle with neutrals
|
||||
|
||||
bool enteredWinningCheatCode, enteredLosingCheatCode; //if true, this player has entered cheat codes for loss / victory
|
||||
EPlayerStatus status;
|
||||
@ -82,6 +83,7 @@ public:
|
||||
h & visitedObjects;
|
||||
h & status;
|
||||
h & daysWithoutCastle;
|
||||
h & battleBonuses;
|
||||
h & enteredLosingCheatCode;
|
||||
h & enteredWinningCheatCode;
|
||||
h & static_cast<CBonusSystemNode&>(*this);
|
||||
|
@ -1573,7 +1573,7 @@ int32_t CBattleInfoCallback::battleGetSpellCost(const spells::Spell * sp, const
|
||||
}
|
||||
}
|
||||
|
||||
return ret - manaReduction + manaIncrease;
|
||||
return std::max(0, ret - manaReduction + manaIncrease);
|
||||
}
|
||||
|
||||
bool CBattleInfoCallback::battleHasShootingPenalty(const battle::Unit * shooter, BattleHex destHex) const
|
||||
|
@ -27,6 +27,8 @@ namespace GameConstants
|
||||
};
|
||||
|
||||
const std::string ALIGNMENT_NAMES [3] = {"good", "evil", "neutral"};
|
||||
|
||||
const std::string DIFFICULTY_NAMES [5] = {"pawn", "knight", "rook", "queen", "king"};
|
||||
}
|
||||
|
||||
namespace NPrimarySkill
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "../VCMI_Lib.h"
|
||||
#include "../battle/BattleInfo.h"
|
||||
#include "../campaign/CampaignState.h"
|
||||
#include "../constants/StringConstants.h"
|
||||
#include "../filesystem/ResourcePath.h"
|
||||
#include "../mapObjectConstructors/AObjectTypeHandler.h"
|
||||
#include "../mapObjectConstructors/CObjectClassesHandler.h"
|
||||
@ -455,7 +456,7 @@ void CGameState::init(const IMapService * mapService, StartInfo * si, Load::Prog
|
||||
initRandomFactionsForPlayers();
|
||||
randomizeMapObjects();
|
||||
placeStartingHeroes();
|
||||
initStartingResources();
|
||||
initDifficulty();
|
||||
initHeroes();
|
||||
initStartingBonus();
|
||||
initTowns();
|
||||
@ -657,6 +658,41 @@ void CGameState::initGlobalBonuses()
|
||||
VLC->creh->loadCrExpBon(globalEffects);
|
||||
}
|
||||
|
||||
void CGameState::initDifficulty()
|
||||
{
|
||||
logGlobal->debug("\tLoading difficulty settings");
|
||||
const JsonNode config = JsonUtils::assembleFromFiles("config/difficulty.json");
|
||||
|
||||
const JsonNode & difficultyAI(config["ai"][GameConstants::DIFFICULTY_NAMES[scenarioOps->difficulty]]);
|
||||
const JsonNode & difficultyHuman(config["human"][GameConstants::DIFFICULTY_NAMES[scenarioOps->difficulty]]);
|
||||
|
||||
auto setDifficulty = [](PlayerState & state, const JsonNode & json)
|
||||
{
|
||||
//set starting resources
|
||||
state.resources = TResources(json["resources"]);
|
||||
|
||||
//set global bonuses
|
||||
for(auto & jsonBonus : json["globalBonuses"].Vector())
|
||||
if(auto bonus = JsonUtils::parseBonus(jsonBonus))
|
||||
state.addNewBonus(bonus);
|
||||
|
||||
//set battle bonuses
|
||||
for(auto & jsonBonus : json["battleBonuses"].Vector())
|
||||
if(auto bonus = JsonUtils::parseBonus(jsonBonus))
|
||||
state.battleBonuses.push_back(*bonus);
|
||||
|
||||
};
|
||||
|
||||
for (auto & elem : players)
|
||||
{
|
||||
PlayerState &p = elem.second;
|
||||
setDifficulty(p, p.human ? difficultyHuman : difficultyAI);
|
||||
}
|
||||
|
||||
if (campaign)
|
||||
campaign->initStartingResources();
|
||||
}
|
||||
|
||||
void CGameState::initGrailPosition()
|
||||
{
|
||||
logGlobal->debug("\tPicking grail position");
|
||||
@ -813,30 +849,6 @@ void CGameState::removeHeroPlaceholders()
|
||||
}
|
||||
}
|
||||
|
||||
void CGameState::initStartingResources()
|
||||
{
|
||||
logGlobal->debug("\tSetting up resources");
|
||||
const JsonNode config(JsonPath::builtin("config/startres.json"));
|
||||
const JsonVector &vector = config["difficulty"].Vector();
|
||||
const JsonNode &level = vector[scenarioOps->difficulty];
|
||||
|
||||
TResources startresAI(level["ai"]);
|
||||
TResources startresHuman(level["human"]);
|
||||
|
||||
for (auto & elem : players)
|
||||
{
|
||||
PlayerState &p = elem.second;
|
||||
|
||||
if (p.human)
|
||||
p.resources = startresHuman;
|
||||
else
|
||||
p.resources = startresAI;
|
||||
}
|
||||
|
||||
if (campaign)
|
||||
campaign->initStartingResources();
|
||||
}
|
||||
|
||||
void CGameState::initHeroes()
|
||||
{
|
||||
for(auto hero : map->heroesOnMap) //heroes instances initialization
|
||||
|
@ -192,7 +192,7 @@ private:
|
||||
void placeStartingHeroes();
|
||||
void placeStartingHero(const PlayerColor & playerColor, const HeroTypeID & heroTypeId, int3 townPos);
|
||||
void removeHeroPlaceholders();
|
||||
void initStartingResources();
|
||||
void initDifficulty();
|
||||
void initHeroes();
|
||||
void placeHeroesInTowns();
|
||||
void initFogOfWar();
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "../../lib/gameState/CGameState.h"
|
||||
#include "../../lib/mapping/CMap.h"
|
||||
#include "../../lib/modding/IdentifierStorage.h"
|
||||
#include "../../lib/CPlayerState.h"
|
||||
|
||||
BattleProcessor::BattleProcessor(CGameHandler * gameHandler)
|
||||
: gameHandler(gameHandler)
|
||||
@ -114,6 +115,19 @@ void BattleProcessor::startBattlePrimary(const CArmedInstance *army1, const CArm
|
||||
const auto * battle = gameHandler->gameState()->getBattle(battleID);
|
||||
assert(battle);
|
||||
|
||||
//add battle bonuses based from player state only when attacks neutral creatures
|
||||
const auto * attackerInfo = gameHandler->getPlayerState(army1->getOwner(), false);
|
||||
if(attackerInfo && !army2->getOwner().isValidPlayer())
|
||||
{
|
||||
for(auto bonus : attackerInfo->battleBonuses)
|
||||
{
|
||||
GiveBonus giveBonus(GiveBonus::ETarget::HERO);
|
||||
giveBonus.id = hero1->id.getNum();
|
||||
giveBonus.bonus = bonus;
|
||||
gameHandler->sendAndApply(&giveBonus);
|
||||
}
|
||||
}
|
||||
|
||||
auto lastBattleQuery = std::dynamic_pointer_cast<CBattleQuery>(gameHandler->queries->topQuery(battle->sides[0].color));
|
||||
|
||||
if (lastBattleQuery)
|
||||
|
Loading…
Reference in New Issue
Block a user