mirror of
https://github.com/vcmi/vcmi.git
synced 2025-07-03 00:46:55 +02:00
@ -634,6 +634,8 @@ bool CVideoPlayer::playVideo(int x, int y, bool stopOnKey)
|
|||||||
pos.y = y;
|
pos.y = y;
|
||||||
frameTime = 0.0;
|
frameTime = 0.0;
|
||||||
|
|
||||||
|
auto lastTimePoint = boost::chrono::steady_clock::now();
|
||||||
|
|
||||||
while(nextFrame())
|
while(nextFrame())
|
||||||
{
|
{
|
||||||
if(stopOnKey)
|
if(stopOnKey)
|
||||||
@ -654,10 +656,17 @@ bool CVideoPlayer::playVideo(int x, int y, bool stopOnKey)
|
|||||||
#else
|
#else
|
||||||
auto packet_duration = frame->duration;
|
auto packet_duration = frame->duration;
|
||||||
#endif
|
#endif
|
||||||
double frameDurationSec = packet_duration * av_q2d(format->streams[stream]->time_base);
|
// Framerate delay
|
||||||
uint32_t timeToSleepMillisec = 1000 * (frameDurationSec);
|
double targetFrameTimeSeconds = packet_duration * av_q2d(format->streams[stream]->time_base);
|
||||||
|
auto targetFrameTime = boost::chrono::milliseconds(static_cast<int>(1000 * (targetFrameTimeSeconds)));
|
||||||
|
|
||||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(timeToSleepMillisec));
|
auto timePointAfterPresent = boost::chrono::steady_clock::now();
|
||||||
|
auto timeSpentBusy = boost::chrono::duration_cast<boost::chrono::milliseconds>(timePointAfterPresent - lastTimePoint);
|
||||||
|
|
||||||
|
if (targetFrameTime > timeSpentBusy)
|
||||||
|
boost::this_thread::sleep_for(targetFrameTime - timeSpentBusy);
|
||||||
|
|
||||||
|
lastTimePoint = boost::chrono::steady_clock::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -1730,7 +1730,7 @@
|
|||||||
"bonuses" : [
|
"bonuses" : [
|
||||||
{
|
{
|
||||||
"type" : "CREATURE_GROWTH",
|
"type" : "CREATURE_GROWTH",
|
||||||
"subtype" : "creatureLevel1",
|
"subtype" : "creatureLevel2",
|
||||||
"val" : 5,
|
"val" : 5,
|
||||||
"propagator": "VISITED_TOWN_AND_VISITOR"
|
"propagator": "VISITED_TOWN_AND_VISITOR"
|
||||||
}
|
}
|
||||||
@ -1743,7 +1743,7 @@
|
|||||||
"bonuses" : [
|
"bonuses" : [
|
||||||
{
|
{
|
||||||
"type" : "CREATURE_GROWTH",
|
"type" : "CREATURE_GROWTH",
|
||||||
"subtype" : "creatureLevel2",
|
"subtype" : "creatureLevel3",
|
||||||
"val" : 4,
|
"val" : 4,
|
||||||
"propagator": "VISITED_TOWN_AND_VISITOR"
|
"propagator": "VISITED_TOWN_AND_VISITOR"
|
||||||
}
|
}
|
||||||
@ -1756,7 +1756,7 @@
|
|||||||
"bonuses" : [
|
"bonuses" : [
|
||||||
{
|
{
|
||||||
"type" : "CREATURE_GROWTH",
|
"type" : "CREATURE_GROWTH",
|
||||||
"subtype" : "creatureLevel3",
|
"subtype" : "creatureLevel4",
|
||||||
"val" : 3,
|
"val" : 3,
|
||||||
"propagator": "VISITED_TOWN_AND_VISITOR"
|
"propagator": "VISITED_TOWN_AND_VISITOR"
|
||||||
}
|
}
|
||||||
@ -1769,7 +1769,7 @@
|
|||||||
"bonuses" : [
|
"bonuses" : [
|
||||||
{
|
{
|
||||||
"type" : "CREATURE_GROWTH",
|
"type" : "CREATURE_GROWTH",
|
||||||
"subtype" : "creatureLevel4",
|
"subtype" : "creatureLevel5",
|
||||||
"val" : 2,
|
"val" : 2,
|
||||||
"propagator": "VISITED_TOWN_AND_VISITOR"
|
"propagator": "VISITED_TOWN_AND_VISITOR"
|
||||||
}
|
}
|
||||||
@ -1782,7 +1782,7 @@
|
|||||||
"bonuses" : [
|
"bonuses" : [
|
||||||
{
|
{
|
||||||
"type" : "CREATURE_GROWTH",
|
"type" : "CREATURE_GROWTH",
|
||||||
"subtype" : "creatureLevel5",
|
"subtype" : "creatureLevel6",
|
||||||
"val" : 1,
|
"val" : 1,
|
||||||
"propagator": "VISITED_TOWN_AND_VISITOR"
|
"propagator": "VISITED_TOWN_AND_VISITOR"
|
||||||
}
|
}
|
||||||
|
@ -244,7 +244,7 @@ Increased effect of spell affecting creature, ie. Aenain makes Disrupting Ray de
|
|||||||
"subtype" : "spell.disruptingRay",
|
"subtype" : "spell.disruptingRay",
|
||||||
"type" : "SPECIAL_ADD_VALUE_ENCHANT"
|
"type" : "SPECIAL_ADD_VALUE_ENCHANT"
|
||||||
}
|
}
|
||||||
``````
|
```
|
||||||
|
|
||||||
- subtype: affected spell identifier
|
- subtype: affected spell identifier
|
||||||
- additionalInfo: value to add
|
- additionalInfo: value to add
|
||||||
|
@ -21,6 +21,7 @@ BattleFieldInfo * BattleFieldHandler::loadFromJson(const std::string & scope, co
|
|||||||
|
|
||||||
auto * info = new BattleFieldInfo(BattleField(index), identifier);
|
auto * info = new BattleFieldInfo(BattleField(index), identifier);
|
||||||
|
|
||||||
|
info->modScope = scope;
|
||||||
info->graphics = ImagePath::fromJson(json["graphics"]);
|
info->graphics = ImagePath::fromJson(json["graphics"]);
|
||||||
info->icon = json["icon"].String();
|
info->icon = json["icon"].String();
|
||||||
info->name = json["name"].String();
|
info->name = json["name"].String();
|
||||||
@ -66,7 +67,7 @@ int32_t BattleFieldInfo::getIconIndex() const
|
|||||||
|
|
||||||
std::string BattleFieldInfo::getJsonKey() const
|
std::string BattleFieldInfo::getJsonKey() const
|
||||||
{
|
{
|
||||||
return identifier;
|
return modScope + ':' + identifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string BattleFieldInfo::getNameTextID() const
|
std::string BattleFieldInfo::getNameTextID() const
|
||||||
|
@ -27,6 +27,7 @@ public:
|
|||||||
bool isSpecial;
|
bool isSpecial;
|
||||||
ImagePath graphics;
|
ImagePath graphics;
|
||||||
std::string name;
|
std::string name;
|
||||||
|
std::string modScope;
|
||||||
std::string identifier;
|
std::string identifier;
|
||||||
std::string icon;
|
std::string icon;
|
||||||
si32 iconIndex;
|
si32 iconIndex;
|
||||||
|
@ -612,10 +612,20 @@ CCreature * CCreatureHandler::loadFromJson(const std::string & scope, const Json
|
|||||||
cre->addBonus(node["attack"].Integer(), BonusType::PRIMARY_SKILL, BonusSubtypeID(PrimarySkill::ATTACK));
|
cre->addBonus(node["attack"].Integer(), BonusType::PRIMARY_SKILL, BonusSubtypeID(PrimarySkill::ATTACK));
|
||||||
cre->addBonus(node["defense"].Integer(), BonusType::PRIMARY_SKILL, BonusSubtypeID(PrimarySkill::DEFENSE));
|
cre->addBonus(node["defense"].Integer(), BonusType::PRIMARY_SKILL, BonusSubtypeID(PrimarySkill::DEFENSE));
|
||||||
|
|
||||||
cre->addBonus(node["damage"]["min"].Integer(), BonusType::CREATURE_DAMAGE, BonusCustomSubtype::creatureDamageMin);
|
int minDamage = node["damage"]["min"].Integer();
|
||||||
cre->addBonus(node["damage"]["max"].Integer(), BonusType::CREATURE_DAMAGE, BonusCustomSubtype::creatureDamageMax);
|
int maxDamage = node["damage"]["max"].Integer();
|
||||||
|
|
||||||
assert(node["damage"]["min"].Integer() <= node["damage"]["max"].Integer());
|
if (minDamage <= maxDamage)
|
||||||
|
{
|
||||||
|
cre->addBonus(minDamage, BonusType::CREATURE_DAMAGE, BonusCustomSubtype::creatureDamageMin);
|
||||||
|
cre->addBonus(maxDamage, BonusType::CREATURE_DAMAGE, BonusCustomSubtype::creatureDamageMax);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logGlobal->error("Mod %s: creature %s has minimal damage (%d) greater than maximal damage (%d)!", scope, identifier, minDamage, maxDamage);
|
||||||
|
cre->addBonus(maxDamage, BonusType::CREATURE_DAMAGE, BonusCustomSubtype::creatureDamageMin);
|
||||||
|
cre->addBonus(minDamage, BonusType::CREATURE_DAMAGE, BonusCustomSubtype::creatureDamageMax);
|
||||||
|
}
|
||||||
|
|
||||||
if(!node["shots"].isNull())
|
if(!node["shots"].isNull())
|
||||||
cre->addBonus(node["shots"].Integer(), BonusType::SHOTS);
|
cre->addBonus(node["shots"].Integer(), BonusType::SHOTS);
|
||||||
|
@ -320,7 +320,7 @@ void CGDwelling::newTurn(CRandomGenerator & rand) const
|
|||||||
creaturesAccumulate = VLC->settings()->getBoolean(EGameSettings::DWELLINGS_ACCUMULATE_WHEN_NEUTRAL);
|
creaturesAccumulate = VLC->settings()->getBoolean(EGameSettings::DWELLINGS_ACCUMULATE_WHEN_NEUTRAL);
|
||||||
|
|
||||||
const CCreature * cre =creatures[i].second[0].toCreature();
|
const CCreature * cre =creatures[i].second[0].toCreature();
|
||||||
TQuantity amount = cre->getGrowth() * (1 + cre->valOfBonuses(BonusType::CREATURE_GROWTH_PERCENT)/100) + cre->valOfBonuses(BonusType::CREATURE_GROWTH);
|
TQuantity amount = cre->getGrowth() * (1 + cre->valOfBonuses(BonusType::CREATURE_GROWTH_PERCENT)/100) + cre->valOfBonuses(BonusType::CREATURE_GROWTH, BonusCustomSubtype::creatureLevel(cre->getLevel()));
|
||||||
if (creaturesAccumulate && ID != Obj::REFUGEE_CAMP) //camp should not try to accumulate different kinds of creatures
|
if (creaturesAccumulate && ID != Obj::REFUGEE_CAMP) //camp should not try to accumulate different kinds of creatures
|
||||||
sac.creatures[i].first += amount;
|
sac.creatures[i].first += amount;
|
||||||
else
|
else
|
||||||
|
@ -164,7 +164,8 @@ GrowthInfo CGTownInstance::getGrowthInfo(int level) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
//other *-of-legion-like bonuses (%d to growth cumulative with grail)
|
//other *-of-legion-like bonuses (%d to growth cumulative with grail)
|
||||||
TConstBonusListPtr bonuses = getBonuses(Selector::typeSubtype(BonusType::CREATURE_GROWTH, BonusCustomSubtype::creatureLevel(level)));
|
// Note: bonus uses 1-based levels (Pikeman is level 1), town list uses 0-based (Pikeman in 0-th creatures entry)
|
||||||
|
TConstBonusListPtr bonuses = getBonuses(Selector::typeSubtype(BonusType::CREATURE_GROWTH, BonusCustomSubtype::creatureLevel(level+1)));
|
||||||
for(const auto & b : *bonuses)
|
for(const auto & b : *bonuses)
|
||||||
ret.entries.emplace_back(b->val, b->Description());
|
ret.entries.emplace_back(b->val, b->Description());
|
||||||
|
|
||||||
|
@ -2223,17 +2223,10 @@ CGObjectInstance * CMapLoaderH3M::readTown(const int3 & position, std::shared_pt
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::set<SpellID> spellsMask;
|
std::set<SpellID> spellsMask = VLC->spellh->getDefaultAllowed(); // by default - include spells from mods
|
||||||
|
|
||||||
reader->readBitmaskSpells(spellsMask, true);
|
reader->readBitmaskSpells(spellsMask, true);
|
||||||
std::copy(spellsMask.begin(), spellsMask.end(), std::back_inserter(object->possibleSpells));
|
std::copy(spellsMask.begin(), spellsMask.end(), std::back_inserter(object->possibleSpells));
|
||||||
|
|
||||||
auto defaultAllowed = VLC->spellh->getDefaultAllowed();
|
|
||||||
|
|
||||||
//add all spells from mods
|
|
||||||
for(int i = features.spellsCount; i < defaultAllowed.size(); ++i)
|
|
||||||
if(defaultAllowed.count(i))
|
|
||||||
object->possibleSpells.emplace_back(i);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(features.levelHOTA1)
|
if(features.levelHOTA1)
|
||||||
|
@ -71,7 +71,8 @@ SilentCaster::SilentCaster(PlayerColor owner_, const Caster * hero_):
|
|||||||
|
|
||||||
void SilentCaster::getCasterName(MetaString & text) const
|
void SilentCaster::getCasterName(MetaString & text) const
|
||||||
{
|
{
|
||||||
logGlobal->error("Unexpected call to SilentCaster::getCasterName");
|
// NOTE: can be triggered (for example) if creature steps into Tower mines/moat while hero has Recanter's Cloak
|
||||||
|
logGlobal->debug("Unexpected call to SilentCaster::getCasterName");
|
||||||
}
|
}
|
||||||
|
|
||||||
void SilentCaster::getCastDescription(const Spell * spell, const std::vector<const battle::Unit *> & attacked, MetaString & text) const
|
void SilentCaster::getCastDescription(const Spell * spell, const std::vector<const battle::Unit *> & attacked, MetaString & text) const
|
||||||
|
@ -3292,7 +3292,11 @@ void CGameHandler::handleTownEvents(CGTownInstance * town, NewTurn &n)
|
|||||||
|
|
||||||
for (auto & i : ev.buildings)
|
for (auto & i : ev.buildings)
|
||||||
{
|
{
|
||||||
if (!town->hasBuilt(i))
|
// Only perform action if:
|
||||||
|
// 1. Building exists in town (don't attempt to build Lvl 5 guild in Fortress
|
||||||
|
// 2. Building was not built yet
|
||||||
|
// othervice, silently ignore / skip it
|
||||||
|
if (town->town->buildings.count(i) && !town->hasBuilt(i))
|
||||||
{
|
{
|
||||||
buildStructure(town->id, i, true);
|
buildStructure(town->id, i, true);
|
||||||
iw.components.emplace_back(ComponentType::BUILDING, BuildingTypeUniqueID(town->getFaction(), i));
|
iw.components.emplace_back(ComponentType::BUILDING, BuildingTypeUniqueID(town->getFaction(), i));
|
||||||
|
@ -1301,6 +1301,7 @@ void BattleActionProcessor::handleAfterAttackCasting(const CBattleInfoCallback &
|
|||||||
// send empty event to client
|
// send empty event to client
|
||||||
// temporary(?) workaround to force animations to trigger
|
// temporary(?) workaround to force animations to trigger
|
||||||
StacksInjured fakeEvent;
|
StacksInjured fakeEvent;
|
||||||
|
fakeEvent.battleID = battle.getBattle()->getBattleID();
|
||||||
gameHandler->sendAndApply(&fakeEvent);
|
gameHandler->sendAndApply(&fakeEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,6 +181,7 @@ void BattleFlowProcessor::trySummonGuardians(const CBattleInfoCallback & battle,
|
|||||||
// send empty event to client
|
// send empty event to client
|
||||||
// temporary(?) workaround to force animations to trigger
|
// temporary(?) workaround to force animations to trigger
|
||||||
StacksInjured fakeEvent;
|
StacksInjured fakeEvent;
|
||||||
|
fakeEvent.battleID = battle.getBattle()->getBattleID();
|
||||||
gameHandler->sendAndApply(&fakeEvent);
|
gameHandler->sendAndApply(&fakeEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -676,8 +677,7 @@ void BattleFlowProcessor::stackTurnTrigger(const CBattleInfoCallback & battle, c
|
|||||||
}
|
}
|
||||||
if(st->hasBonusOfType(BonusType::MANA_DRAIN) && !st->drainedMana)
|
if(st->hasBonusOfType(BonusType::MANA_DRAIN) && !st->drainedMana)
|
||||||
{
|
{
|
||||||
const PlayerColor opponent = battle.otherPlayer(battle.battleGetOwner(st));
|
const CGHeroInstance * opponentHero = battle.battleGetFightingHero(battle.otherSide(st->unitSide()));
|
||||||
const CGHeroInstance * opponentHero = battle.battleGetFightingHero(opponent);
|
|
||||||
if(opponentHero)
|
if(opponentHero)
|
||||||
{
|
{
|
||||||
ui32 manaDrained = st->valOfBonuses(BonusType::MANA_DRAIN);
|
ui32 manaDrained = st->valOfBonuses(BonusType::MANA_DRAIN);
|
||||||
|
@ -498,10 +498,10 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle)
|
|||||||
|
|
||||||
BattleResultAccepted raccepted;
|
BattleResultAccepted raccepted;
|
||||||
raccepted.battleID = battle.getBattle()->getBattleID();
|
raccepted.battleID = battle.getBattle()->getBattleID();
|
||||||
raccepted.heroResult[0].army = const_cast<CArmedInstance*>(battle.battleGetArmyObject(0));
|
raccepted.heroResult[0].army = const_cast<CArmedInstance*>(battle.battleGetArmyObject(BattleSide::ATTACKER));
|
||||||
raccepted.heroResult[1].army = const_cast<CArmedInstance*>(battle.battleGetArmyObject(1));
|
raccepted.heroResult[1].army = const_cast<CArmedInstance*>(battle.battleGetArmyObject(BattleSide::DEFENDER));
|
||||||
raccepted.heroResult[0].hero = const_cast<CGHeroInstance*>(battle.battleGetFightingHero(0));
|
raccepted.heroResult[0].hero = const_cast<CGHeroInstance*>(battle.battleGetFightingHero(BattleSide::ATTACKER));
|
||||||
raccepted.heroResult[1].hero = const_cast<CGHeroInstance*>(battle.battleGetFightingHero(1));
|
raccepted.heroResult[1].hero = const_cast<CGHeroInstance*>(battle.battleGetFightingHero(BattleSide::DEFENDER));
|
||||||
raccepted.heroResult[0].exp = battleResult->exp[0];
|
raccepted.heroResult[0].exp = battleResult->exp[0];
|
||||||
raccepted.heroResult[1].exp = battleResult->exp[1];
|
raccepted.heroResult[1].exp = battleResult->exp[1];
|
||||||
raccepted.winnerSide = finishingBattle->winnerSide;
|
raccepted.winnerSide = finishingBattle->winnerSide;
|
||||||
|
Reference in New Issue
Block a user