1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +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:
Nordsoft91 2023-09-23 03:21:19 +02:00 committed by GitHub
commit a36450e2d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 195 additions and 58 deletions

70
config/difficulty.json Normal file
View 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": []
}
},
}

View File

@ -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 }
}
]
}

View 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.

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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)
@ -113,6 +114,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));