1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Merge branch 'develop' into creature-numeric-quantities

This commit is contained in:
Dydzio 2023-01-21 12:57:29 +01:00
commit 685d63603d
362 changed files with 10853 additions and 5367 deletions

View File

@ -49,7 +49,8 @@ int64_t AttackPossibility::calculateDamageReduce(
vstd::amin(damageDealt, defender->getAvailableHealth());
auto enemyDamageBeforeAttack = cb.battleEstimateDamage(BattleAttackInfo(defender, attacker, defender->canShoot()));
// FIXME: provide distance info for Jousting bonus
auto enemyDamageBeforeAttack = cb.battleEstimateDamage(defender, attacker, 0);
auto enemiesKilled = damageDealt / defender->MaxHealth() + (damageDealt % defender->MaxHealth() >= defender->getFirstHPleft() ? 1 : 0);
auto enemyDamage = averageDmg(enemyDamageBeforeAttack);
auto damagePerEnemy = enemyDamage / (double)defender->getCount();
@ -74,10 +75,11 @@ int64_t AttackPossibility::evaluateBlockedShootersDmg(const BattleAttackInfo & a
if(!state.battleCanShoot(st))
continue;
BattleAttackInfo rangeAttackInfo(st, attacker, true);
// FIXME: provide distance info for Jousting bonus
BattleAttackInfo rangeAttackInfo(st, attacker, 0, true);
rangeAttackInfo.defenderPos = hex;
BattleAttackInfo meleeAttackInfo(st, attacker, false);
BattleAttackInfo meleeAttackInfo(st, attacker, 0, false);
meleeAttackInfo.defenderPos = hex;
auto rangeDmg = state.battleEstimateDamage(rangeAttackInfo);
@ -211,8 +213,8 @@ AttackPossibility AttackPossibility::evaluate(const BattleAttackInfo & attackInf
bestAp.shootersBlockedDmg = evaluateBlockedShootersDmg(attackInfo, hex, state);
logAi->debug("BattleAI best AP: %s -> %s at %d from %d, affects %d units: d:%lld a:%lld c:%lld s:%lld",
attackInfo.attacker->unitType()->identifier,
attackInfo.defender->unitType()->identifier,
attackInfo.attacker->unitType()->getJsonKey(),
attackInfo.defender->unitType()->getJsonKey(),
(int)bestAp.dest, (int)bestAp.from, (int)bestAp.affectedUnits.size(),
bestAp.defenderDamageReduce, bestAp.attackerDamageReduce, bestAp.collateralDamageReduce, bestAp.shootersBlockedDmg);

View File

@ -46,14 +46,14 @@ std::vector<BattleHex> CBattleAI::getBrokenWallMoatHexes() const
{
std::vector<BattleHex> result;
for(int wallPart = EWallPart::BOTTOM_WALL; wallPart < EWallPart::UPPER_WALL; wallPart++)
for(EWallPart wallPart : { EWallPart::BOTTOM_WALL, EWallPart::BELOW_GATE, EWallPart::OVER_GATE, EWallPart::UPPER_WALL })
{
auto state = cb->battleGetWallState(wallPart);
if(state != EWallState::DESTROYED)
continue;
auto wallHex = cb->wallPartToBattleHex((EWallPart::EWallPart)wallPart);
auto wallHex = cb->wallPartToBattleHex((EWallPart)wallPart);
auto moatHex = wallHex.cloneInDirection(BattleHex::LEFT);
result.push_back(moatHex);
@ -207,10 +207,10 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
}
logAi->debug("BattleAI: %s -> %s x %d, %s, from %d curpos %d dist %d speed %d: +%lld -%lld = %lld",
bestAttack.attackerState->unitType()->identifier,
bestAttack.affectedUnits[0]->unitType()->identifier,
bestAttack.attackerState->unitType()->getJsonKey(),
bestAttack.affectedUnits[0]->unitType()->getJsonKey(),
(int)bestAttack.affectedUnits[0]->getCount(), action, (int)bestAttack.from, (int)bestAttack.attack.attacker->getPosition().hex,
bestAttack.attack.chargedFields, bestAttack.attack.attacker->Speed(0, true),
bestAttack.attack.chargeDistance, bestAttack.attack.attacker->Speed(0, true),
bestAttack.defenderDamageReduce, bestAttack.attackerDamageReduce, bestAttack.attackValue()
);
}
@ -364,7 +364,7 @@ BattleAction CBattleAI::useCatapult(const CStack * stack)
}
else
{
EWallPart::EWallPart wallParts[] = {
EWallPart wallParts[] = {
EWallPart::KEEP,
EWallPart::BOTTOM_TOWER,
EWallPart::UPPER_TOWER,
@ -378,10 +378,9 @@ BattleAction CBattleAI::useCatapult(const CStack * stack)
{
auto wallState = cb->battleGetWallState(wallPart);
if(wallState == EWallState::INTACT || wallState == EWallState::DAMAGED)
if(wallState == EWallState::REINFORCED || wallState == EWallState::INTACT || wallState == EWallState::DAMAGED)
{
targetHex = cb->wallPartToBattleHex(wallPart);
break;
}
}
@ -688,7 +687,7 @@ void CBattleAI::attemptCastingSpell()
if(castToPerform.value > 0)
{
LOGFL("Best spell is %s (value %d). Will cast.", castToPerform.spell->name % castToPerform.value);
LOGFL("Best spell is %s (value %d). Will cast.", castToPerform.spell->getNameTranslated() % castToPerform.value);
BattleAction spellcast;
spellcast.actionType = EActionType::HERO_SPELL;
spellcast.actionSubtype = castToPerform.spell->id;
@ -699,7 +698,7 @@ void CBattleAI::attemptCastingSpell()
}
else
{
LOGFL("Best spell is %s. But it is actually useless (value %d).", castToPerform.spell->name % castToPerform.value);
LOGFL("Best spell is %s. But it is actually useless (value %d).", castToPerform.spell->getNameTranslated() % castToPerform.value);
}
}

View File

@ -69,7 +69,8 @@ int64_t BattleExchangeVariant::trackAttack(
const bool counterAttacksBlocked = attacker->hasBonus(selectorBlocksRetaliation, cachingStringBlocksRetaliation);
TDmgRange retaliation;
BattleAttackInfo bai(attacker.get(), defender.get(), shooting);
// FIXME: provide distance info for Jousting bonus
BattleAttackInfo bai(attacker.get(), defender.get(), 0, shooting);
if(shooting)
{
@ -234,7 +235,8 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(const battle::Uni
for(auto hex : hexes)
{
auto bai = BattleAttackInfo(activeStack, closestStack, cb->battleCanShoot(activeStack));
// FIXME: provide distance info for Jousting bonus
auto bai = BattleAttackInfo(activeStack, closestStack, 0, cb->battleCanShoot(activeStack));
auto attack = AttackPossibility::evaluate(bai, hex, hb);
attack.shootersBlockedDmg = 0; // we do not want to count on it, it is not for sure

View File

@ -46,10 +46,8 @@ PotentialTargets::PotentialTargets(const battle::Unit * attacker, const Hypothet
auto GenerateAttackInfo = [&](bool shooting, BattleHex hex) -> AttackPossibility
{
auto bai = BattleAttackInfo(attackerInfo, defender, shooting);
if(hex.isValid() && !shooting)
bai.chargedFields = reachability.distances[hex];
int distance = hex.isValid() ? reachability.distances[hex] : 0;
auto bai = BattleAttackInfo(attackerInfo, defender, distance, shooting);
return AttackPossibility::evaluate(bai, hex, state);
};
@ -92,8 +90,8 @@ PotentialTargets::PotentialTargets(const battle::Unit * attacker, const Hypothet
auto & bestAp = possibleAttacks[0];
logGlobal->info("Battle AI best: %s -> %s at %d from %d, affects %d units: d:%lld a:%lld c:%lld s:%lld",
bestAp.attack.attacker->unitType()->identifier,
state.battleGetUnitByPos(bestAp.dest)->unitType()->identifier,
bestAp.attack.attacker->unitType()->getJsonKey(),
state.battleGetUnitByPos(bestAp.dest)->unitType()->getJsonKey(),
(int)bestAp.dest, (int)bestAp.from, (int)bestAp.affectedUnits.size(),
bestAp.defenderDamageReduce, bestAp.attackerDamageReduce, bestAp.collateralDamageReduce, bestAp.shootersBlockedDmg);
}

View File

@ -205,7 +205,7 @@ std::string StackWithBonuses::getDescription() const
oss << unitOwner().getStr();
oss << " battle stack [" << unitId() << "]: " << getCount() << " of ";
if(type)
oss << type->namePl;
oss << type->getJsonKey();
else
oss << "[UNDEFINED TYPE]";
@ -403,7 +403,7 @@ void HypotheticBattle::removeUnitBonus(uint32_t id, const std::vector<Bonus> & b
bonusTreeVersion++;
}
void HypotheticBattle::setWallState(int partOfWall, si8 state)
void HypotheticBattle::setWallState(EWallPart partOfWall, EWallState state)
{
//TODO:HypotheticBattle::setWallState
}

View File

@ -130,7 +130,7 @@ public:
void updateUnitBonus(uint32_t id, const std::vector<Bonus> & bonus) override;
void removeUnitBonus(uint32_t id, const std::vector<Bonus> & bonus) override;
void setWallState(int partOfWall, si8 state) override;
void setWallState(EWallPart partOfWall, EWallState state) override;
void addObstacle(const ObstacleChanges & changes) override;
void updateObstacle(const ObstacleChanges& changes) override;

View File

@ -283,7 +283,7 @@ void AIGateway::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID her
auto firstHero = cb->getHero(hero1);
auto secondHero = cb->getHero(hero2);
status.addQuery(query, boost::str(boost::format("Exchange between heroes %s (%d) and %s (%d)") % firstHero->name % firstHero->tempOwner % secondHero->name % secondHero->tempOwner));
status.addQuery(query, boost::str(boost::format("Exchange between heroes %s (%d) and %s (%d)") % firstHero->getNameTranslated() % firstHero->tempOwner % secondHero->getNameTranslated() % secondHero->tempOwner));
requestActionASAP([=]()
{
@ -544,7 +544,7 @@ void AIGateway::heroGotLevel(const CGHeroInstance * hero, PrimarySkill::PrimaryS
LOG_TRACE_PARAMS(logAi, "queryID '%i'", queryID);
NET_EVENT_HANDLER;
status.addQuery(queryID, boost::str(boost::format("Hero %s got level %d") % hero->name % hero->level));
status.addQuery(queryID, boost::str(boost::format("Hero %s got level %d") % hero->getNameTranslated() % hero->level));
HeroPtr hPtr = hero;
requestActionASAP([=]()
@ -737,7 +737,7 @@ bool AIGateway::makePossibleUpgrades(const CArmedInstance * obj)
{
myCb->upgradeCreature(obj, SlotID(i), ui.newID[0]);
upgraded = true;
logAi->debug("Upgraded %d %s to %s", s->count, ui.oldID.toCreature()->namePl, ui.newID[0].toCreature()->namePl);
logAi->debug("Upgraded %d %s to %s", s->count, ui.oldID.toCreature()->getNamePluralTranslated(), ui.newID[0].toCreature()->getNamePluralTranslated());
}
}
}
@ -787,7 +787,7 @@ void AIGateway::makeTurn()
for (auto h : cb->getHeroesInfo())
{
if (h->movement)
logAi->warn("Hero %s has %d MP left", h->name, h->movement);
logAi->warn("Hero %s has %d MP left", h->getNameTranslated(), h->movement);
}
#if NKAI_TRACE_LEVEL == 0
}
@ -808,7 +808,7 @@ void AIGateway::makeTurn()
void AIGateway::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
{
LOG_TRACE_PARAMS(logAi, "Hero %s and object %s at %s", h->name % obj->getObjectName() % obj->pos.toString());
LOG_TRACE_PARAMS(logAi, "Hero %s and object %s at %s", h->getNameTranslated() % obj->getObjectName() % obj->pos.toString());
switch(obj->ID)
{
case Obj::CREATURE_GENERATOR1:
@ -1070,7 +1070,7 @@ void AIGateway::battleStart(const CCreatureSet * army1, const CCreatureSet * arm
assert(playerID > PlayerColor::PLAYER_LIMIT || status.getBattle() == UPCOMING_BATTLE);
status.setBattle(ONGOING_BATTLE);
const CGObjectInstance * presumedEnemy = vstd::backOrNull(cb->getVisitableObjs(tile)); //may be nullptr in some very are cases -> eg. visited monolith and fighting with an enemy at the FoW covered exit
battlename = boost::str(boost::format("Starting battle of %s attacking %s at %s") % (hero1 ? hero1->name : "a army") % (presumedEnemy ? presumedEnemy->getObjectName() : "unknown enemy") % tile.toString());
battlename = boost::str(boost::format("Starting battle of %s attacking %s at %s") % (hero1 ? hero1->getNameTranslated() : "a army") % (presumedEnemy ? presumedEnemy->getObjectName() : "unknown enemy") % tile.toString());
CAdventureAI::battleStart(army1, army2, tile, hero1, hero2, side);
}
@ -1172,7 +1172,7 @@ bool AIGateway::moveHeroToTile(int3 dst, HeroPtr h)
}
};
logAi->debug("Moving hero %s to tile %s", h->name, dst.toString());
logAi->debug("Moving hero %s to tile %s", h->getNameTranslated(), dst.toString());
int3 startHpos = h->visitablePos();
bool ret = false;
if(startHpos == dst)
@ -1192,7 +1192,7 @@ bool AIGateway::moveHeroToTile(int3 dst, HeroPtr h)
cb->getPathsInfo(h.get())->getPath(path, dst);
if(path.nodes.empty())
{
logAi->error("Hero %s cannot reach %s.", h->name, dst.toString());
logAi->error("Hero %s cannot reach %s.", h->getNameTranslated(), dst.toString());
return true;
}
int i = (int)path.nodes.size() - 1;
@ -1344,15 +1344,15 @@ bool AIGateway::moveHeroToTile(int3 dst, HeroPtr h)
throw cannotFulfillGoalException("Invalid path found!");
}
logAi->debug("Hero %s moved from %s to %s. Returning %d.", h->name, startHpos.toString(), h->visitablePos().toString(), ret);
logAi->debug("Hero %s moved from %s to %s. Returning %d.", h->getNameTranslated(), startHpos.toString(), h->visitablePos().toString(), ret);
}
return ret;
}
void AIGateway::buildStructure(const CGTownInstance * t, BuildingID building)
{
auto name = t->town->buildings.at(building)->Name();
logAi->debug("Player %d will build %s in town of %s at %s", ai->playerID, name, t->name, t->pos.toString());
auto name = t->town->buildings.at(building)->getNameTranslated();
logAi->debug("Player %d will build %s in town of %s at %s", ai->playerID, name, t->getNameTranslated(), t->pos.toString());
cb->buildBuilding(t, building); //just do this;
}

View File

@ -69,7 +69,7 @@ HeroPtr::HeroPtr(const CGHeroInstance * H)
}
h = H;
name = h->name;
name = h->getNameTranslated();
hid = H->id;
// infosCount[ai->playerID][hid]++;
}

View File

@ -130,7 +130,7 @@ void BuildAnalyzer::update()
for(const CGTownInstance* town : towns)
{
logAi->trace("Checking town %s", town->name);
logAi->trace("Checking town %s", town->getNameTranslated());
developmentInfos.push_back(TownDevelopmentInfo(town));
TownDevelopmentInfo & developmentInfo = developmentInfos.back();
@ -351,7 +351,7 @@ BuildingInfo::BuildingInfo(
dailyIncome = building->produce;
exists = town->hasBuilt(id);
prerequisitesCount = 1;
name = building->Name();
name = building->getNameTranslated();
if(creature)
{

View File

@ -70,7 +70,7 @@ float HeroManager::evaluateSecSkill(SecondarySkill skill, const CGHeroInstance *
float HeroManager::evaluateSpeciality(const CGHeroInstance * hero) const
{
auto heroSpecial = Selector::source(Bonus::HERO_SPECIAL, hero->type->ID.getNum());
auto heroSpecial = Selector::source(Bonus::HERO_SPECIAL, hero->type->getIndex());
auto secondarySkillBonus = Selector::type()(Bonus::SECONDARY_SKILL_PREMY);
auto specialSecondarySkillBonuses = hero->getBonuses(heroSpecial.And(secondarySkillBonus));
float specialityScore = 0.0f;
@ -172,7 +172,7 @@ void HeroManager::update()
for(auto hero : myHeroes)
{
logAi->trace("Hero %s has role %s", hero->name, heroRoles[hero] == HeroRole::MAIN ? "main" : "scout");
logAi->trace("Hero %s has role %s", hero->getNameTranslated(), heroRoles[hero] == HeroRole::MAIN ? "main" : "scout");
}
}

View File

@ -202,7 +202,6 @@ void ObjectClusterizer::clusterize()
Obj::WHIRLPOOL,
Obj::BUOY,
Obj::SIGN,
Obj::SIGN,
Obj::GARRISON,
Obj::MONSTER,
Obj::GARRISON2,

View File

@ -50,14 +50,14 @@ Goals::TGoalVec DefenceBehavior::decompose() const
void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town) const
{
logAi->trace("Evaluating defence for %s", town->name);
logAi->trace("Evaluating defence for %s", town->getNameTranslated());
auto treatNode = ai->nullkiller->dangerHitMap->getObjectTreat(town);
auto treats = { treatNode.fastestDanger, treatNode.maximumDanger };
if(!treatNode.fastestDanger.hero)
{
logAi->trace("No treat found for town %s", town->name);
logAi->trace("No treat found for town %s", town->getNameTranslated());
return;
}
@ -78,8 +78,8 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
logAi->trace(
"Hero %s in garrison of town %s is suposed to defend the town",
town->garrisonHero->name,
town->name);
town->garrisonHero->getNameTranslated(),
town->getNameTranslated());
return;
}
@ -88,7 +88,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
if(reinforcement)
{
logAi->trace("Town %s can buy defence army %lld", town->name, reinforcement);
logAi->trace("Town %s can buy defence army %lld", town->getNameTranslated(), reinforcement);
tasks.push_back(Goals::sptr(Goals::BuyArmy(town, reinforcement).setpriority(0.5f)));
}
@ -98,10 +98,10 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
{
logAi->trace(
"Town %s has treat %lld in %s turns, hero: %s",
town->name,
town->getNameTranslated(),
treat.danger,
std::to_string(treat.turn),
treat.hero->name);
treat.hero->getNameTranslated());
bool treatIsUnderControl = false;
@ -187,7 +187,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
if(paths.empty())
{
logAi->trace("No ways to defend town %s", town->name);
logAi->trace("No ways to defend town %s", town->getNameTranslated());
continue;
}

View File

@ -191,7 +191,7 @@ int getDwellingArmyCost(const CGObjectInstance * target)
uint64_t evaluateArtifactArmyValue(CArtifactInstance * art)
{
if(art->artType->id == ArtifactID::SPELL_SCROLL)
if(art->artType->getId() == ArtifactID::SPELL_SCROLL)
return 1500;
auto statsValue =

View File

@ -60,7 +60,7 @@ std::string AbstractGoal::toString() const //TODO: virtualize
desc = "GATHER TROOPS";
break;
case GET_ART_TYPE:
desc = "GET ARTIFACT OF TYPE " + VLC->arth->objects[aid]->getName();
desc = "GET ARTIFACT OF TYPE " + VLC->arth->objects[aid]->getNameTranslated();
break;
case DIG_AT_TILE:
desc = "DIG AT TILE " + tile.toString();
@ -69,7 +69,7 @@ std::string AbstractGoal::toString() const //TODO: virtualize
return boost::lexical_cast<std::string>(goalType);
}
if(hero.get(true)) //FIXME: used to crash when we lost hero and failed goal
desc += " (" + hero->name + ")";
desc += " (" + hero->getNameTranslated() + ")";
return desc;
}

View File

@ -33,19 +33,19 @@ void AdventureSpellCast::accept(AIGateway * ai)
auto spell = getSpell();
logAi->trace("Decomposing adventure spell cast of %s for hero %s", spell->name, hero->name);
logAi->trace("Decomposing adventure spell cast of %s for hero %s", spell->getNameTranslated(), hero->getNameTranslated());
if(!spell->isAdventure())
throw cannotFulfillGoalException(spell->name + " is not an adventure spell.");
throw cannotFulfillGoalException(spell->getNameTranslated() + " is not an adventure spell.");
if(!hero->canCastThisSpell(spell))
throw cannotFulfillGoalException("Hero can not cast " + spell->name);
throw cannotFulfillGoalException("Hero can not cast " + spell->getNameTranslated());
if(hero->mana < hero->getSpellCost(spell))
throw cannotFulfillGoalException("Hero has not enough mana to cast " + spell->name);
throw cannotFulfillGoalException("Hero has not enough mana to cast " + spell->getNameTranslated());
if(spellID == SpellID::TOWN_PORTAL && town && town->visitingHero)
throw cannotFulfillGoalException("The town is already occupied by " + town->visitingHero->name);
throw cannotFulfillGoalException("The town is already occupied by " + town->visitingHero->getNameTranslated());
if(town && spellID == SpellID::TOWN_PORTAL)
{
@ -70,7 +70,7 @@ void AdventureSpellCast::accept(AIGateway * ai)
std::string AdventureSpellCast::toString() const
{
return "AdventureSpellCast " + spellID.toSpell()->name;
return "AdventureSpellCast " + spellID.toSpell()->getNameTranslated();
}
}

View File

@ -46,7 +46,7 @@ bool BuildThis::operator==(const BuildThis & other) const
std::string BuildThis::toString() const
{
return "Build " + buildingInfo.name + " in " + town->name;
return "Build " + buildingInfo.name + " in " + town->getNameTranslated();
}
void BuildThis::accept(AIGateway * ai)
@ -58,7 +58,7 @@ void BuildThis::accept(AIGateway * ai)
if(cb->canBuildStructure(town, b) == EBuildingState::ALLOWED)
{
logAi->debug("Player %d will build %s in town of %s at %s",
ai->playerID, town->town->buildings.at(b)->Name(), town->name, town->pos.toString());
ai->playerID, town->town->buildings.at(b)->getNameTranslated(), town->getNameTranslated(), town->pos.toString());
cb->buildBuilding(town, b);
return;

View File

@ -29,7 +29,7 @@ bool BuyArmy::operator==(const BuyArmy & other) const
std::string BuyArmy::toString() const
{
return "Buy army at " + town->name;
return "Buy army at " + town->getNameTranslated();
}
void BuyArmy::accept(AIGateway * ai)

View File

@ -33,7 +33,7 @@ ExchangeSwapTownHeroes::ExchangeSwapTownHeroes(
std::string ExchangeSwapTownHeroes::toString() const
{
return "Exchange and swap heroes of " + town->name;
return "Exchange and swap heroes of " + town->getNameTranslated();
}
bool ExchangeSwapTownHeroes::operator==(const ExchangeSwapTownHeroes & other) const
@ -54,13 +54,13 @@ void ExchangeSwapTownHeroes::accept(AIGateway * ai)
if(currentGarrisonHero.get() != town->visitingHero.get())
{
logAi->error("VisitingHero is empty, expected %s", currentGarrisonHero->name);
logAi->error("VisitingHero is empty, expected %s", currentGarrisonHero->getNameTranslated());
return;
}
ai->buildArmyIn(town);
ai->nullkiller->unlockHero(currentGarrisonHero.get());
logAi->debug("Extracted hero %s from garrison of %s", currentGarrisonHero->name, town->name);
logAi->debug("Extracted hero %s from garrison of %s", currentGarrisonHero->getNameTranslated(), town->getNameTranslated());
return;
}
@ -91,7 +91,7 @@ void ExchangeSwapTownHeroes::accept(AIGateway * ai)
ai->makePossibleUpgrades(town->visitingHero);
}
logAi->debug("Put hero %s to garrison of %s", garrisonHero->name, town->name);
logAi->debug("Put hero %s to garrison of %s", garrisonHero->getNameTranslated(), town->getNameTranslated());
}
}

View File

@ -75,7 +75,7 @@ void ExecuteHeroChain::accept(AIGateway * ai)
continue;
}
logAi->debug("Executing chain node %d. Moving hero %s to %s", i, hero->name, node.coord.toString());
logAi->debug("Executing chain node %d. Moving hero %s to %s", i, hero->getNameTranslated(), node.coord.toString());
try
{
@ -111,7 +111,7 @@ void ExecuteHeroChain::accept(AIGateway * ai)
{
logAi->error(
"Unable to complete chain. Expected hero %s to arrive to %s in 0 turns but he cannot do this",
hero->name,
hero->getNameTranslated(),
node.coord.toString());
return;
@ -143,7 +143,7 @@ void ExecuteHeroChain::accept(AIGateway * ai)
if(isOk && path.nodes.back().turns > 0)
{
logAi->warn("Hero %s has %d mp which is not enough to continue his way towards %s.", hero->name, hero->movement, node.coord.toString());
logAi->warn("Hero %s has %d mp which is not enough to continue his way towards %s.", hero->getNameTranslated(), hero->movement, node.coord.toString());
ai->nullkiller->lockHero(hero, HeroLockedReason::HERO_CHAIN);
return;
@ -162,7 +162,7 @@ void ExecuteHeroChain::accept(AIGateway * ai)
{
logAi->error(
"Enable to complete chain. Expected hero %s to arive to %s but he is at %s",
hero->name,
hero->getNameTranslated(),
node.coord.toString(),
hero->visitablePos().toString());
@ -187,14 +187,14 @@ void ExecuteHeroChain::accept(AIGateway * ai)
std::string ExecuteHeroChain::toString() const
{
return "ExecuteHeroChain " + targetName + " by " + chainPath.targetHero->name;
return "ExecuteHeroChain " + targetName + " by " + chainPath.targetHero->getNameTranslated();
}
bool ExecuteHeroChain::moveHeroToTile(const CGHeroInstance * hero, const int3 & tile)
{
if(tile == hero->visitablePos() && cb->getVisitableObjs(hero->visitablePos()).size() < 2)
{
logAi->warn("Why do I want to move hero %s to tile %s? Already standing on that tile! ", hero->name, tile.toString());
logAi->warn("Why do I want to move hero %s to tile %s? Already standing on that tile! ", hero->getNameTranslated(), tile.toString());
return true;
}

View File

@ -26,7 +26,7 @@ using namespace Goals;
std::string RecruitHero::toString() const
{
return "Recruit hero at " + town->name;
return "Recruit hero at " + town->getNameTranslated();
}
void RecruitHero::accept(AIGateway * ai)
@ -40,7 +40,7 @@ void RecruitHero::accept(AIGateway * ai)
throw cannotFulfillGoalException("No town to recruit hero!");
}
logAi->debug("Trying to recruit a hero in %s at %s", t->name, t->visitablePos().toString());
logAi->debug("Trying to recruit a hero in %s at %s", t->getNameTranslated(), t->visitablePos().toString());
auto heroes = cb->getAvailableHeroes(t);
@ -78,4 +78,4 @@ void RecruitHero::accept(AIGateway * ai)
ai->moveHeroToTile(t->visitablePos(), t->visitingHero.get());
}
}
}

View File

@ -1437,10 +1437,10 @@ std::string AIPath::toString() const
{
std::stringstream str;
str << targetHero->name << "[" << std::hex << chainMask << std::dec << "]" << ", turn " << (int)(turn()) << ": ";
str << targetHero->getNameTranslated() << "[" << std::hex << chainMask << std::dec << "]" << ", turn " << (int)(turn()) << ": ";
for(auto node : nodes)
str << node.targetHero->name << "[" << std::hex << node.chainMask << std::dec << "]" << "->" << node.coord.toString() << "; ";
str << node.targetHero->getNameTranslated() << "[" << std::hex << node.chainMask << std::dec << "]" << "->" << node.coord.toString() << "; ";
return str.str();
}

View File

@ -27,7 +27,7 @@ namespace AIPathfinding
if(!hero->visitedTown)
{
throw cannotFulfillGoalException(
hero->name + " being at " + hero->visitablePos().toString() + " has no town to recruit creatures.");
hero->getNameTranslated() + " being at " + hero->visitablePos().toString() + " has no town to recruit creatures.");
}
ai->recruitCreatures(hero->visitedTown, hero);

View File

@ -34,7 +34,7 @@ void TownPortalAction::execute(const CGHeroInstance * hero) const
std::string TownPortalAction::toString() const
{
return "Town Portal to " + target->name;
return "Town Portal to " + target->getNameTranslated();
}
/*
bool TownPortalAction::canAct(const CGHeroInstance * hero, const AIPathNode * source) const

View File

@ -80,7 +80,7 @@ int ChainActor::maxMovePoints(CGPathNode::ELayer layer)
std::string ChainActor::toString() const
{
return hero->name;
return hero->getNameTranslated();
}
ObjectActor::ObjectActor(const CGObjectInstance * obj, const CCreatureSet * army, uint64_t chainMask, int initialTurn)
@ -463,5 +463,5 @@ TownGarrisonActor::TownGarrisonActor(const CGTownInstance * town, uint64_t chain
std::string TownGarrisonActor::toString() const
{
return town->name;
return town->getNameTranslated();
}

View File

@ -55,7 +55,8 @@ public:
{}
void calcDmg(const CStack * ourStack)
{
TDmgRange retal, dmg = cbc->battleEstimateDamage(ourStack, s, &retal);
// FIXME: provide distance info for Jousting bonus
TDmgRange retal, dmg = cbc->battleEstimateDamage(ourStack, s, 0, &retal);
adi = static_cast<int>((dmg.first + dmg.second) / 2);
adr = static_cast<int>((retal.first + retal.second) / 2);
}

View File

@ -69,7 +69,7 @@ HeroPtr::HeroPtr(const CGHeroInstance * H)
}
h = H;
name = h->name;
name = h->getNameTranslated();
hid = H->id;
// infosCount[ai->playerID][hid]++;
}

View File

@ -91,7 +91,7 @@ std::string AbstractGoal::name() const //TODO: virtualize
}
break;
case GET_ART_TYPE:
desc = "GET ARTIFACT OF TYPE " + VLC->artifacts()->getByIndex(aid)->getName();
desc = "GET ARTIFACT OF TYPE " + VLC->artifacts()->getByIndex(aid)->getNameTranslated();
break;
case VISIT_TILE:
desc = "VISIT TILE " + tile.toString();
@ -106,7 +106,7 @@ std::string AbstractGoal::name() const //TODO: virtualize
return boost::lexical_cast<std::string>(goalType);
}
if(hero.get(true)) //FIXME: used to crash when we lost hero and failed goal
desc += " (" + hero->name + ")";
desc += " (" + hero->getNameTranslated() + ")";
return desc;
}

View File

@ -33,19 +33,19 @@ TSubgoal AdventureSpellCast::whatToDoToAchieve()
auto spell = getSpell();
logAi->trace("Decomposing adventure spell cast of %s for hero %s", spell->getName(), hero->name);
logAi->trace("Decomposing adventure spell cast of %s for hero %s", spell->getNameTranslated(), hero->getNameTranslated());
if(!spell->isAdventure())
throw cannotFulfillGoalException(spell->getName() + " is not an adventure spell.");
throw cannotFulfillGoalException(spell->getNameTranslated() + " is not an adventure spell.");
if(!hero->canCastThisSpell(spell))
throw cannotFulfillGoalException("Hero can not cast " + spell->getName());
throw cannotFulfillGoalException("Hero can not cast " + spell->getNameTranslated());
if(hero->mana < hero->getSpellCost(spell))
throw cannotFulfillGoalException("Hero has not enough mana to cast " + spell->getName());
throw cannotFulfillGoalException("Hero has not enough mana to cast " + spell->getNameTranslated());
if(spellID == SpellID::TOWN_PORTAL && town && town->visitingHero)
throw cannotFulfillGoalException("The town is already occupied by " + town->visitingHero->name);
throw cannotFulfillGoalException("The town is already occupied by " + town->visitingHero->getNameTranslated());
return iAmElementar();
}
@ -75,10 +75,10 @@ void AdventureSpellCast::accept(VCAI * ai)
std::string AdventureSpellCast::name() const
{
return "AdventureSpellCast " + getSpell()->getName();
return "AdventureSpellCast " + getSpell()->getNameTranslated();
}
std::string AdventureSpellCast::completeMessage() const
{
return "Spell cast successfully " + getSpell()->getName();
return "Spell cast successfully " + getSpell()->getNameTranslated();
}

View File

@ -41,5 +41,5 @@ TSubgoal BuyArmy::whatToDoToAchieve()
std::string BuyArmy::completeMessage() const
{
return boost::format("Bought army of value %d in town of %s") % value, town->name;
return boost::format("Bought army of value %d in town of %s") % value, town->getNameTranslated();
}

View File

@ -92,7 +92,7 @@ TSubgoal CompleteQuest::whatToDoToAchieve()
result->name(),
result->tile.toString(),
result->objid,
result->hero.validAndSet() ? result->hero->name : "not specified");
result->hero.validAndSet() ? result->hero->getNameTranslated() : "not specified");
return result;
}
@ -273,4 +273,4 @@ TGoalVec CompleteQuest::missionDestroyObj() const
}
return solutions;
}
}

View File

@ -240,7 +240,7 @@ bool Explore::operator==(const Explore & other) const
std::string Explore::completeMessage() const
{
return "Hero " + hero.get()->name + " completed exploration";
return "Hero " + hero.get()->getNameTranslated() + " completed exploration";
}
TSubgoal Explore::whatToDoToAchieve()
@ -339,7 +339,7 @@ TGoalVec Explore::getAllPossibleSubgoals()
{
for(auto h : heroes)
{
logAi->trace("Exploration searching for a new point for hero %s", h->name);
logAi->trace("Exploration searching for a new point for hero %s", h->getNameTranslated());
TSubgoal goal = explorationNewPoint(h);

View File

@ -33,7 +33,7 @@ bool GatherArmy::operator==(const GatherArmy & other) const
std::string GatherArmy::completeMessage() const
{
return "Hero " + hero.get()->name + " gathered army of value " + boost::lexical_cast<std::string>(value);
return "Hero " + hero.get()->getNameTranslated() + " gathered army of value " + boost::lexical_cast<std::string>(value);
}
TSubgoal GatherArmy::whatToDoToAchieve()

View File

@ -56,7 +56,7 @@ TSubgoal GatherTroops::whatToDoToAchieve()
{
if(getCreaturesCount(hero) >= this->value)
{
logAi->trace("Completing GATHER_TROOPS by hero %s", hero->name);
logAi->trace("Completing GATHER_TROOPS by hero %s", hero->getNameTranslated());
throw goalFulfilledException(sptr(*this));
}

View File

@ -33,7 +33,7 @@ bool VisitHero::operator==(const VisitHero & other) const
std::string VisitHero::completeMessage() const
{
return "hero " + hero.get()->name + " visited hero " + boost::lexical_cast<std::string>(objid);
return "hero " + hero.get()->getNameTranslated() + " visited hero " + boost::lexical_cast<std::string>(objid);
}
TSubgoal VisitHero::whatToDoToAchieve()

View File

@ -33,7 +33,7 @@ bool VisitObj::operator==(const VisitObj & other) const
std::string VisitObj::completeMessage() const
{
return "hero " + hero.get()->name + " captured Object ID = " + boost::lexical_cast<std::string>(objid);
return "hero " + hero.get()->getNameTranslated() + " captured Object ID = " + boost::lexical_cast<std::string>(objid);
}
TGoalVec VisitObj::getAllPossibleSubgoals()

View File

@ -33,7 +33,7 @@ bool VisitTile::operator==(const VisitTile & other) const
std::string VisitTile::completeMessage() const
{
return "Hero " + hero.get()->name + " visited tile " + tile.toString();
return "Hero " + hero.get()->getNameTranslated() + " visited tile " + tile.toString();
}
TSubgoal VisitTile::whatToDoToAchieve()

View File

@ -55,7 +55,7 @@ void AIPathfinder::updatePaths(std::vector<HeroPtr> heroes)
auto calculatePaths = [&](const CGHeroInstance * hero, std::shared_ptr<AIPathfinding::AIPathfinderConfig> config)
{
logAi->debug("Recalculate paths for %s", hero->name);
logAi->debug("Recalculate paths for %s", hero->getNameTranslated());
cb->calculatePaths(config);
};

View File

@ -303,7 +303,7 @@ void VCAI::heroExchangeStarted(ObjectInstanceID hero1, ObjectInstanceID hero2, Q
auto firstHero = cb->getHero(hero1);
auto secondHero = cb->getHero(hero2);
status.addQuery(query, boost::str(boost::format("Exchange between heroes %s (%d) and %s (%d)") % firstHero->name % firstHero->tempOwner % secondHero->name % secondHero->tempOwner));
status.addQuery(query, boost::str(boost::format("Exchange between heroes %s (%d) and %s (%d)") % firstHero->getNameTranslated() % firstHero->tempOwner % secondHero->getNameTranslated() % secondHero->tempOwner));
requestActionASAP([=]()
{
@ -617,7 +617,7 @@ void VCAI::heroGotLevel(const CGHeroInstance * hero, PrimarySkill::PrimarySkill
{
LOG_TRACE_PARAMS(logAi, "queryID '%i'", queryID);
NET_EVENT_HANDLER;
status.addQuery(queryID, boost::str(boost::format("Hero %s got level %d") % hero->name % hero->level));
status.addQuery(queryID, boost::str(boost::format("Hero %s got level %d") % hero->getNameTranslated() % hero->level));
requestActionASAP([=](){ answerQuery(queryID, 0); });
}
@ -814,7 +814,7 @@ void VCAI::makeTurn()
for (auto h : cb->getHeroesInfo())
{
if (h->movement)
logAi->warn("Hero %s has %d MP left", h->name, h->movement);
logAi->warn("Hero %s has %d MP left", h->getNameTranslated(), h->movement);
}
}
catch (boost::thread_interrupted & e)
@ -1034,7 +1034,7 @@ void VCAI::mainLoop()
void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
{
LOG_TRACE_PARAMS(logAi, "Hero %s and object %s at %s", h->name % obj->getObjectName() % obj->pos.toString());
LOG_TRACE_PARAMS(logAi, "Hero %s and object %s at %s", h->getNameTranslated() % obj->getObjectName() % obj->pos.toString());
switch(obj->ID)
{
case Obj::CREATURE_GENERATOR1:
@ -1427,7 +1427,7 @@ void VCAI::wander(HeroPtr h)
{
//TODO pick the truly best
const CGTownInstance * t = *boost::max_element(townsNotReachable, compareReinforcements);
logAi->debug("%s can't reach any town, we'll try to make our way to %s at %s", h->name, t->name, t->visitablePos().toString());
logAi->debug("%s can't reach any town, we'll try to make our way to %s at %s", h->getNameTranslated(), t->getNameTranslated(), t->visitablePos().toString());
int3 pos1 = h->pos;
striveToGoal(sptr(Goals::ClearWayTo(t->visitablePos()).sethero(h))); //TODO: drop "strive", add to mainLoop
//if out hero is stuck, we may need to request another hero to clear the way we see
@ -1581,7 +1581,7 @@ void VCAI::battleStart(const CCreatureSet * army1, const CCreatureSet * army2, i
assert(playerID > PlayerColor::PLAYER_LIMIT || status.getBattle() == UPCOMING_BATTLE);
status.setBattle(ONGOING_BATTLE);
const CGObjectInstance * presumedEnemy = vstd::backOrNull(cb->getVisitableObjs(tile)); //may be nullptr in some very are cases -> eg. visited monolith and fighting with an enemy at the FoW covered exit
battlename = boost::str(boost::format("Starting battle of %s attacking %s at %s") % (hero1 ? hero1->name : "a army") % (presumedEnemy ? presumedEnemy->getObjectName() : "unknown enemy") % tile.toString());
battlename = boost::str(boost::format("Starting battle of %s attacking %s at %s") % (hero1 ? hero1->getNameTranslated() : "a army") % (presumedEnemy ? presumedEnemy->getObjectName() : "unknown enemy") % tile.toString());
CAdventureAI::battleStart(army1, army2, tile, hero1, hero2, side);
}
@ -1667,7 +1667,7 @@ void VCAI::validateVisitableObjs()
});
for(auto & p : reservedHeroesMap)
{
errorMsg = " shouldn't be on list for hero " + p.first->name + "!";
errorMsg = " shouldn't be on list for hero " + p.first->getNameTranslated() + "!";
vstd::erase_if(p.second, shouldBeErased);
}
@ -1808,7 +1808,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
}
};
logAi->debug("Moving hero %s to tile %s", h->name, dst.toString());
logAi->debug("Moving hero %s to tile %s", h->getNameTranslated(), dst.toString());
int3 startHpos = h->visitablePos();
bool ret = false;
if(startHpos == dst)
@ -1828,7 +1828,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
cb->getPathsInfo(h.get())->getPath(path, dst);
if(path.nodes.empty())
{
logAi->error("Hero %s cannot reach %s.", h->name, dst.toString());
logAi->error("Hero %s cannot reach %s.", h->getNameTranslated(), dst.toString());
throw goalFulfilledException(sptr(Goals::VisitTile(dst).sethero(h)));
}
int i = (int)path.nodes.size() - 1;
@ -1990,15 +1990,15 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
throw cannotFulfillGoalException("Invalid path found!");
}
evaluateGoal(h); //new hero position means new game situation
logAi->debug("Hero %s moved from %s to %s. Returning %d.", h->name, startHpos.toString(), h->visitablePos().toString(), ret);
logAi->debug("Hero %s moved from %s to %s. Returning %d.", h->getNameTranslated(), startHpos.toString(), h->visitablePos().toString(), ret);
}
return ret;
}
void VCAI::buildStructure(const CGTownInstance * t, BuildingID building)
{
auto name = t->town->buildings.at(building)->Name();
logAi->debug("Player %d will build %s in town of %s at %s", ai->playerID, name, t->name, t->pos.toString());
auto name = t->town->buildings.at(building)->getNameTranslated();
logAi->debug("Player %d will build %s in town of %s at %s", ai->playerID, name, t->getNameTranslated(), t->pos.toString());
cb->buildBuilding(t, building); //just do this;
}
@ -2027,7 +2027,7 @@ void VCAI::tryRealize(Goals::VisitTile & g)
throw cannotFulfillGoalException("Cannot visit tile: hero is out of MPs!");
if(g.tile == g.hero->visitablePos() && cb->getVisitableObjs(g.hero->visitablePos()).size() < 2)
{
logAi->warn("Why do I want to move hero %s to tile %s? Already standing on that tile! ", g.hero->name, g.tile.toString());
logAi->warn("Why do I want to move hero %s to tile %s? Already standing on that tile! ", g.hero->getNameTranslated(), g.tile.toString());
throw goalFulfilledException(sptr(g));
}
if(ai->moveHeroToTile(g.tile, g.hero.get()))
@ -2043,7 +2043,7 @@ void VCAI::tryRealize(Goals::VisitObj & g)
throw cannotFulfillGoalException("Cannot visit object: hero is out of MPs!");
if(position == g.hero->visitablePos() && cb->getVisitableObjs(g.hero->visitablePos()).size() < 2)
{
logAi->warn("Why do I want to move hero %s to tile %s? Already standing on that tile! ", g.hero->name, g.tile.toString());
logAi->warn("Why do I want to move hero %s to tile %s? Already standing on that tile! ", g.hero->getNameTranslated(), g.tile.toString());
throw goalFulfilledException(sptr(g));
}
if(ai->moveHeroToTile(position, g.hero.get()))
@ -2081,7 +2081,7 @@ void VCAI::tryRealize(Goals::BuildThis & g)
if (cb->canBuildStructure(t, b) == EBuildingState::ALLOWED)
{
logAi->debug("Player %d will build %s in town of %s at %s",
playerID, t->town->buildings.at(b)->Name(), t->name, t->pos.toString());
playerID, t->town->buildings.at(b)->getNameTranslated(), t->getNameTranslated(), t->pos.toString());
cb->buildBuilding(t, b);
throw goalFulfilledException(sptr(g));
}
@ -2404,7 +2404,7 @@ void VCAI::performTypicalActions()
if(!h) //hero might be lost. getUnblockedHeroes() called once on start of turn
continue;
logAi->debug("Hero %s started wandering, MP=%d", h->name.c_str(), h->movement);
logAi->debug("Hero %s started wandering, MP=%d", h->getNameTranslated(), h->movement);
makePossibleUpgrades(*h);
pickBestArtifacts(*h);
try
@ -2439,7 +2439,7 @@ void VCAI::checkHeroArmy(HeroPtr h)
void VCAI::recruitHero(const CGTownInstance * t, bool throwing)
{
logAi->debug("Trying to recruit a hero in %s at %s", t->name, t->visitablePos().toString());
logAi->debug("Trying to recruit a hero in %s at %s", t->getNameTranslated(), t->visitablePos().toString());
auto heroes = cb->getAvailableHeroes(t);
if(heroes.size())

12
CI/conan/base/apple Normal file
View File

@ -0,0 +1,12 @@
[settings]
compiler=apple-clang
compiler.version=14
compiler.libcxx=libc++
build_type=Release
# required for Boost.Locale in versions >= 1.81
compiler.cppstd=11
[conf]
tools.apple:enable_bitcode = False
tools.cmake.cmaketoolchain:generator = Ninja

5
CI/conan/base/ios Normal file
View File

@ -0,0 +1,5 @@
include(apple)
[settings]
os=iOS
os.sdk=iphoneos

4
CI/conan/base/macos Normal file
View File

@ -0,0 +1,4 @@
include(apple)
[settings]
os=Macos

View File

@ -1,14 +1,5 @@
include(base/ios)
[settings]
os=iOS
os.version=12.0
os.sdk=iphoneos
arch=armv8
compiler=apple-clang
compiler.version=13
compiler.libcxx=libc++
build_type=Release
[options]
[build_requires]
[env]
[conf]
tools.cmake.cmaketoolchain:generator = Ninja

View File

@ -1,14 +1,8 @@
include(base/ios)
[settings]
os=iOS
os.version=10.0
os.sdk=iphoneos
arch=armv7
compiler=apple-clang
# Xcode 13.x is the last version that can build for armv7
compiler.version=13
compiler.libcxx=libc++
build_type=Release
[options]
[build_requires]
[env]
[conf]
tools.cmake.cmaketoolchain:generator = Ninja

View File

@ -1,13 +1,5 @@
include(base/macos)
[settings]
os=Macos
os.version=11.0
arch=armv8
compiler=apple-clang
compiler.version=13
compiler.libcxx=libc++
build_type=Release
[options]
[build_requires]
[env]
[conf]
tools.cmake.cmaketoolchain:generator = Ninja

View File

@ -1,13 +1,5 @@
include(base/macos)
[settings]
os=Macos
os.version=10.13
arch=x86_64
compiler=apple-clang
compiler.version=13
compiler.libcxx=libc++
build_type=Release
[options]
[build_requires]
[env]
[conf]
tools.cmake.cmaketoolchain:generator = Ninja

View File

@ -1,7 +1,7 @@
#!/usr/bin/env bash
echo DEVELOPER_DIR=/Applications/Xcode_13.4.1.app >> $GITHUB_ENV
echo DEVELOPER_DIR=/Applications/Xcode_14.2.app >> $GITHUB_ENV
mkdir ~/.conan ; cd ~/.conan
curl -L 'https://github.com/vcmi/vcmi-ios-deps/releases/download/1.1/ios-arm64.xz' \
curl -L 'https://github.com/vcmi/vcmi-ios-deps/releases/download/1.2/ios-arm64.txz' \
| tar -xf -

View File

@ -1,9 +1,9 @@
#!/usr/bin/env bash
echo DEVELOPER_DIR=/Applications/Xcode_13.4.1.app >> $GITHUB_ENV
echo DEVELOPER_DIR=/Applications/Xcode_14.2.app >> $GITHUB_ENV
brew install ninja
mkdir ~/.conan ; cd ~/.conan
curl -L "https://github.com/vcmi/vcmi-deps-macos/releases/download/1.1/$DEPS_FILENAME.txz" \
curl -L "https://github.com/vcmi/vcmi-deps-macos/releases/download/1.2/$DEPS_FILENAME.txz" \
| tar -xf -

View File

@ -1,10 +1,10 @@
curl -LfsS -o "vcpkg-export-${VCMI_BUILD_PLATFORM}-windows-v140.7z" \
"https://github.com/vcmi/vcmi-deps-windows/releases/download/v1.5/vcpkg-export-${VCMI_BUILD_PLATFORM}-windows-v140.7z"
7z x "vcpkg-export-${VCMI_BUILD_PLATFORM}-windows-v140.7z"
curl -LfsS -o "vcpkg-export-${VCMI_BUILD_PLATFORM}-windows-v143.7z" \
"https://github.com/vcmi/vcmi-deps-windows/releases/download/v1.6/vcpkg-export-${VCMI_BUILD_PLATFORM}-windows-v143.7z"
7z x "vcpkg-export-${VCMI_BUILD_PLATFORM}-windows-v143.7z"
rm -r -f vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/debug
mkdir -p vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/debug/bin
cp vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/bin/* vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/debug/bin
#rm -r -f vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/debug
#mkdir -p vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/debug/bin
#cp vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/bin/* vcpkg/installed/${VCMI_BUILD_PLATFORM}-windows/debug/bin
DUMPBIN_DIR=$(vswhere -latest -find **/dumpbin.exe | head -n 1)
dirname "$DUMPBIN_DIR" > $GITHUB_PATH
dirname "$DUMPBIN_DIR" > $GITHUB_PATH

View File

@ -90,6 +90,11 @@ if(APPLE_IOS AND COPY_CONFIG_ON_BUILD)
set(COPY_CONFIG_ON_BUILD OFF)
endif()
# No QT Linguist on MXE
if((MINGW) AND (${CMAKE_CROSSCOMPILING}))
set(ENABLE_TRANSLATIONS OFF)
endif()
############################################
# Miscellaneous options #
############################################
@ -181,6 +186,14 @@ set(CMAKE_XCODE_ATTRIBUTE_MARKETING_VERSION ${APP_SHORT_VERSION})
set(CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH NO)
set(CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH[variant=Debug] YES)
if(ENABLE_LAUNCHER)
add_definitions(-DENABLE_LAUNCHER)
endif()
if(ENABLE_EDITOR)
add_definitions(-DENABLE_EDITOR)
endif()
if(ENABLE_SINGLE_APP_BUILD)
add_definitions(-DSINGLE_PROCESS_APP=1)
endif()
@ -232,7 +245,7 @@ if(MINGW OR MSVC)
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4800") # 4800: implicit conversion from 'xxx' to bool. Possible information loss
if(ENABLE_STRICT_COMPILATION)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wx") # Treats all compiler warnings as errors
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /WX") # Treats all compiler warnings as errors
endif()
if(ENABLE_MULTI_PROCESS_BUILDS)
@ -360,12 +373,8 @@ if(ENABLE_LAUNCHER OR ENABLE_EDITOR)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Network)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Network)
find_package(QT NAMES Qt6 Qt5 COMPONENTS LinguistTools)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS LinguistTools)
if(NOT Qt${QT_VERSION_MAJOR}LinguistTools_DIR)
set(ENABLE_TRANSLATIONS OFF)
endif()
if(ENABLE_TRANSLATIONS)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS LinguistTools)
add_definitions(-DENABLE_QT_TRANSLATIONS)
endif()
endif()
@ -563,6 +572,9 @@ if(WIN32)
FILES ${integration_loc}
DESTINATION ${BIN_DIR}/platforms
)
install(
FILES "$<TARGET_FILE:Qt${QT_VERSION_MAJOR}::QWindowsVistaStylePlugin>"
DESTINATION ${BIN_DIR}/styles)
endif()
endif()

View File

@ -1,6 +1,20 @@
{
"name" : "VCMI essential files",
"description" : "Essential files required for VCMI to run correctly",
"german" : {
"name" : "VCMI - grundlegende Dateien",
"description" : "Grundlegende Dateien, die für die korrekte Ausführung von VCMI erforderlich sind",
"author" : "VCMI-Team",
"modType" : "Grafik",
},
"ukrainian" : {
"name" : "VCMI - ключові файли",
"description" : "Ключові файли необхідні для повноцінної роботи VCMI",
"author" : "Команда VCMI",
"modType" : "Графіка",
},
"version" : "1.1",
"author" : "VCMI Team",

View File

@ -10,8 +10,7 @@
#include "StdInc.h"
#include "../lib/filesystem/Filesystem.h"
#include "SDL.h"
#include "SDL_image.h"
#include <SDL_image.h>
#include "CBitmapHandler.h"
#include "gui/SDL_Extensions.h"
#include "../lib/vcmi_endian.h"

View File

@ -38,7 +38,7 @@ VCMI_LIB_NAMESPACE_END
class CMapHandler;
class CSoundHandler;
class CMusicHandler;
class CCursorHandler;
class CursorHandler;
class IMainVideoPlayer;
class CServerHandler;
@ -49,7 +49,7 @@ public:
CSoundHandler * soundh;
CMusicHandler * musich;
CConsoleHandler * consoleh;
CCursorHandler * curh;
CursorHandler * curh;
IMainVideoPlayer * videoh;
};
extern CClientState * CCS;

View File

@ -13,8 +13,6 @@
#include <boost/program_options.hpp>
#include <vcmi/scripting/Service.h>
#include "gui/SDL_Extensions.h"
#include "CGameInfo.h"
#include "mapHandler.h"
@ -25,7 +23,7 @@
#include "lobby/CSelectionBase.h"
#include "windows/CCastleInterface.h"
#include "../lib/CConsoleHandler.h"
#include "gui/CCursorHandler.h"
#include "gui/CursorHandler.h"
#include "../lib/CGameState.h"
#include "../CCallback.h"
#include "CPlayerInterface.h"
@ -33,37 +31,32 @@
#include "../lib/CBuildingHandler.h"
#include "CVideoHandler.h"
#include "../lib/CHeroHandler.h"
#include "../lib/CCreatureHandler.h"
#include "../lib/spells/CSpellHandler.h"
#include "CMusicHandler.h"
#include "../lib/CGeneralTextHandler.h"
#include "Graphics.h"
#include "Client.h"
#include "../lib/CConfigHandler.h"
#include "../lib/serializer/BinaryDeserializer.h"
#include "../lib/serializer/BinarySerializer.h"
#include "../lib/VCMI_Lib.h"
#include "../lib/VCMIDirs.h"
#include "../lib/NetPacks.h"
#include "CMessage.h"
#include "../lib/CModHandler.h"
#include "../lib/ScriptHandler.h"
#include "../lib/CTownHandler.h"
#include "../lib/CArtHandler.h"
#include "../lib/GameConstants.h"
#include "gui/CGuiHandler.h"
#include "../lib/logging/CBasicLogConfigurator.h"
#include "../lib/StringConstants.h"
#include "../lib/CPlayerState.h"
#include "gui/CAnimation.h"
#include "../lib/serializer/Connection.h"
#include "CServerHandler.h"
#include "gui/NotificationHandler.h"
#include "ClientCommandManager.h"
#include <boost/asio.hpp>
#include "mainmenu/CPrologEpilogVideo.h"
#include <vstd/StringUtils.h>
#include <SDL.h>
#ifdef VCMI_WINDOWS
#include "SDL_syswm.h"
@ -71,7 +64,7 @@
#ifdef VCMI_ANDROID
#include "lib/CAndroidVMHelper.h"
#endif
#include "../lib/UnlockGuard.h"
#include "CMT.h"
#if __MINGW32__
@ -208,6 +201,8 @@ int main(int argc, char * argv[])
("lobby-host", "if this client hosts session")
("lobby-uuid", po::value<std::string>(), "uuid to the server")
("lobby-connections", po::value<ui16>(), "connections of server")
("lobby-username", po::value<std::string>(), "player name")
("lobby-gamemode", po::value<ui16>(), "use 0 for new game and 1 for load game")
("uuid", po::value<std::string>(), "uuid for the client");
if(argc > 1)
@ -247,7 +242,14 @@ int main(int argc, char * argv[])
std::cout.flags(std::ios::unitbuf);
#ifndef VCMI_IOS
console = new CConsoleHandler();
*console->cb = processCommand;
auto callbackFunction = [](std::string buffer, bool calledFromIngameConsole)
{
ClientCommandManager commandController;
commandController.processCommand(buffer, calledFromIngameConsole);
};
*console->cb = callbackFunction;
console->start();
#endif
@ -470,7 +472,7 @@ int main(int argc, char * argv[])
pomtime.getDiff();
graphics = new Graphics(); // should be before curh
CCS->curh = new CCursorHandler();
CCS->curh = new CursorHandler();
logGlobal->info("Screen handler: %d ms", pomtime.getDiff());
pomtime.getDiff();
@ -489,29 +491,8 @@ int main(int argc, char * argv[])
session["autoSkip"].Bool() = vm.count("autoSkip");
session["oneGoodAI"].Bool() = vm.count("oneGoodAI");
session["aiSolo"].Bool() = false;
std::shared_ptr<CMainMenu> mmenu;
session["lobby"].Bool() = false;
if(vm.count("lobby"))
{
session["lobby"].Bool() = true;
session["host"].Bool() = false;
session["address"].String() = vm["lobby-address"].as<std::string>();
CSH->uuid = vm["uuid"].as<std::string>();
session["port"].Integer() = vm["lobby-port"].as<ui16>();
logGlobal->info("Remote lobby mode at %s:%d, uuid is %s", session["address"].String(), session["port"].Integer(), CSH->uuid);
if(vm.count("lobby-host"))
{
session["host"].Bool() = true;
session["hostConnections"].String() = std::to_string(vm["lobby-connections"].as<ui16>());
session["hostUuid"].String() = vm["lobby-uuid"].as<std::string>();
logGlobal->info("This client will host session, server uuid is %s", session["hostUuid"].String());
}
//we should not reconnect to previous game in online mode
Settings saveSession = settings.write["server"]["reconnect"];
saveSession->Bool() = false;
}
if(vm.count("testmap"))
{
session["testmap"].String() = vm["testmap"].as<std::string>();
@ -526,7 +507,44 @@ int main(int argc, char * argv[])
}
else
{
GH.curInt = CMainMenu::create().get();
mmenu = CMainMenu::create();
GH.curInt = mmenu.get();
}
std::vector<std::string> names;
session["lobby"].Bool() = false;
if(vm.count("lobby"))
{
session["lobby"].Bool() = true;
session["host"].Bool() = false;
session["address"].String() = vm["lobby-address"].as<std::string>();
if(vm.count("lobby-username"))
session["username"].String() = vm["lobby-username"].as<std::string>();
else
session["username"].String() = settings["launcher"]["lobbyUsername"].String();
if(vm.count("lobby-gamemode"))
session["gamemode"].Integer() = vm["lobby-gamemode"].as<ui16>();
else
session["gamemode"].Integer() = 0;
CSH->uuid = vm["uuid"].as<std::string>();
session["port"].Integer() = vm["lobby-port"].as<ui16>();
logGlobal->info("Remote lobby mode at %s:%d, uuid is %s", session["address"].String(), session["port"].Integer(), CSH->uuid);
if(vm.count("lobby-host"))
{
session["host"].Bool() = true;
session["hostConnections"].String() = std::to_string(vm["lobby-connections"].as<ui16>());
session["hostUuid"].String() = vm["lobby-uuid"].as<std::string>();
logGlobal->info("This client will host session, server uuid is %s", session["hostUuid"].String());
}
//we should not reconnect to previous game in online mode
Settings saveSession = settings.write["server"]["reconnect"];
saveSession->Bool() = false;
//start lobby immediately
names.push_back(session["username"].String());
ESelectionScreen sscreen = session["gamemode"].Integer() == 0 ? ESelectionScreen::newGame : ESelectionScreen::loadGame;
mmenu->openLobby(sscreen, session["host"].Bool(), &names, ELoadMode::MULTI);
}
// Restore remote session - start game immediately
@ -548,437 +566,6 @@ int main(int argc, char * argv[])
return 0;
}
void printInfoAboutIntObject(const CIntObject *obj, int level)
{
std::stringstream sbuffer;
sbuffer << std::string(level, '\t');
sbuffer << typeid(*obj).name() << " *** ";
if (obj->active)
{
#define PRINT(check, text) if (obj->active & CIntObject::check) sbuffer << text
PRINT(LCLICK, 'L');
PRINT(RCLICK, 'R');
PRINT(HOVER, 'H');
PRINT(MOVE, 'M');
PRINT(KEYBOARD, 'K');
PRINT(TIME, 'T');
PRINT(GENERAL, 'A');
PRINT(WHEEL, 'W');
PRINT(DOUBLECLICK, 'D');
#undef PRINT
}
else
sbuffer << "inactive";
sbuffer << " at " << obj->pos.x <<"x"<< obj->pos.y;
sbuffer << " (" << obj->pos.w <<"x"<< obj->pos.h << ")";
logGlobal->info(sbuffer.str());
for(const CIntObject *child : obj->children)
printInfoAboutIntObject(child, level+1);
}
void removeGUI()
{
// CClient::endGame
GH.curInt = nullptr;
if(GH.topInt())
GH.topInt()->deactivate();
adventureInt = nullptr;
GH.listInt.clear();
GH.objsToBlit.clear();
GH.statusbar = nullptr;
logGlobal->info("Removed GUI.");
LOCPLINT = nullptr;
}
#ifndef VCMI_IOS
void processCommand(const std::string &message)
{
std::istringstream readed;
readed.str(message);
std::string cn; //command name
readed >> cn;
// Check mantis issue 2292 for details
// if(LOCPLINT && LOCPLINT->cingconsole)
// LOCPLINT->cingconsole->print(message);
if(message==std::string("die, fool"))
{
exit(EXIT_SUCCESS);
}
else if(cn==std::string("activate"))
{
int what;
readed >> what;
switch (what)
{
case 0:
GH.topInt()->activate();
break;
case 1:
adventureInt->activate();
break;
case 2:
LOCPLINT->castleInt->activate();
break;
}
}
else if(cn=="redraw")
{
GH.totalRedraw();
}
else if(cn=="screen")
{
std::cout << "Screenbuf points to ";
if(screenBuf == screen)
logGlobal->error("screen");
else if(screenBuf == screen2)
logGlobal->error("screen2");
else
logGlobal->error("?!?");
SDL_SaveBMP(screen, "Screen_c.bmp");
SDL_SaveBMP(screen2, "Screen2_c.bmp");
}
else if(cn=="save")
{
if(!CSH->client)
{
std::cout << "Game in not active";
return;
}
std::string fname;
readed >> fname;
CSH->client->save(fname);
}
// else if(cn=="load")
// {
// // TODO: this code should end the running game and manage to call startGame instead
// std::string fname;
// readed >> fname;
// CSH->client->loadGame(fname);
// }
else if(message=="convert txt")
{
VLC->generaltexth->dumpAllTexts();
}
else if(message=="get config")
{
std::cout << "Command accepted.\t";
const bfs::path outPath =
VCMIDirs::get().userExtractedPath() / "configuration";
bfs::create_directories(outPath);
const std::vector<std::string> contentNames = {"heroClasses", "artifacts", "creatures", "factions", "objects", "heroes", "spells", "skills"};
for(auto contentName : contentNames)
{
auto & content = (*VLC->modh->content)[contentName];
auto contentOutPath = outPath / contentName;
bfs::create_directories(contentOutPath);
for(auto & iter : content.modData)
{
const JsonNode & modData = iter.second.modData;
for(auto & nameAndObject : modData.Struct())
{
const JsonNode & object = nameAndObject.second;
std::string name = CModHandler::normalizeIdentifier(object.meta, CModHandler::scopeBuiltin(), nameAndObject.first);
boost::algorithm::replace_all(name,":","_");
const bfs::path filePath = contentOutPath / (name + ".json");
bfs::ofstream file(filePath);
file << object.toJson();
}
}
}
std::cout << "\rExtracting done :)\n";
std::cout << " Extracted files can be found in " << outPath << " directory\n";
}
#if SCRIPTING_ENABLED
else if(message=="get scripts")
{
std::cout << "Command accepted.\t";
const bfs::path outPath =
VCMIDirs::get().userExtractedPath() / "scripts";
bfs::create_directories(outPath);
for(auto & kv : VLC->scriptHandler->objects)
{
std::string name = kv.first;
boost::algorithm::replace_all(name,":","_");
const scripting::ScriptImpl * script = kv.second.get();
bfs::path filePath = outPath / (name + ".lua");
bfs::ofstream file(filePath);
file << script->getSource();
}
std::cout << "\rExtracting done :)\n";
std::cout << " Extracted files can be found in " << outPath << " directory\n";
}
#endif
else if(message=="get txt")
{
std::cout << "Command accepted.\t";
const bfs::path outPath =
VCMIDirs::get().userExtractedPath();
auto list = CResourceHandler::get()->getFilteredFiles([](const ResourceID & ident)
{
return ident.getType() == EResType::TEXT && boost::algorithm::starts_with(ident.getName(), "DATA/");
});
for (auto & filename : list)
{
const bfs::path filePath = outPath / (filename.getName() + ".TXT");
bfs::create_directories(filePath.parent_path());
bfs::ofstream file(filePath);
auto text = CResourceHandler::get()->load(filename)->readAll();
file.write((char*)text.first.get(), text.second);
}
std::cout << "\rExtracting done :)\n";
std::cout << " Extracted files can be found in " << outPath << " directory\n";
}
else if(cn=="crash")
{
int *ptr = nullptr;
*ptr = 666;
//disaster!
}
else if(cn == "mp" && adventureInt)
{
if(const CGHeroInstance *h = dynamic_cast<const CGHeroInstance *>(adventureInt->selection))
std::cout << h->movement << "; max: " << h->maxMovePoints(true) << "/" << h->maxMovePoints(false) << std::endl;
}
else if(cn == "bonuses")
{
bool jsonFormat = (message == "bonuses json");
auto format = [jsonFormat](const BonusList & b) -> std::string
{
if(jsonFormat)
return b.toJsonNode().toJson(true);
std::ostringstream ss;
ss << b;
return ss.str();
};
std::cout << "Bonuses of " << adventureInt->selection->getObjectName() << std::endl
<< format(adventureInt->selection->getBonusList()) << std::endl;
std::cout << "\nInherited bonuses:\n";
TCNodes parents;
adventureInt->selection->getParents(parents);
for(const CBonusSystemNode *parent : parents)
{
std::cout << "\nBonuses from " << typeid(*parent).name() << std::endl << format(*parent->getAllBonuses(Selector::all, Selector::all)) << std::endl;
}
}
else if(cn == "not dialog")
{
LOCPLINT->showingDialog->setn(false);
}
else if(cn == "gui")
{
for(auto & child : GH.listInt)
{
const auto childPtr = child.get();
if(const CIntObject * obj = dynamic_cast<const CIntObject *>(childPtr))
printInfoAboutIntObject(obj, 0);
else
std::cout << typeid(childPtr).name() << std::endl;
}
}
else if(cn=="tell")
{
std::string what;
int id1, id2;
readed >> what >> id1 >> id2;
if(what == "hs")
{
for(const CGHeroInstance *h : LOCPLINT->cb->getHeroesInfo())
if(h->type->ID.getNum() == id1)
if(const CArtifactInstance *a = h->getArt(ArtifactPosition(id2)))
std::cout << a->nodeName();
}
}
else if (cn == "set")
{
std::string what, value;
readed >> what;
Settings conf = settings.write["session"][what];
readed >> value;
if (value == "on")
{
conf->Bool() = true;
logGlobal->info("Option %s enabled!", what);
}
else if (value == "off")
{
conf->Bool() = false;
logGlobal->info("Option %s disabled!", what);
}
}
else if(cn == "unlock")
{
std::string mxname;
readed >> mxname;
if(mxname == "pim" && LOCPLINT)
LOCPLINT->pim->unlock();
}
else if(cn == "def2bmp")
{
std::string URI;
readed >> URI;
std::unique_ptr<CAnimation> anim = std::make_unique<CAnimation>(URI);
anim->preload();
anim->exportBitmaps(VCMIDirs::get().userExtractedPath());
}
else if(cn == "extract")
{
std::string URI;
readed >> URI;
if (CResourceHandler::get()->existsResource(ResourceID(URI)))
{
const bfs::path outPath = VCMIDirs::get().userExtractedPath() / URI;
auto data = CResourceHandler::get()->load(ResourceID(URI))->readAll();
bfs::create_directories(outPath.parent_path());
bfs::ofstream outFile(outPath, bfs::ofstream::binary);
outFile.write((char*)data.first.get(), data.second);
}
else
logGlobal->error("File not found!");
}
else if(cn == "setBattleAI")
{
std::string fname;
readed >> fname;
std::cout << "Will try loading that AI to see if it is correct name...\n";
try
{
if(auto ai = CDynLibHandler::getNewBattleAI(fname)) //test that given AI is indeed available... heavy but it is easy to make a typo and break the game
{
Settings neutralAI = settings.write["server"]["neutralAI"];
neutralAI->String() = fname;
std::cout << "Setting changed, from now the battle ai will be " << fname << "!\n";
}
}
catch(std::exception &e)
{
logGlobal->warn("Failed opening %s: %s", fname, e.what());
logGlobal->warn("Setting not changes, AI not found or invalid!");
}
}
auto giveTurn = [&](PlayerColor player)
{
YourTurn yt;
yt.player = player;
yt.daysWithoutCastle = CSH->client->getPlayerState(player)->daysWithoutCastle;
yt.applyCl(CSH->client);
};
Settings session = settings.write["session"];
if(cn == "autoskip")
{
session["autoSkip"].Bool() = !session["autoSkip"].Bool();
}
else if(cn == "gosolo")
{
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
if(!CSH->client)
{
std::cout << "Game in not active";
return;
}
PlayerColor color;
if(session["aiSolo"].Bool())
{
for(auto & elem : CSH->client->gameState()->players)
{
if(elem.second.human)
CSH->client->installNewPlayerInterface(std::make_shared<CPlayerInterface>(elem.first), elem.first);
}
}
else
{
color = LOCPLINT->playerID;
removeGUI();
for(auto & elem : CSH->client->gameState()->players)
{
if(elem.second.human)
{
auto AiToGive = CSH->client->aiNameForPlayer(*CSH->client->getPlayerSettings(elem.first), false);
logNetwork->info("Player %s will be lead by %s", elem.first, AiToGive);
CSH->client->installNewPlayerInterface(CDynLibHandler::getNewAI(AiToGive), elem.first);
}
}
GH.totalRedraw();
giveTurn(color);
}
session["aiSolo"].Bool() = !session["aiSolo"].Bool();
}
else if(cn == "controlai")
{
std::string colorName;
readed >> colorName;
boost::to_lower(colorName);
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
if(!CSH->client)
{
std::cout << "Game in not active";
return;
}
PlayerColor color;
if(LOCPLINT)
color = LOCPLINT->playerID;
for(auto & elem : CSH->client->gameState()->players)
{
if(elem.second.human || (colorName.length() &&
elem.first.getNum() != vstd::find_pos(GameConstants::PLAYER_COLOR_NAMES, colorName)))
{
continue;
}
removeGUI();
CSH->client->installNewPlayerInterface(std::make_shared<CPlayerInterface>(elem.first), elem.first);
}
GH.totalRedraw();
if(color != PlayerColor::NEUTRAL)
giveTurn(color);
}
// Check mantis issue 2292 for details
/* else if(client && client->serv && client->serv->connected && LOCPLINT) //send to server
{
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
LOCPLINT->cb->sendMessage(message);
}*/
}
#endif
//plays intro, ends when intro is over or button has been pressed (handles events)
void playIntro()
{

View File

@ -8,7 +8,11 @@
*
*/
#pragma once
#include <SDL_render.h>
struct SDL_Texture;
struct SDL_Window;
struct SDL_Renderer;
struct SDL_Surface;
extern SDL_Texture * screenTexture;
@ -19,5 +23,4 @@ extern SDL_Surface *screen; // main screen surface
extern SDL_Surface *screen2; // and hlp surface (used to store not-active interfaces layer)
extern SDL_Surface *screenBuf; // points to screen (if only advmapint is present) or screen2 (else) - should be used when updating controls which are not regularly redrawed
void removeGUI();
void handleQuit(bool ask = true);

View File

@ -18,12 +18,11 @@ set(client_SRCS
gui/CAnimation.cpp
gui/Canvas.cpp
gui/CCursorHandler.cpp
gui/CursorHandler.cpp
gui/CGuiHandler.cpp
gui/CIntObject.cpp
gui/ColorFilter.cpp
gui/Fonts.cpp
gui/Geometries.cpp
gui/SDL_Extensions.cpp
gui/NotificationHandler.cpp
gui/InterfaceObjectConfigurable.cpp
@ -82,6 +81,7 @@ set(client_SRCS
NetPacksClient.cpp
NetPacksLobbyClient.cpp
SDLRWwrapper.cpp
ClientCommandManager.cpp
)
set(client_HEADERS
@ -104,12 +104,12 @@ set(client_HEADERS
gui/CAnimation.h
gui/Canvas.h
gui/CCursorHandler.h
gui/CursorHandler.h
gui/CGuiHandler.h
gui/ColorFilter.h
gui/CIntObject.h
gui/Fonts.h
gui/Geometries.h
gui/TextAlignment.h
gui/SDL_Compat.h
gui/SDL_Extensions.h
gui/SDL_Pixels.h
@ -168,6 +168,7 @@ set(client_HEADERS
mapHandler.h
resource.h
SDLRWwrapper.h
ClientCommandManager.h
)
if(APPLE_IOS)
@ -232,7 +233,7 @@ if(WIN32)
add_custom_command(TARGET vcmiclient POST_BUILD
WORKING_DIRECTORY "$<TARGET_FILE_DIR:vcmiclient>"
COMMAND ${CMAKE_COMMAND} -E copy AI/fuzzylite.dll fuzzylite.dll
COMMAND ${CMAKE_COMMAND} -E copy AI/tbb.dll tbb.dll
COMMAND ${CMAKE_COMMAND} -E copy AI/tbb12.dll tbb12.dll
)
endif()
elseif(APPLE_IOS)

View File

@ -102,9 +102,7 @@ SDL_Surface * CMessage::drawDialogBox(int w, int h, PlayerColor playerColor)
{
for (int j=0; j<h; j+=background->h)
{
Rect srcR(0,0,background->w, background->h);
Rect dstR(i,j,w,h);
CSDL_Ext::blitSurface(background, &srcR, ret, &dstR);
CSDL_Ext::blitSurface(background, ret, Point(i,j));
}
}
drawBorder(playerColor, ret, w, h);

View File

@ -10,7 +10,6 @@
#pragma once
#include "Graphics.h"
#include "gui/Geometries.h"
struct SDL_Surface;
class CInfoWindow;

View File

@ -9,7 +9,7 @@
*/
#include "StdInc.h"
#include <SDL_mixer.h>
#include <SDL.h>
#include <SDL_timer.h>
#include "CMusicHandler.h"
#include "CGameInfo.h"
@ -20,7 +20,7 @@
#include "../lib/StringConstants.h"
#include "../lib/CRandomGenerator.h"
#include "../lib/VCMIDirs.h"
#include "../lib/Terrain.h"
#include "../lib/TerrainHandler.h"
#define VCMI_SOUND_NAME(x)
#define VCMI_SOUND_FILE(y) #y,
@ -89,36 +89,6 @@ CSoundHandler::CSoundHandler():
soundBase::battle02, soundBase::battle03, soundBase::battle04,
soundBase::battle05, soundBase::battle06, soundBase::battle07
};
//predefine terrain set
//TODO: support custom sounds for new terrains and load from json
horseSounds =
{
{Terrain::DIRT, soundBase::horseDirt},
{Terrain::SAND, soundBase::horseSand},
{Terrain::GRASS, soundBase::horseGrass},
{Terrain::SNOW, soundBase::horseSnow},
{Terrain::SWAMP, soundBase::horseSwamp},
{Terrain::ROUGH, soundBase::horseRough},
{Terrain::SUBTERRANEAN, soundBase::horseSubterranean},
{Terrain::LAVA, soundBase::horseLava},
{Terrain::WATER, soundBase::horseWater},
{Terrain::ROCK, soundBase::horseRock}
};
}
void CSoundHandler::loadHorseSounds()
{
const auto & terrains = CGI->terrainTypeHandler->terrains();
for(const auto & terrain : terrains)
{
//since all sounds are hardcoded, let's keep it
if(vstd::contains(horseSounds, terrain.id))
continue;
//Use already existing horse sound
horseSounds[terrain.id] = horseSounds.at(terrains[terrain.id].horseSoundId);
}
}
void CSoundHandler::init()
@ -368,9 +338,9 @@ CMusicHandler::CMusicHandler():
void CMusicHandler::loadTerrainMusicThemes()
{
for (const auto & terrain : CGI->terrainTypeHandler->terrains())
for (const auto & terrain : CGI->terrainTypeHandler->objects)
{
addEntryToSet("terrain_" + terrain.name, "Music/" + terrain.musicFilename);
addEntryToSet("terrain_" + terrain->getJsonKey(), "Music/" + terrain->musicFilename);
}
}
@ -399,6 +369,7 @@ void CMusicHandler::release()
boost::mutex::scoped_lock guard(mutex);
Mix_HookMusicFinished(nullptr);
current->stop();
current.reset();
next.reset();
@ -542,6 +513,20 @@ MusicEntry::MusicEntry(CMusicHandler *owner, std::string setName, std::string mu
}
MusicEntry::~MusicEntry()
{
if (playing)
{
assert(0);
logGlobal->error("Attempt to delete music while playing!");
Mix_HaltMusic();
}
if (loop == 0 && Mix_FadingMusic() != MIX_NO_FADING)
{
assert(0);
logGlobal->error("Attempt to delete music while fading out!");
Mix_HaltMusic();
}
logGlobal->trace("Del-ing music file %s", currentName);
if (music)
Mix_FreeMusic(music);
@ -619,7 +604,7 @@ bool MusicEntry::play()
bool MusicEntry::stop(int fade_ms)
{
if (playing)
if (Mix_PlayingMusic())
{
playing = false;
loop = 0;

View File

@ -11,7 +11,6 @@
#include "../lib/CConfigHandler.h"
#include "../lib/CSoundBase.h"
#include "../lib/Terrain.h"
struct _Mix_Music;
struct SDL_RWops;
@ -61,7 +60,6 @@ public:
CSoundHandler();
void init() override;
void loadHorseSounds();
void release() override;
void setVolume(ui32 percent) override;
@ -84,7 +82,6 @@ public:
// Sets
std::vector<soundBase::soundID> pickupSounds;
std::vector<soundBase::soundID> battleIntroSounds;
std::map<TerrainId, soundBase::soundID> horseSounds;
};
// Helper //now it looks somewhat useless

View File

@ -19,7 +19,7 @@
#include "battle/BattleWindow.h"
#include "../CCallback.h"
#include "windows/CCastleInterface.h"
#include "gui/CCursorHandler.h"
#include "gui/CursorHandler.h"
#include "windows/CKingdomInterface.h"
#include "CGameInfo.h"
#include "windows/CHeroWindow.h"
@ -61,7 +61,9 @@
#include "windows/InfoWindows.h"
#include "../lib/UnlockGuard.h"
#include "../lib/CPathfinder.h"
#include <SDL.h>
#include "../lib/RoadHandler.h"
#include "../lib/TerrainHandler.h"
#include <SDL_timer.h>
#include "CServerHandler.h"
// FIXME: only needed for CGameState::mutex
#include "../lib/CGameState.h"
@ -156,7 +158,6 @@ void CPlayerInterface::initGameInterface(std::shared_ptr<Environment> ENV, std::
cb = CB;
env = ENV;
CCS->soundh->loadHorseSounds();
CCS->musich->loadTerrainMusicThemes();
initializeHeroTownList();
@ -260,7 +261,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose)
{
updateAmbientSounds();
//We may need to change music - select new track, music handler will change it if needed
CCS->musich->playMusicFromSet("terrain", LOCPLINT->cb->getTile(hero->visitablePos())->terType->name, true, false);
CCS->musich->playMusicFromSet("terrain", LOCPLINT->cb->getTile(hero->visitablePos())->terType->getJsonKey(), true, false);
if(details.result == TryMoveHero::TELEPORTATION)
{
@ -409,7 +410,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose)
void CPlayerInterface::heroKilled(const CGHeroInstance* hero)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
LOG_TRACE_PARAMS(logGlobal, "Hero %s killed handler for player %s", hero->name % playerID);
LOG_TRACE_PARAMS(logGlobal, "Hero %s killed handler for player %s", hero->getNameTranslated() % playerID);
const CArmedInstance *newSelection = nullptr;
if (makingTurn)
@ -436,7 +437,7 @@ void CPlayerInterface::heroKilled(const CGHeroInstance* hero)
adventureInt->select(newSelection, true);
else if (adventureInt->selection == hero)
adventureInt->selection = nullptr;
if (vstd::contains(paths, hero))
paths.erase(hero);
}
@ -1267,7 +1268,7 @@ template <typename Handler> void CPlayerInterface::serializeTempl( Handler &h, c
if (p.second.nodes.size())
pathsMap[p.first] = p.second.endPos();
else
logGlobal->debug("%s has assigned an empty path! Ignoring it...", p.first->name);
logGlobal->debug("%s has assigned an empty path! Ignoring it...", p.first->getNameTranslated());
}
h & pathsMap;
}
@ -1362,14 +1363,14 @@ void CPlayerInterface::showGarrisonDialog( const CArmedInstance *up, const CGHer
*/
void CPlayerInterface::showArtifactAssemblyDialog(const Artifact * artifact, const Artifact * assembledArtifact, CFunctionList<bool()> onYes)
{
std::string text = artifact->getDescription();
std::string text = artifact->getDescriptionTranslated();
text += "\n\n";
std::vector<std::shared_ptr<CComponent>> scs;
if(assembledArtifact)
{
// You possess all of the components to...
text += boost::str(boost::format(CGI->generaltexth->allTexts[732]) % assembledArtifact->getName());
text += boost::str(boost::format(CGI->generaltexth->allTexts[732]) % assembledArtifact->getNameTranslated());
// Picture of assembled artifact at bottom.
auto sc = std::make_shared<CComponent>(CComponent::artifact, assembledArtifact->getIndex(), 0);
@ -1623,7 +1624,7 @@ int CPlayerInterface::getLastIndex( std::string namePrefix)
else
for (directory_iterator dir(gamesDir); dir != enddir; ++dir)
{
if (is_regular(dir->status()))
if (is_regular_file(dir->status()))
{
std::string name = dir->path().filename().string();
if (starts_with(name, namePrefix) && ends_with(name, ".vcgm1"))
@ -2372,8 +2373,9 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
for (auto & elem : path.nodes)
elem.coord = h->convertFromVisitablePos(elem.coord);
TerrainId currentTerrain = Terrain::BORDER; // not init yet
TerrainId currentTerrain = ETerrainId::NONE;
TerrainId newTerrain;
bool wasOnRoad = true;
int sh = -1;
auto canStop = [&](CGPathNode * node) -> bool
@ -2389,13 +2391,18 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
for (i=(int)path.nodes.size()-1; i>0 && (stillMoveHero.data == CONTINUE_MOVE || !canStop(&path.nodes[i])); i--)
{
int3 currentCoord = path.nodes[i].coord;
int3 prevCoord = path.nodes[i].coord;
int3 nextCoord = path.nodes[i-1].coord;
auto currentObject = getObj(currentCoord, currentCoord == h->pos);
auto prevRoad = cb->getTile(h->convertToVisitablePos(prevCoord))->roadType;
auto nextRoad = cb->getTile(h->convertToVisitablePos(nextCoord))->roadType;
bool movingOnRoad = prevRoad->getId() != Road::NO_ROAD && nextRoad->getId() != Road::NO_ROAD;
auto prevObject = getObj(prevCoord, prevCoord == h->pos);
auto nextObjectTop = getObj(nextCoord, false);
auto nextObject = getObj(nextCoord, true);
auto destTeleportObj = getDestTeleportObj(currentObject, nextObjectTop, nextObject);
auto destTeleportObj = getDestTeleportObj(prevObject, nextObjectTop, nextObject);
if (isTeleportAction(path.nodes[i-1].action) && destTeleportObj != nullptr)
{
CCS->soundh->stopSound(sh);
@ -2410,7 +2417,10 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
}
if(i != path.nodes.size() - 1)
{
sh = CCS->soundh->playSound(CCS->soundh->horseSounds[currentTerrain], -1);
if (movingOnRoad)
sh = CCS->soundh->playSound(VLC->terrainTypeHandler->getById(currentTerrain)->horseSound, -1);
else
sh = CCS->soundh->playSound(VLC->terrainTypeHandler->getById(currentTerrain)->horseSoundPenalty, -1);
}
continue;
}
@ -2428,12 +2438,16 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
sh = CCS->soundh->playSound(soundBase::horseFlying, -1);
#endif
{
newTerrain = cb->getTile(h->convertToVisitablePos(currentCoord))->terType->id;
if(newTerrain != currentTerrain)
newTerrain = cb->getTile(h->convertToVisitablePos(prevCoord))->terType->getId();
if(newTerrain != currentTerrain || wasOnRoad != movingOnRoad)
{
CCS->soundh->stopSound(sh);
sh = CCS->soundh->playSound(CCS->soundh->horseSounds[newTerrain], -1);
if (movingOnRoad)
sh = CCS->soundh->playSound(VLC->terrainTypeHandler->getById(newTerrain)->horseSound, -1);
else
sh = CCS->soundh->playSound(VLC->terrainTypeHandler->getById(newTerrain)->horseSoundPenalty, -1);
currentTerrain = newTerrain;
wasOnRoad = movingOnRoad;
}
}
@ -2473,6 +2487,9 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
// (i == 0) means hero went through all the path
adventureInt->updateMoveHero(h, (i != 0));
adventureInt->updateNextHero(h);
// ugly workaround to force instant update of adventure map
adventureInt->animValHitCount = 8;
}
setMovementStatus(false);

View File

@ -8,7 +8,6 @@
*
*/
#include "StdInc.h"
#include <SDL.h>
#include "CVideoHandler.h"
#include "gui/CGuiHandler.h"
@ -67,8 +66,8 @@ CVideoPlayer::CVideoPlayer()
context = nullptr;
texture = nullptr;
dest = nullptr;
destRect = genRect(0,0,0,0);
pos = genRect(0,0,0,0);
destRect = CSDL_Ext::genRect(0,0,0,0);
pos = CSDL_Ext::genRect(0,0,0,0);
refreshWait = 0;
refreshCount = 0;
doLoop = false;
@ -339,10 +338,10 @@ void CVideoPlayer::show( int x, int y, SDL_Surface *dst, bool update )
pos.x = x;
pos.y = y;
CSDL_Ext::blitSurface(dest, &destRect, dst, &pos);
CSDL_Ext::blitSurface(dest, destRect, dst, pos.topLeft());
if (update)
SDL_UpdateRect(dst, pos.x, pos.y, pos.w, pos.h);
CSDL_Ext::updateRect(dst, pos);
}
void CVideoPlayer::redraw( int x, int y, SDL_Surface *dst, bool update )
@ -442,7 +441,9 @@ bool CVideoPlayer::playVideo(int x, int y, bool stopOnKey)
if(stopOnKey && keyDown())
return false;
SDL_RenderCopy(mainRenderer, texture, nullptr, &pos);
SDL_Rect rect = CSDL_Ext::toSDL(pos);
SDL_RenderCopy(mainRenderer, texture, nullptr, &rect);
SDL_RenderPresent(mainRenderer);
// Wait 3 frames

View File

@ -9,8 +9,10 @@
*/
#pragma once
struct SDL_Surface;
#include "../lib/Rect.h"
struct SDL_Surface;
struct SDL_Texture;
class IVideoPlayer
{
@ -54,9 +56,6 @@ public:
#include "../lib/filesystem/CInputStream.h"
#include <SDL.h>
#include <SDL_video.h>
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
@ -106,8 +105,8 @@ class CVideoPlayer : public IMainVideoPlayer
SDL_Texture *texture;
SDL_Surface *dest;
SDL_Rect destRect; // valid when dest is used
SDL_Rect pos; // destination on screen
Rect destRect; // valid when dest is used
Rect pos; // destination on screen
int refreshWait; // Wait several refresh before updating the image
int refreshCount;

View File

@ -10,8 +10,6 @@
#include "StdInc.h"
#include "Client.h"
#include <SDL.h>
#include "CMusicHandler.h"
#include "../lib/mapping/CCampaignHandler.h"
#include "../CCallback.h"
@ -46,9 +44,9 @@
#include "../lib/CThreadHelper.h"
#include "../lib/registerTypes/RegisterTypes.h"
#include "gui/CGuiHandler.h"
#include "CMT.h"
#include "CServerHandler.h"
#include "../lib/ScriptHandler.h"
#include "windows/CAdvmapInterface.h"
#include <vcmi/events/EventBus.h>
#ifdef VCMI_ANDROID
@ -761,8 +759,29 @@ void CClient::reinitScripting()
#endif
}
void CClient::removeGUI()
{
// CClient::endGame
GH.curInt = nullptr;
if(GH.topInt())
GH.topInt()->deactivate();
adventureInt.reset();
GH.listInt.clear();
GH.objsToBlit.clear();
GH.statusbar.reset();
logGlobal->info("Removed GUI.");
LOCPLINT = nullptr;
}
#ifdef VCMI_ANDROID
extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_clientSetupJNI(JNIEnv * env, jobject cls)
{
logNetwork->info("Received clientSetupJNI");
CAndroidVMHelper::cacheVM(env);
}
extern "C" JNIEXPORT void JNICALL Java_eu_vcmi_vcmi_NativeMethods_notifyServerClosed(JNIEnv * env, jobject cls)
{
logNetwork->info("Received server closed signal");

View File

@ -240,6 +240,7 @@ public:
void showInfoDialog(InfoWindow * iw) override {};
void showInfoDialog(const std::string & msg, PlayerColor player) override {};
void removeGUI();
#if SCRIPTING_ENABLED
scripting::Pool * getGlobalContextPool() const override;

View File

@ -0,0 +1,494 @@
/*
* ClientCommandManager.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "ClientCommandManager.h"
#include "Client.h"
#include "CPlayerInterface.h"
#include "CServerHandler.h"
#include "gui/CGuiHandler.h"
#include "../lib/NetPacks.h"
#include "../lib/CConfigHandler.h"
#include "../lib/CGameState.h"
#include "../lib/CPlayerState.h"
#include "../lib/StringConstants.h"
#include "gui/CAnimation.h"
#include "windows/CAdvmapInterface.h"
#include "windows/CCastleInterface.h"
#include "../CCallback.h"
#include "../lib/CGeneralTextHandler.h"
#include "../lib/CHeroHandler.h"
#include "../lib/CModHandler.h"
#include "../lib/VCMIDirs.h"
#ifdef SCRIPTING_ENABLED
#include "../lib/ScriptHandler.h"
#endif
void ClientCommandManager::handleGoSolo()
{
Settings session = settings.write["session"];
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
if(!CSH->client)
{
printCommandMessage("Game is not in playing state");
return;
}
PlayerColor color;
if(session["aiSolo"].Bool())
{
for(auto & elem : CSH->client->gameState()->players)
{
if(elem.second.human)
CSH->client->installNewPlayerInterface(std::make_shared<CPlayerInterface>(elem.first), elem.first);
}
}
else
{
color = LOCPLINT->playerID;
CSH->client->removeGUI();
for(auto & elem : CSH->client->gameState()->players)
{
if(elem.second.human)
{
auto AiToGive = CSH->client->aiNameForPlayer(*CSH->client->getPlayerSettings(elem.first), false);
printCommandMessage("Player " + elem.first.getStr() + " will be lead by " + AiToGive, ELogLevel::INFO);
CSH->client->installNewPlayerInterface(CDynLibHandler::getNewAI(AiToGive), elem.first);
}
}
GH.totalRedraw();
giveTurn(color);
}
session["aiSolo"].Bool() = !session["aiSolo"].Bool();
}
void ClientCommandManager::handleControlAi(const std::string &colorName)
{
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
if(!CSH->client)
{
printCommandMessage("Game is not in playing state");
return;
}
PlayerColor color;
if(LOCPLINT)
color = LOCPLINT->playerID;
for(auto & elem : CSH->client->gameState()->players)
{
if(elem.second.human || (colorName.length() &&
elem.first.getNum() != vstd::find_pos(GameConstants::PLAYER_COLOR_NAMES, colorName)))
{
continue;
}
CSH->client->removeGUI();
CSH->client->installNewPlayerInterface(std::make_shared<CPlayerInterface>(elem.first), elem.first);
}
GH.totalRedraw();
if(color != PlayerColor::NEUTRAL)
giveTurn(color);
}
void ClientCommandManager::processCommand(const std::string &message, bool calledFromIngameConsole)
{
std::istringstream singleWordBuffer;
singleWordBuffer.str(message);
std::string commandName;
singleWordBuffer >> commandName;
currentCallFromIngameConsole = calledFromIngameConsole;
if(message==std::string("die, fool"))
{
exit(EXIT_SUCCESS);
}
else if(commandName == std::string("activate"))
{
int what;
singleWordBuffer >> what;
switch (what)
{
case 0:
GH.topInt()->activate();
break;
case 1:
adventureInt->activate();
break;
case 2:
LOCPLINT->castleInt->activate();
break;
default:
printCommandMessage("Wrong argument specified!", ELogLevel::ERROR);
}
}
else if(commandName == "redraw")
{
GH.totalRedraw();
}
else if(commandName == "screen")
{
printCommandMessage("Screenbuf points to ");
if(screenBuf == screen)
printCommandMessage("screen", ELogLevel::ERROR);
else if(screenBuf == screen2)
printCommandMessage("screen2", ELogLevel::ERROR);
else
printCommandMessage("?!?", ELogLevel::ERROR);
SDL_SaveBMP(screen, "Screen_c.bmp");
SDL_SaveBMP(screen2, "Screen2_c.bmp");
}
else if(commandName == "save")
{
if(!CSH->client)
{
printCommandMessage("Game is not in playing state");
return;
}
std::string fname;
singleWordBuffer >> fname;
CSH->client->save(fname);
}
// else if(commandName=="load")
// {
// // TODO: this code should end the running game and manage to call startGame instead
// std::string fname;
// singleWordBuffer >> fname;
// CSH->client->loadGame(fname);
// }
else if(message=="convert txt")
{
VLC->generaltexth->dumpAllTexts();
}
else if(message=="get config")
{
printCommandMessage("Command accepted.\t");
const boost::filesystem::path outPath =
VCMIDirs::get().userExtractedPath() / "configuration";
boost::filesystem::create_directories(outPath);
const std::vector<std::string> contentNames = {"heroClasses", "artifacts", "creatures", "factions", "objects", "heroes", "spells", "skills"};
for(auto contentName : contentNames)
{
auto & content = (*VLC->modh->content)[contentName];
auto contentOutPath = outPath / contentName;
boost::filesystem::create_directories(contentOutPath);
for(auto & iter : content.modData)
{
const JsonNode & modData = iter.second.modData;
for(auto & nameAndObject : modData.Struct())
{
const JsonNode & object = nameAndObject.second;
std::string name = CModHandler::makeFullIdentifier(object.meta, contentName, nameAndObject.first);
boost::algorithm::replace_all(name,":","_");
const boost::filesystem::path filePath = contentOutPath / (name + ".json");
boost::filesystem::ofstream file(filePath);
file << object.toJson();
}
}
}
printCommandMessage("\rExtracting done :)\n");
printCommandMessage("Extracted files can be found in " + outPath.string() + " directory\n");
}
#if SCRIPTING_ENABLED
else if(message=="get scripts")
{
printCommandMessage("Command accepted.\t");
const boost::filesystem::path outPath =
VCMIDirs::get().userExtractedPath() / "scripts";
boost::filesystem::create_directories(outPath);
for(auto & kv : VLC->scriptHandler->objects)
{
std::string name = kv.first;
boost::algorithm::replace_all(name,":","_");
const scripting::ScriptImpl * script = kv.second.get();
boost::filesystem::path filePath = outPath / (name + ".lua");
boost::filesystem::ofstream file(filePath);
file << script->getSource();
}
printCommandMessage("\rExtracting done :)\n");
printCommandMessage("Extracted files can be found in " + outPath.string() + " directory\n");
}
#endif
else if(message=="get txt")
{
printCommandMessage("Command accepted.\t");
const boost::filesystem::path outPath =
VCMIDirs::get().userExtractedPath();
auto list =
CResourceHandler::get()->getFilteredFiles([](const ResourceID & ident)
{
return ident.getType() == EResType::TEXT && boost::algorithm::starts_with(ident.getName(), "DATA/");
});
for (auto & filename : list)
{
const boost::filesystem::path filePath = outPath / (filename.getName() + ".TXT");
boost::filesystem::create_directories(filePath.parent_path());
boost::filesystem::ofstream file(filePath);
auto text = CResourceHandler::get()->load(filename)->readAll();
file.write((char*)text.first.get(), text.second);
}
printCommandMessage("\rExtracting done :)\n");
printCommandMessage("Extracted files can be found in " + outPath.string() + " directory\n");
}
else if(commandName == "crash")
{
int *ptr = nullptr;
*ptr = 666;
//disaster!
}
else if(commandName == "mp" && adventureInt)
{
if(const CGHeroInstance *h = dynamic_cast<const CGHeroInstance *>(adventureInt->selection))
printCommandMessage(std::to_string(h->movement) + "; max: " + std::to_string(h->maxMovePoints(true)) + "/" + std::to_string(h->maxMovePoints(false)) + "\n");
}
else if(commandName == "bonuses")
{
bool jsonFormat = (message == "bonuses json");
auto format = [jsonFormat](const BonusList & b) -> std::string
{
if(jsonFormat)
return b.toJsonNode().toJson(true);
std::ostringstream ss;
ss << b;
return ss.str();
};
printCommandMessage("Bonuses of " + adventureInt->selection->getObjectName() + "\n");
printCommandMessage(format(adventureInt->selection->getBonusList()) + "\n");
printCommandMessage("\nInherited bonuses:\n");
TCNodes parents;
adventureInt->selection->getParents(parents);
for(const CBonusSystemNode *parent : parents)
{
printCommandMessage(std::string("\nBonuses from ") + typeid(*parent).name() + "\n" + format(*parent->getAllBonuses(Selector::all, Selector::all)) + "\n");
}
}
else if(commandName == "not dialog")
{
LOCPLINT->showingDialog->setn(false);
}
else if(commandName == "gui")
{
for(auto & child : GH.listInt)
{
const auto childPtr = child.get();
if(const CIntObject * obj = dynamic_cast<const CIntObject *>(childPtr))
printInfoAboutInterfaceObject(obj, 0);
else
printCommandMessage(std::string(typeid(childPtr).name()) + "\n");
}
}
else if(commandName == "tell")
{
std::string what;
int id1, id2;
singleWordBuffer >> what >> id1 >> id2;
if(what == "hs")
{
for(const CGHeroInstance *h : LOCPLINT->cb->getHeroesInfo())
if(h->type->getIndex() == id1)
if(const CArtifactInstance *a = h->getArt(ArtifactPosition(id2)))
printCommandMessage(a->nodeName());
}
}
else if (commandName == "set")
{
std::string what, value;
singleWordBuffer >> what;
Settings config = settings.write["session"][what];
singleWordBuffer >> value;
if (value == "on")
{
config->Bool() = true;
printCommandMessage("Option " + what + " enabled!", ELogLevel::INFO);
}
else if (value == "off")
{
config->Bool() = false;
printCommandMessage("Option " + what + " disabled!", ELogLevel::INFO);
}
}
else if(commandName == "unlock")
{
std::string mxname;
singleWordBuffer >> mxname;
if(mxname == "pim" && LOCPLINT)
LOCPLINT->pim->unlock();
}
else if(commandName == "def2bmp")
{
std::string URI;
singleWordBuffer >> URI;
std::unique_ptr<CAnimation> anim = std::make_unique<CAnimation>(URI);
anim->preload();
anim->exportBitmaps(VCMIDirs::get().userExtractedPath());
}
else if(commandName == "extract")
{
std::string URI;
singleWordBuffer >> URI;
if (CResourceHandler::get()->existsResource(ResourceID(URI)))
{
const boost::filesystem::path outPath = VCMIDirs::get().userExtractedPath() / URI;
auto data = CResourceHandler::get()->load(ResourceID(URI))->readAll();
boost::filesystem::create_directories(outPath.parent_path());
boost::filesystem::ofstream outFile(outPath, boost::filesystem::ofstream::binary);
outFile.write((char*)data.first.get(), data.second);
}
else
printCommandMessage("File not found!", ELogLevel::ERROR);
}
else if(commandName == "setBattleAI")
{
std::string fname;
singleWordBuffer >> fname;
printCommandMessage("Will try loading that AI to see if it is correct name...\n");
try
{
if(auto ai = CDynLibHandler::getNewBattleAI(fname)) //test that given AI is indeed available... heavy but it is easy to make a typo and break the game
{
Settings neutralAI = settings.write["server"]["neutralAI"];
neutralAI->String() = fname;
printCommandMessage("Setting changed, from now the battle ai will be " + fname + "!\n");
}
}
catch(std::exception &e)
{
printCommandMessage("Failed opening " + fname + ": " + e.what(), ELogLevel::WARN);
printCommandMessage("Setting not changed, AI not found or invalid!", ELogLevel::WARN);
}
}
else if(commandName == "autoskip")
{
Settings session = settings.write["session"];
session["autoSkip"].Bool() = !session["autoSkip"].Bool();
}
else if(commandName == "gosolo")
{
ClientCommandManager::handleGoSolo();
}
else if(commandName == "controlai")
{
std::string colorName;
singleWordBuffer >> colorName;
boost::to_lower(colorName);
ClientCommandManager::handleControlAi(colorName);
}
else
{
printCommandMessage("Command not found :(", ELogLevel::ERROR);
}
}
void ClientCommandManager::giveTurn(const PlayerColor &colorIdentifier)
{
YourTurn yt;
yt.player = colorIdentifier;
yt.daysWithoutCastle = CSH->client->getPlayerState(colorIdentifier)->daysWithoutCastle;
yt.applyCl(CSH->client);
}
void ClientCommandManager::printInfoAboutInterfaceObject(const CIntObject *obj, int level)
{
std::stringstream sbuffer;
sbuffer << std::string(level, '\t');
sbuffer << typeid(*obj).name() << " *** ";
if (obj->active)
{
#define PRINT(check, text) if (obj->active & CIntObject::check) sbuffer << text
PRINT(LCLICK, 'L');
PRINT(RCLICK, 'R');
PRINT(HOVER, 'H');
PRINT(MOVE, 'M');
PRINT(KEYBOARD, 'K');
PRINT(TIME, 'T');
PRINT(GENERAL, 'A');
PRINT(WHEEL, 'W');
PRINT(DOUBLECLICK, 'D');
#undef PRINT
}
else
sbuffer << "inactive";
sbuffer << " at " << obj->pos.x <<"x"<< obj->pos.y;
sbuffer << " (" << obj->pos.w <<"x"<< obj->pos.h << ")";
printCommandMessage(sbuffer.str(), ELogLevel::INFO);
for(const CIntObject *child : obj->children)
printInfoAboutInterfaceObject(child, level+1);
}
void ClientCommandManager::printCommandMessage(const std::string &commandMessage, ELogLevel::ELogLevel messageType)
{
switch(messageType)
{
case ELogLevel::NOT_SET:
std::cout << commandMessage;
break;
case ELogLevel::TRACE:
logGlobal->trace(commandMessage);
break;
case ELogLevel::DEBUG:
logGlobal->debug(commandMessage);
break;
case ELogLevel::INFO:
logGlobal->info(commandMessage);
break;
case ELogLevel::WARN:
logGlobal->warn(commandMessage);
break;
case ELogLevel::ERROR:
logGlobal->error(commandMessage);
break;
default:
std::cout << commandMessage;
break;
}
if(currentCallFromIngameConsole)
{
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
if(LOCPLINT && LOCPLINT->cingconsole)
{
LOCPLINT->cingconsole->print(commandMessage);
}
}
}

View File

@ -0,0 +1,31 @@
/*
* ClientCommandManager.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
VCMI_LIB_NAMESPACE_BEGIN
class PlayerColor;
VCMI_LIB_NAMESPACE_END
class CIntObject;
class ClientCommandManager //take mantis #2292 issue about account if thinking about handling cheats from command-line
{
bool currentCallFromIngameConsole;
void giveTurn(const PlayerColor &color);
void printInfoAboutInterfaceObject(const CIntObject *obj, int level);
void printCommandMessage(const std::string &commandMessage, ELogLevel::ELogLevel messageType = ELogLevel::NOT_SET);
void handleGoSolo();
void handleControlAi(const std::string &colorName);
public:
ClientCommandManager() = default;
void processCommand(const std::string &message, bool calledFromIngameConsole);
};

View File

@ -286,7 +286,7 @@ void Graphics::blueToPlayersAdv(SDL_Surface * sur, PlayerColor player)
//FIXME: not all player colored images have player palette at last 32 indexes
//NOTE: following code is much more correct but still not perfect (bugged with status bar)
SDL_SetColors(sur, palette, 224, 32);
CSDL_Ext::setColors(sur, palette, 224, 32);
#if 0

View File

@ -11,7 +11,6 @@
#include "gui/Fonts.h"
#include "../lib/GameConstants.h"
#include "gui/Geometries.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@ -63,7 +63,7 @@ void LobbyClientDisconnected::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHa
void LobbyChatMessage::applyOnLobbyScreen(CLobbyScreen * lobby, CServerHandler * handler)
{
if(lobby)
if(lobby && lobby->card)
{
lobby->card->chat->addNewMessage(playerName + ": " + message);
lobby->card->setChat(true);

View File

@ -19,7 +19,7 @@
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../gui/CCursorHandler.h"
#include "../gui/CursorHandler.h"
#include "../gui/CGuiHandler.h"
#include "../gui/CIntObject.h"
#include "../windows/CCreatureWindow.h"
@ -460,28 +460,31 @@ void BattleActionsController::handleHex(BattleHex myNumber, int eventType)
}
};
TDmgRange damage = owner.curInt->cb->battleEstimateDamage(owner.stacksController->getActiveStack(), shere);
BattleHex attackFromHex = owner.fieldController->fromWhichHexAttack(myNumber);
TDmgRange damage = owner.curInt->cb->battleEstimateDamage(owner.stacksController->getActiveStack(), shere, attackFromHex);
std::string estDmgText = formatDmgRange(std::make_pair((ui32)damage.first, (ui32)damage.second)); //calculating estimated dmg
newConsoleMsg = (boost::format(CGI->generaltexth->allTexts[36]) % shere->getName() % estDmgText).str(); //Attack %s (%s damage)
}
break;
case PossiblePlayerBattleAction::SHOOT:
{
if (owner.curInt->cb->battleHasShootingPenalty(owner.stacksController->getActiveStack(), myNumber))
auto const * shooter = owner.stacksController->getActiveStack();
if (owner.curInt->cb->battleHasShootingPenalty(shooter, myNumber))
cursorFrame = Cursor::Combat::SHOOT_PENALTY;
else
cursorFrame = Cursor::Combat::SHOOT;
realizeAction = [=](){owner.giveCommand(EActionType::SHOOT, myNumber);};
TDmgRange damage = owner.curInt->cb->battleEstimateDamage(owner.stacksController->getActiveStack(), shere);
TDmgRange damage = owner.curInt->cb->battleEstimateDamage(shooter, shere, shooter->getPosition());
std::string estDmgText = formatDmgRange(std::make_pair((ui32)damage.first, (ui32)damage.second)); //calculating estimated dmg
//printing - Shoot %s (%d shots left, %s damage)
newConsoleMsg = (boost::format(CGI->generaltexth->allTexts[296]) % shere->getName() % owner.stacksController->getActiveStack()->shots.available() % estDmgText).str();
newConsoleMsg = (boost::format(CGI->generaltexth->allTexts[296]) % shere->getName() % shooter->shots.available() % estDmgText).str();
}
break;
case PossiblePlayerBattleAction::AIMED_SPELL_CREATURE:
currentSpell = CGI->spellh->objects[creatureCasting ? owner.stacksController->activeStackSpellToCast() : spellToCast->actionSubtype]; //necessary if creature has random Genie spell at same time
newConsoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[27]) % currentSpell->name % shere->getName()); //Cast %s on %s
newConsoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[27]) % currentSpell->getNameTranslated() % shere->getName()); //Cast %s on %s
switch (currentSpell->id)
{
case SpellID::SACRIFICE:
@ -494,7 +497,7 @@ void BattleActionsController::handleHex(BattleHex myNumber, int eventType)
break;
case PossiblePlayerBattleAction::ANY_LOCATION:
currentSpell = CGI->spellh->objects[creatureCasting ? owner.stacksController->activeStackSpellToCast() : spellToCast->actionSubtype]; //necessary if creature has random Genie spell at same time
newConsoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[26]) % currentSpell->name); //Cast %s
newConsoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[26]) % currentSpell->getNameTranslated()); //Cast %s
isCastingPossible = true;
break;
case PossiblePlayerBattleAction::RANDOM_GENIE_SPELL: //we assume that teleport / sacrifice will never be available as random spell
@ -519,7 +522,7 @@ void BattleActionsController::handleHex(BattleHex myNumber, int eventType)
isCastingPossible = true;
break;
case PossiblePlayerBattleAction::FREE_LOCATION:
newConsoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[26]) % currentSpell->name); //Cast %s
newConsoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[26]) % currentSpell->getNameTranslated()); //Cast %s
isCastingPossible = true;
break;
case PossiblePlayerBattleAction::HEAL:
@ -558,7 +561,7 @@ void BattleActionsController::handleHex(BattleHex myNumber, int eventType)
break;
case PossiblePlayerBattleAction::FREE_LOCATION:
cursorFrame = Cursor::Combat::BLOCKED;
newConsoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[181]) % currentSpell->name); //No room to place %s here
newConsoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[181]) % currentSpell->getNameTranslated()); //No room to place %s here
break;
default:
if (myNumber == -1)
@ -579,7 +582,7 @@ void BattleActionsController::handleHex(BattleHex myNumber, int eventType)
default:
spellcastingCursor = true;
if (newConsoleMsg.empty() && currentSpell)
newConsoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[26]) % currentSpell->name); //Cast %s
newConsoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[26]) % currentSpell->getNameTranslated()); //Cast %s
break;
}

View File

@ -22,7 +22,7 @@
#include "../CGameInfo.h"
#include "../CMusicHandler.h"
#include "../CPlayerInterface.h"
#include "../gui/CCursorHandler.h"
#include "../gui/CursorHandler.h"
#include "../gui/CGuiHandler.h"
#include "../../CCallback.h"

View File

@ -10,7 +10,6 @@
#pragma once
#include "../../lib/battle/BattleHex.h"
#include "../gui/Geometries.h"
#include "BattleConstants.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -18,6 +17,7 @@ VCMI_LIB_NAMESPACE_BEGIN
class CStack;
class CCreature;
class CSpell;
class Point;
VCMI_LIB_NAMESPACE_END
@ -28,7 +28,6 @@ class CAnimation;
class BattleInterface;
class CreatureAnimation;
struct StackAttackedInfo;
struct Point;
/// Base class of battle animations
class BattleAnimation

View File

@ -10,7 +10,7 @@
#pragma once
#include "../../lib/battle/BattleHex.h"
#include "../gui/Geometries.h"
#include "../../lib/Point.h"
#include "BattleConstants.h"
VCMI_LIB_NAMESPACE_BEGIN

View File

@ -26,7 +26,7 @@
#include "../gui/CAnimation.h"
#include "../gui/Canvas.h"
#include "../gui/CGuiHandler.h"
#include "../gui/CCursorHandler.h"
#include "../gui/CursorHandler.h"
#include "../../CCallback.h"
#include "../../lib/BattleFieldHandler.h"
@ -211,11 +211,11 @@ std::set<BattleHex> BattleFieldController::getHighlightedHexesStackRange()
for(BattleHex hex : set)
result.insert(hex);
// display the movement shadow of the stack at b (i.e. stack under mouse)
const CStack * const shere = owner.curInt->cb->battleGetStackByPos(hoveredHex, false);
if(shere && shere != owner.stacksController->getActiveStack() && shere->alive())
// display the movement shadow of stack under mouse
const CStack * const hoveredStack = owner.curInt->cb->battleGetStackByPos(hoveredHex, true);
if(hoveredStack && hoveredStack != owner.stacksController->getActiveStack())
{
std::vector<BattleHex> v = owner.curInt->cb->battleGetAvailableHexes(shere, true, nullptr);
std::vector<BattleHex> v = owner.curInt->cb->battleGetAvailableHexes(hoveredStack, true, nullptr);
for(BattleHex hex : v)
result.insert(hex);
}

View File

@ -15,12 +15,11 @@
VCMI_LIB_NAMESPACE_BEGIN
class CStack;
class Rect;
class Point;
VCMI_LIB_NAMESPACE_END
struct Rect;
struct Point;
class ClickableHex;
class BattleHero;
class Canvas;

View File

@ -28,7 +28,7 @@
#include "../CMusicHandler.h"
#include "../CPlayerInterface.h"
#include "../gui/Canvas.h"
#include "../gui/CCursorHandler.h"
#include "../gui/CursorHandler.h"
#include "../gui/CGuiHandler.h"
#include "../windows/CAdvmapInterface.h"
@ -41,6 +41,7 @@
#include "../../lib/mapObjects/CGTownInstance.h"
#include "../../lib/NetPacks.h"
#include "../../lib/UnlockGuard.h"
#include "../../lib/TerrainHandler.h"
CondSh<BattleAction *> BattleInterface::givenCommand(nullptr);
@ -136,8 +137,8 @@ BattleInterface::~BattleInterface()
if (adventureInt && adventureInt->selection)
{
//FIXME: this should be moved to adventureInt which should restore correct track based on selection/active player
const auto & terrain = *(LOCPLINT->cb->getTile(adventureInt->selection->visitablePos())->terType);
CCS->musich->playMusicFromSet("terrain", terrain.name, true, false);
const auto * terrain = LOCPLINT->cb->getTile(adventureInt->selection->visitablePos())->terType;
CCS->musich->playMusicFromSet("terrain", terrain->getJsonKey(), true, false);
}
// may happen if user decided to close game while in battle

View File

@ -26,7 +26,7 @@
#include "../Graphics.h"
#include "../gui/CAnimation.h"
#include "../gui/Canvas.h"
#include "../gui/CCursorHandler.h"
#include "../gui/CursorHandler.h"
#include "../gui/CGuiHandler.h"
#include "../widgets/AdventureMapClasses.h"
#include "../widgets/Buttons.h"
@ -160,7 +160,7 @@ void BattleConsole::setEnteringMode(bool on)
if (on)
{
assert(enteringText == false);
CSDL_Ext::startTextInput(&pos);
CSDL_Ext::startTextInput(pos);
}
else
{
@ -308,7 +308,7 @@ void BattleHero::clickRight(tribool down, bool previousState)
return;
Point windowPosition;
windowPosition.x = (!defender) ? owner.fieldController->pos.topLeft().x + 1 : owner.fieldController->pos.topRight().x - 79;
windowPosition.x = (!defender) ? owner.fieldController->pos.left() + 1 : owner.fieldController->pos.right() - 79;
windowPosition.y = owner.fieldController->pos.y + 135;
InfoAboutHero targetHero;
@ -494,7 +494,7 @@ BattleResultWindow::BattleResultWindow(const BattleResult & br, CPlayerInterface
{
OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
pos = genRect(561, 470, (screen->w - 800)/2 + 165, (screen->h - 600)/2 + 19);
pos = CSDL_Ext::genRect(561, 470, (screen->w - 800)/2 + 165, (screen->h - 600)/2 + 19);
background = std::make_shared<CPicture>("CPRESULT");
background->colorize(owner.playerID);
@ -551,7 +551,7 @@ BattleResultWindow::BattleResultWindow(const BattleResult & br, CPlayerInterface
if(best != stacks.end()) //should be always but to be safe...
{
icons.push_back(std::make_shared<CAnimImage>("TWCRPORT", (*best)->type->getIconIndex(), 0, xs[i], 38));
sideNames[i] = (*best)->type->getPluralName();
sideNames[i] = (*best)->type->getNamePluralTranslated();
}
}
}
@ -613,7 +613,7 @@ BattleResultWindow::BattleResultWindow(const BattleResult & br, CPlayerInterface
if (ourHero)
{
str += CGI->generaltexth->allTexts[305];
boost::algorithm::replace_first(str, "%s", ourHero->name);
boost::algorithm::replace_first(str, "%s", ourHero->getNameTranslated());
boost::algorithm::replace_first(str, "%d", boost::lexical_cast<std::string>(br.exp[weAreAttacker ? 0 : 1]));
}
@ -804,7 +804,7 @@ void StackQueue::update()
int32_t StackQueue::getSiegeShooterIconID()
{
return owner.siegeController->getSiegedTown()->town->faction->index;
return owner.siegeController->getSiegedTown()->town->faction->getIndex();
}
StackQueue::StackBox::StackBox(StackQueue * owner):
@ -850,7 +850,7 @@ void StackQueue::StackBox::setUnit(const battle::Unit * unit, size_t turn)
if (unit->unitType()->idNumber == CreatureID::ARROW_TOWERS)
icon->setFrame(owner->getSiegeShooterIconID(), 1);
amount->setText(makeNumberShort(unit->getCount()));
amount->setText(CSDL_Ext::makeNumberShort(unit->getCount()));
if(stateIcon)
{

View File

@ -13,6 +13,7 @@ VCMI_LIB_NAMESPACE_BEGIN
struct BattleHex;
struct CObstacleInstance;
class Point;
VCMI_LIB_NAMESPACE_END
@ -21,7 +22,6 @@ class Canvas;
class CAnimation;
class BattleInterface;
class BattleRenderer;
struct Point;
/// Controls all currently active projectiles on the battlefield
/// (with exception of moat, which is apparently handled by siege controller)

View File

@ -15,7 +15,6 @@
#include "BattleStacksController.h"
#include "CreatureAnimation.h"
#include "../gui/Geometries.h"
#include "../gui/CAnimation.h"
#include "../gui/Canvas.h"
#include "../gui/CGuiHandler.h"
@ -158,7 +157,7 @@ const CCreature & BattleProjectileController::getShooter(const CStack * stack) c
if(creature->animation.missleFrameAngles.empty())
{
logAnim->error("Mod error: Creature '%s' on the Archer's tower is not a shooter. Mod should be fixed. Trying to use archer's data instead...", creature->nameSing);
logAnim->error("Mod error: Creature '%s' on the Archer's tower is not a shooter. Mod should be fixed. Trying to use archer's data instead...", creature->getNameSingularTranslated());
creature = CGI->creh->objects[CreatureID::ARCHER];
}
@ -313,7 +312,7 @@ void BattleProjectileController::createProjectile(const CStack * shooter, Point
std::shared_ptr<ProjectileBase> projectile;
if (stackUsesRayProjectile(shooter) && stackUsesMissileProjectile(shooter))
{
logAnim->error("Mod error: Creature '%s' has both missile and ray projectiles configured. Mod should be fixed. Using ray projectile configuration...", shooterInfo.nameSing);
logAnim->error("Mod error: Creature '%s' has both missile and ray projectiles configured. Mod should be fixed. Using ray projectile configuration...", shooterInfo.getNameSingularTranslated());
}
if (stackUsesRayProjectile(shooter))

View File

@ -10,7 +10,7 @@
#pragma once
#include "../../lib/CCreatureHandler.h"
#include "../gui/Geometries.h"
#include "../../lib/Point.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -19,7 +19,6 @@ class CSpell;
VCMI_LIB_NAMESPACE_END
struct Point;
class CAnimation;
class Canvas;
class BattleInterface;

View File

@ -28,22 +28,29 @@
#include "../../lib/CStack.h"
#include "../../lib/mapObjects/CGTownInstance.h"
std::string BattleSiegeController::getWallPieceImageName(EWallVisual::EWallVisual what, EWallState::EWallState state) const
std::string BattleSiegeController::getWallPieceImageName(EWallVisual::EWallVisual what, EWallState state) const
{
auto getImageIndex = [&]() -> int
{
bool isTower = (what == EWallVisual::KEEP || what == EWallVisual::BOTTOM_TOWER || what == EWallVisual::UPPER_TOWER);
switch (state)
{
case EWallState::INTACT :
case EWallState::REINFORCED :
return 1;
case EWallState::INTACT :
if (town->hasBuilt(BuildingID::CASTLE))
return 2; // reinforced walls were damaged
else
return 1;
case EWallState::DAMAGED :
// towers don't have separate image here - INTACT and DAMAGED is 1, DESTROYED is 2
if(what == EWallVisual::KEEP || what == EWallVisual::BOTTOM_TOWER || what == EWallVisual::UPPER_TOWER)
if (isTower)
return 1;
else
return 2;
case EWallState::DESTROYED :
if (what == EWallVisual::KEEP || what == EWallVisual::BOTTOM_TOWER || what == EWallVisual::UPPER_TOWER)
if (isTower)
return 2;
else
return 3;
@ -58,7 +65,7 @@ std::string BattleSiegeController::getWallPieceImageName(EWallVisual::EWallVisua
{
case EWallVisual::BACKGROUND_WALL:
{
switch(town->town->faction->index)
switch(town->town->faction->getIndex())
{
case ETownType::RAMPART:
case ETownType::NECROPOLIS:
@ -128,11 +135,11 @@ bool BattleSiegeController::getWallPieceExistance(EWallVisual::EWallVisual what)
switch (what)
{
case EWallVisual::MOAT: return town->hasBuilt(BuildingID::CITADEL) && town->town->faction->index != ETownType::TOWER;
case EWallVisual::MOAT_BANK: return town->hasBuilt(BuildingID::CITADEL) && town->town->faction->index != ETownType::TOWER && town->town->faction->index != ETownType::NECROPOLIS;
case EWallVisual::KEEP_BATTLEMENT: return town->hasBuilt(BuildingID::CITADEL) && EWallState::EWallState(owner.curInt->cb->battleGetWallState(EWallPart::KEEP)) != EWallState::DESTROYED;
case EWallVisual::UPPER_BATTLEMENT: return town->hasBuilt(BuildingID::CASTLE) && EWallState::EWallState(owner.curInt->cb->battleGetWallState(EWallPart::UPPER_TOWER)) != EWallState::DESTROYED;
case EWallVisual::BOTTOM_BATTLEMENT: return town->hasBuilt(BuildingID::CASTLE) && EWallState::EWallState(owner.curInt->cb->battleGetWallState(EWallPart::BOTTOM_TOWER)) != EWallState::DESTROYED;
case EWallVisual::MOAT: return town->hasBuilt(BuildingID::CITADEL) && town->town->faction->getIndex() != ETownType::TOWER;
case EWallVisual::MOAT_BANK: return town->hasBuilt(BuildingID::CITADEL) && town->town->faction->getIndex() != ETownType::TOWER && town->town->faction->getIndex() != ETownType::NECROPOLIS;
case EWallVisual::KEEP_BATTLEMENT: return town->hasBuilt(BuildingID::CITADEL) && owner.curInt->cb->battleGetWallState(EWallPart::KEEP) != EWallState::DESTROYED;
case EWallVisual::UPPER_BATTLEMENT: return town->hasBuilt(BuildingID::CASTLE) && owner.curInt->cb->battleGetWallState(EWallPart::UPPER_TOWER) != EWallState::DESTROYED;
case EWallVisual::BOTTOM_BATTLEMENT: return town->hasBuilt(BuildingID::CASTLE) && owner.curInt->cb->battleGetWallState(EWallPart::BOTTOM_TOWER) != EWallState::DESTROYED;
default: return true;
}
}
@ -177,7 +184,7 @@ BattleSiegeController::BattleSiegeController(BattleInterface & owner, const CGTo
if ( !getWallPieceExistance(EWallVisual::EWallVisual(g)) )
continue;
wallPieceImages[g] = IImage::createFromFile(getWallPieceImageName(EWallVisual::EWallVisual(g), EWallState::INTACT));
wallPieceImages[g] = IImage::createFromFile(getWallPieceImageName(EWallVisual::EWallVisual(g), EWallState::REINFORCED));
}
}
@ -321,7 +328,7 @@ bool BattleSiegeController::isAttackableByCatapult(BattleHex hex) const
if (!owner.curInt->cb->isWallPartPotentiallyAttackable(wallPart))
return false;
auto state = owner.curInt->cb->battleGetWallState(static_cast<int>(wallPart));
auto state = owner.curInt->cb->battleGetWallState(wallPart);
return state != EWallState::DESTROYED && state != EWallState::NONE;
}
@ -354,12 +361,12 @@ void BattleSiegeController::stackIsCatapulting(const CatapultAttack & ca)
for (auto attackInfo : ca.attackedParts)
{
int wallId = attackInfo.attackedPart + EWallVisual::DESTRUCTIBLE_FIRST;
int wallId = static_cast<int>(attackInfo.attackedPart) + EWallVisual::DESTRUCTIBLE_FIRST;
//gate state changing handled separately
if (wallId == EWallVisual::GATE)
continue;
auto wallState = EWallState::EWallState(owner.curInt->cb->battleGetWallState(attackInfo.attackedPart));
auto wallState = EWallState(owner.curInt->cb->battleGetWallState(attackInfo.attackedPart));
wallPieceImages[wallId] = IImage::createFromFile(getWallPieceImageName(EWallVisual::EWallVisual(wallId), wallState));
}

View File

@ -18,10 +18,10 @@ struct CatapultAttack;
class CCreature;
class CStack;
class CGTownInstance;
class Point;
VCMI_LIB_NAMESPACE_END
struct Point;
class Canvas;
class BattleInterface;
class BattleRenderer;
@ -76,7 +76,7 @@ class BattleSiegeController
std::array<std::shared_ptr<IImage>, EWallVisual::WALL_LAST + 1> wallPieceImages;
/// return URI for image for a wall piece
std::string getWallPieceImageName(EWallVisual::EWallVisual what, EWallState::EWallState state) const;
std::string getWallPieceImageName(EWallVisual::EWallVisual what, EWallState state) const;
/// returns BattleHex to which chosen wall piece is bound
BattleHex getWallPiecePosition(EWallVisual::EWallVisual what) const;

View File

@ -28,6 +28,7 @@
#include "../gui/CAnimation.h"
#include "../gui/CGuiHandler.h"
#include "../gui/Canvas.h"
#include "../gui/SDL_Extensions.h"
#include "../../lib/spells/ISpellMechanics.h"
#include "../../CCallback.h"
@ -83,10 +84,10 @@ BattleStacksController::BattleStacksController(BattleInterface & owner):
amountNegative = IImage::createFromFile("CMNUMWIN.BMP");
amountEffNeutral = IImage::createFromFile("CMNUMWIN.BMP");
static const auto shifterNormal = ColorFilter::genRangeShifter( 0,0,0, 0.6, 0.2, 1.0 );
static const auto shifterPositive = ColorFilter::genRangeShifter( 0,0,0, 0.2, 1.0, 0.2 );
static const auto shifterNegative = ColorFilter::genRangeShifter( 0,0,0, 1.0, 0.2, 0.2 );
static const auto shifterNeutral = ColorFilter::genRangeShifter( 0,0,0, 1.0, 1.0, 0.2 );
static const auto shifterNormal = ColorFilter::genRangeShifter( 0.f, 0.f, 0.f, 0.6f, 0.2f, 1.0f );
static const auto shifterPositive = ColorFilter::genRangeShifter( 0.f, 0.f, 0.f, 0.2f, 1.0f, 0.2f );
static const auto shifterNegative = ColorFilter::genRangeShifter( 0.f, 0.f, 0.f, 1.0f, 0.2f, 0.2f );
static const auto shifterNeutral = ColorFilter::genRangeShifter( 0.f, 0.f, 0.f, 1.0f, 1.0f, 0.2f );
amountNormal->adjustPalette(shifterNormal);
amountPositive->adjustPalette(shifterPositive);
@ -317,7 +318,7 @@ void BattleStacksController::showStackAmountBox(Canvas & canvas, const CStack *
//blitting amount
Point textPos = stackAnimation[stack->ID]->pos.topLeft() + amountBG->dimensions()/2 + Point(xAdd, yAdd);
canvas.drawText(textPos, EFonts::FONT_TINY, Colors::WHITE, ETextAlignment::CENTER, makeNumberShort(stack->getCount()));
canvas.drawText(textPos, EFonts::FONT_TINY, Colors::WHITE, ETextAlignment::CENTER, CSDL_Ext::makeNumberShort(stack->getCount()));
}
void BattleStacksController::showStack(Canvas & canvas, const CStack * stack)
@ -543,7 +544,6 @@ void BattleStacksController::stackMoved(const CStack *stack, std::vector<BattleH
bool BattleStacksController::shouldAttackFacingRight(const CStack * attacker, const CStack * defender)
{
bool mustReverse = owner.curInt->cb->isToReverse(
attacker->getPosition(),
attacker,
defender);

View File

@ -9,7 +9,6 @@
*/
#pragma once
#include "../gui/Geometries.h"
#include "../gui/ColorFilter.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -19,6 +18,7 @@ class BattleAction;
class CStack;
class CSpell;
class SpellID;
class Point;
VCMI_LIB_NAMESPACE_END

View File

@ -21,7 +21,7 @@
#include "../CPlayerInterface.h"
#include "../CMusicHandler.h"
#include "../gui/Canvas.h"
#include "../gui/CCursorHandler.h"
#include "../gui/CursorHandler.h"
#include "../gui/CGuiHandler.h"
#include "../gui/CAnimation.h"
#include "../windows/CSpellWindow.h"
@ -274,10 +274,10 @@ void BattleWindow::bFleef()
//calculating fleeing hero's name
if (owner.attackingHeroInstance)
if (owner.attackingHeroInstance->tempOwner == owner.curInt->cb->getMyColor())
heroName = owner.attackingHeroInstance->name;
heroName = owner.attackingHeroInstance->getNameTranslated();
if (owner.defendingHeroInstance)
if (owner.defendingHeroInstance->tempOwner == owner.curInt->cb->getMyColor())
heroName = owner.defendingHeroInstance->name;
heroName = owner.defendingHeroInstance->getNameTranslated();
//calculating text
auto txt = boost::format(CGI->generaltexth->allTexts[340]) % heroName; //The Shackles of War are present. %s can not retreat!
@ -416,11 +416,11 @@ void BattleWindow::bSpellf()
const auto artID = ArtifactID(blockingBonus->sid);
//If we have artifact, put name of our hero. Otherwise assume it's the enemy.
//TODO check who *really* is source of bonus
std::string heroName = myHero->hasArt(artID) ? myHero->name : owner.enemyHero().name;
std::string heroName = myHero->hasArt(artID) ? myHero->getNameTranslated() : owner.enemyHero().name;
//%s wields the %s, an ancient artifact which creates a p dead to all magic.
LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[683])
% heroName % CGI->artifacts()->getByIndex(artID)->getName()));
% heroName % CGI->artifacts()->getByIndex(artID)->getNameTranslated()));
}
}
}

View File

@ -15,6 +15,7 @@
#include "../gui/Canvas.h"
#include "../gui/ColorFilter.h"
#include "../gui/SDL_Extensions.h"
static const SDL_Color creatureBlueBorder = { 0, 255, 255, 255 };
static const SDL_Color creatureGoldBorder = { 255, 255, 0, 255 };

View File

@ -94,8 +94,8 @@ public:
// Keep the original palette, in order to do color switching operation
void savePalette();
void draw(SDL_Surface * where, int posX=0, int posY=0, const Rect *src=nullptr, ui8 alpha=255) const override;
void draw(SDL_Surface * where, const SDL_Rect * dest, const SDL_Rect * src, ui8 alpha=255) const override;
void draw(SDL_Surface * where, int posX=0, int posY=0, const Rect *src=nullptr) const override;
void draw(SDL_Surface * where, const Rect * dest, const Rect * src) const override;
std::shared_ptr<IImage> scaleFast(float scale) const override;
void exportBitmap(const boost::filesystem::path & path) const override;
void playerColored(PlayerColor player) override;
@ -642,17 +642,16 @@ SDLImage::SDLImage(std::string filename)
}
}
void SDLImage::draw(SDL_Surface *where, int posX, int posY, const Rect *src, ui8 alpha) const
void SDLImage::draw(SDL_Surface *where, int posX, int posY, const Rect *src) const
{
if(!surf)
return;
Rect destRect(posX, posY, surf->w, surf->h);
draw(where, &destRect, src);
}
void SDLImage::draw(SDL_Surface* where, const SDL_Rect* dest, const SDL_Rect* src, ui8 alpha) const
void SDLImage::draw(SDL_Surface* where, const Rect * dest, const Rect* src) const
{
if (!surf)
return;
@ -669,28 +668,23 @@ void SDLImage::draw(SDL_Surface* where, const SDL_Rect* dest, const SDL_Rect* sr
if(src->y < margins.y)
destShift.y += margins.y - src->y;
sourceRect = Rect(*src) & Rect(margins.x, margins.y, surf->w, surf->h);
sourceRect = Rect(*src).intersect(Rect(margins.x, margins.y, surf->w, surf->h));
sourceRect -= margins;
}
else
destShift = margins;
Rect destRect(destShift.x, destShift.y, surf->w, surf->h);
if(dest)
{
destRect.x += dest->x;
destRect.y += dest->y;
}
destShift += dest->topLeft();
if(surf->format->BitsPerPixel == 8)
{
CSDL_Ext::blit8bppAlphaTo24bpp(surf, &sourceRect, where, &destRect);
CSDL_Ext::blit8bppAlphaTo24bpp(surf, sourceRect, where, destShift);
}
else
{
SDL_UpperBlit(surf, &sourceRect, where, &destRect);
CSDL_Ext::blitSurface(surf, sourceRect, where, destShift);
}
}
@ -788,7 +782,7 @@ void SDLImage::shiftPalette(int from, int howMany)
{
palette[(i+1)%howMany] = surf->format->palette->colors[from + i];
}
SDL_SetColors(surf, palette, from, howMany);
CSDL_Ext::setColors(surf, palette, from, howMany);
}
}
@ -828,7 +822,7 @@ void SDLImage::setSpecialPallete(const IImage::SpecialPalette & SpecialPalette)
{
if(surf->format->palette)
{
SDL_SetColors(surf, const_cast<SDL_Color *>(SpecialPalette.data()), 1, 7);
CSDL_Ext::setColors(surf, const_cast<SDL_Color *>(SpecialPalette.data()), 1, 7);
}
}
@ -1274,7 +1268,7 @@ void CFadeAnimation::init(EMode mode, SDL_Surface * sourceSurface, bool freeSurf
shouldFreeSurface = freeSurfaceAtEnd;
}
void CFadeAnimation::draw(SDL_Surface * targetSurface, const SDL_Rect * sourceRect, SDL_Rect * destRect)
void CFadeAnimation::draw(SDL_Surface * targetSurface, const Point &targetPoint)
{
if (!fading || !fadingSurface || fadingMode == EMode::NONE)
{
@ -1283,6 +1277,6 @@ void CFadeAnimation::draw(SDL_Surface * targetSurface, const SDL_Rect * sourceRe
}
CSDL_Ext::setAlpha(fadingSurface, (int)(fadingCounter * 255));
SDL_BlitSurface(fadingSurface, const_cast<SDL_Rect *>(sourceRect), targetSurface, destRect); //FIXME
CSDL_Ext::blitSurface(fadingSurface, targetSurface, targetPoint); //FIXME
CSDL_Ext::setAlpha(fadingSurface, 255);
}

View File

@ -10,7 +10,6 @@
#pragma once
#include "../../lib/vcmi_endian.h"
#include "Geometries.h"
#include "../../lib/GameConstants.h"
#ifdef IN
@ -24,10 +23,13 @@
VCMI_LIB_NAMESPACE_BEGIN
class JsonNode;
class Rect;
class Point;
VCMI_LIB_NAMESPACE_END
struct SDL_Surface;
struct SDL_Color;
class CDefFile;
class ColorFilter;
@ -40,8 +42,8 @@ public:
using SpecialPalette = std::array<SDL_Color, 7>;
//draws image on surface "where" at position
virtual void draw(SDL_Surface * where, int posX = 0, int posY = 0, const Rect * src = nullptr, ui8 alpha = 255) const=0;
virtual void draw(SDL_Surface * where, const SDL_Rect * dest, const SDL_Rect * src, ui8 alpha = 255) const = 0;
virtual void draw(SDL_Surface * where, int posX = 0, int posY = 0, const Rect * src = nullptr) const = 0;
virtual void draw(SDL_Surface * where, const Rect * dest, const Rect * src) const = 0;
virtual std::shared_ptr<IImage> scaleFast(float scale) const = 0;
@ -177,6 +179,6 @@ public:
~CFadeAnimation();
void init(EMode mode, SDL_Surface * sourceSurface, bool freeSurfaceAtEnd = false, float animDelta = DEFAULT_DELTA);
void update();
void draw(SDL_Surface * targetSurface, const SDL_Rect * sourceRect, SDL_Rect * destRect);
void draw(SDL_Surface * targetSurface, const Point & targetPoint);
bool isFading() const { return fading; }
};

View File

@ -1,317 +0,0 @@
/*
* CCursorHandler.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "CCursorHandler.h"
#include <SDL.h>
#include "SDL_Extensions.h"
#include "CGuiHandler.h"
#include "../widgets/Images.h"
#include "../CMT.h"
void CCursorHandler::clearBuffer()
{
Uint32 fillColor = SDL_MapRGBA(buffer->format, 0, 0, 0, 0);
CSDL_Ext::fillRect(buffer, nullptr, fillColor);
}
void CCursorHandler::updateBuffer(CIntObject * payload)
{
payload->moveTo(Point(0,0));
payload->showAll(buffer);
needUpdate = true;
}
void CCursorHandler::replaceBuffer(CIntObject * payload)
{
clearBuffer();
updateBuffer(payload);
}
CCursorHandler::CCursorHandler()
: needUpdate(true)
, buffer(nullptr)
, cursorLayer(nullptr)
, frameTime(0.f)
, showing(false)
{
cursorLayer = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 40, 40);
SDL_SetTextureBlendMode(cursorLayer, SDL_BLENDMODE_BLEND);
xpos = ypos = 0;
type = Cursor::Type::DEFAULT;
dndObject = nullptr;
cursors =
{
std::make_unique<CAnimImage>("CRADVNTR", 0),
std::make_unique<CAnimImage>("CRCOMBAT", 0),
std::make_unique<CAnimImage>("CRDEFLT", 0),
std::make_unique<CAnimImage>("CRSPELL", 0)
};
currentCursor = cursors.at(static_cast<size_t>(Cursor::Type::DEFAULT)).get();
buffer = CSDL_Ext::newSurface(40,40);
SDL_SetSurfaceBlendMode(buffer, SDL_BLENDMODE_NONE);
SDL_ShowCursor(SDL_DISABLE);
set(Cursor::Map::POINTER);
}
Point CCursorHandler::position() const
{
return Point(xpos, ypos);
}
void CCursorHandler::changeGraphic(Cursor::Type type, size_t index)
{
assert(dndObject == nullptr);
if(type != this->type)
{
this->type = type;
this->frame = index;
currentCursor = cursors.at(static_cast<size_t>(type)).get();
currentCursor->setFrame(index);
}
else if(index != this->frame)
{
this->frame = index;
currentCursor->setFrame(index);
}
replaceBuffer(currentCursor);
}
void CCursorHandler::set(Cursor::Default index)
{
changeGraphic(Cursor::Type::DEFAULT, static_cast<size_t>(index));
}
void CCursorHandler::set(Cursor::Map index)
{
changeGraphic(Cursor::Type::ADVENTURE, static_cast<size_t>(index));
}
void CCursorHandler::set(Cursor::Combat index)
{
changeGraphic(Cursor::Type::COMBAT, static_cast<size_t>(index));
}
void CCursorHandler::set(Cursor::Spellcast index)
{
//Note: this is animated cursor, ignore specified frame and only change type
changeGraphic(Cursor::Type::SPELLBOOK, frame);
}
void CCursorHandler::dragAndDropCursor(std::unique_ptr<CAnimImage> object)
{
dndObject = std::move(object);
if(dndObject)
replaceBuffer(dndObject.get());
else
replaceBuffer(currentCursor);
}
void CCursorHandler::cursorMove(const int & x, const int & y)
{
xpos = x;
ypos = y;
}
void CCursorHandler::shiftPos( int &x, int &y )
{
if(( type == Cursor::Type::COMBAT && frame != static_cast<size_t>(Cursor::Combat::POINTER)) || type == Cursor::Type::SPELLBOOK)
{
x-=16;
y-=16;
// Properly align the melee attack cursors.
if (type == Cursor::Type::COMBAT)
{
switch (static_cast<Cursor::Combat>(frame))
{
case Cursor::Combat::HIT_NORTHEAST:
x -= 6;
y += 16;
break;
case Cursor::Combat::HIT_EAST:
x -= 16;
y += 10;
break;
case Cursor::Combat::HIT_SOUTHEAST:
x -= 6;
y -= 6;
break;
case Cursor::Combat::HIT_SOUTHWEST:
x += 16;
y -= 6;
break;
case Cursor::Combat::HIT_WEST:
x += 16;
y += 11;
break;
case Cursor::Combat::HIT_NORTHWEST:
x += 16;
y += 16;
break;
case Cursor::Combat::HIT_NORTH:
x += 9;
y += 16;
break;
case Cursor::Combat::HIT_SOUTH:
x += 9;
y -= 15;
break;
}
}
}
else if(type == Cursor::Type::ADVENTURE)
{
if (frame == 0)
{
//no-op
}
else if(frame == 2)
{
x -= 12;
y -= 10;
}
else if(frame == 3)
{
x -= 12;
y -= 12;
}
else if(frame < 27)
{
int hlpNum = (frame - 4)%6;
if(hlpNum == 0)
{
x -= 15;
y -= 13;
}
else if(hlpNum == 1)
{
x -= 13;
y -= 13;
}
else if(hlpNum == 2)
{
x -= 20;
y -= 20;
}
else if(hlpNum == 3)
{
x -= 13;
y -= 16;
}
else if(hlpNum == 4)
{
x -= 8;
y -= 9;
}
else if(hlpNum == 5)
{
x -= 14;
y -= 16;
}
}
else if(frame == 41)
{
x -= 14;
y -= 16;
}
else if(frame < 31 || frame == 42)
{
x -= 20;
y -= 20;
}
}
}
void CCursorHandler::centerCursor()
{
this->xpos = static_cast<int>((screen->w / 2.) - (currentCursor->pos.w / 2.));
this->ypos = static_cast<int>((screen->h / 2.) - (currentCursor->pos.h / 2.));
SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
SDL_WarpMouse(this->xpos, this->ypos);
SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
}
void CCursorHandler::render()
{
if(!showing)
return;
if (type == Cursor::Type::SPELLBOOK)
{
static const float frameDisplayDuration = 0.1f;
frameTime += GH.mainFPSmng->getElapsedMilliseconds() / 1000.f;
size_t newFrame = frame;
while (frameTime > frameDisplayDuration)
{
frameTime -= frameDisplayDuration;
newFrame++;
}
auto & animation = cursors.at(static_cast<size_t>(type));
while (newFrame > animation->size())
newFrame -= animation->size();
changeGraphic(Cursor::Type::SPELLBOOK, newFrame);
}
//the must update texture in the main (renderer) thread, but changes to cursor type may come from other threads
updateTexture();
int x = xpos;
int y = ypos;
shiftPos(x, y);
if(dndObject)
{
x -= dndObject->pos.w/2;
y -= dndObject->pos.h/2;
}
SDL_Rect destRect;
destRect.x = x;
destRect.y = y;
destRect.w = 40;
destRect.h = 40;
SDL_RenderCopy(mainRenderer, cursorLayer, nullptr, &destRect);
}
void CCursorHandler::updateTexture()
{
if(needUpdate)
{
SDL_UpdateTexture(cursorLayer, nullptr, buffer->pixels, buffer->pitch);
needUpdate = false;
}
}
CCursorHandler::~CCursorHandler()
{
if(buffer)
SDL_FreeSurface(buffer);
if(cursorLayer)
SDL_DestroyTexture(cursorLayer);
}

View File

@ -11,10 +11,11 @@
#include "CGuiHandler.h"
#include "../lib/CondSh.h"
#include <SDL.h>
#include <SDL_timer.h>
#include "CIntObject.h"
#include "CCursorHandler.h"
#include "CursorHandler.h"
#include "SDL_Extensions.h"
#include "../CGameInfo.h"
#include "../../lib/CThreadHelper.h"
@ -167,7 +168,7 @@ void CGuiHandler::totalRedraw()
#endif
for(auto & elem : objsToBlit)
elem->showAll(screen2);
blitAt(screen2,0,0,screen);
CSDL_Ext::blitAt(screen2,0,0,screen);
}
void CGuiHandler::updateTime()
@ -289,7 +290,7 @@ void CGuiHandler::handleCurrentEvent()
for(auto i = hlp.begin(); i != hlp.end() && continueEventHandling; i++)
{
if(!vstd::contains(doubleClickInterested, *i)) continue;
if(isItIn(&(*i)->pos, current->motion.x, current->motion.y))
if((*i)->pos.isInside(current->motion.x, current->motion.y))
{
(*i)->onDoubleClick();
}
@ -321,7 +322,7 @@ void CGuiHandler::handleCurrentEvent()
// SDL doesn't have the proper values for mouse positions on SDL_MOUSEWHEEL, refetch them
int x = 0, y = 0;
SDL_GetMouseState(&x, &y);
(*i)->wheelScrolled(current->wheel.y < 0, isItIn(&(*i)->pos, x, y));
(*i)->wheelScrolled(current->wheel.y < 0, (*i)->pos.isInside(x, y));
}
}
else if(current->type == SDL_TEXTINPUT)
@ -367,7 +368,7 @@ void CGuiHandler::handleMouseButtonClick(CIntObjectList & interestedObjs, EIntOb
auto prev = (*i)->mouseState(btn);
if(!isPressed)
(*i)->updateMouseState(btn, isPressed);
if(isItIn(&(*i)->pos, current->motion.x, current->motion.y))
if((*i)->pos.isInside(current->motion.x, current->motion.y))
{
if(isPressed)
(*i)->updateMouseState(btn, isPressed);
@ -384,7 +385,7 @@ void CGuiHandler::handleMouseMotion()
std::vector<CIntObject*> hlp;
for(auto & elem : hoverable)
{
if(isItIn(&(elem)->pos, current->motion.x, current->motion.y))
if(elem->pos.isInside(current->motion.x, current->motion.y))
{
if (!(elem)->hovered)
hlp.push_back((elem));
@ -408,7 +409,7 @@ void CGuiHandler::simpleRedraw()
{
//update only top interface and draw background
if(objsToBlit.size() > 1)
blitAt(screen2,0,0,screen); //blit background
CSDL_Ext::blitAt(screen2,0,0,screen); //blit background
if(!objsToBlit.empty())
objsToBlit.back()->show(screen); //blit active interface/window
}
@ -419,7 +420,7 @@ void CGuiHandler::handleMoveInterested(const SDL_MouseMotionEvent & motion)
std::list<CIntObject*> miCopy = motioninterested;
for(auto & elem : miCopy)
{
if(elem->strongInterest || isItInOrLowerBounds(&elem->pos, motion.x, motion.y)) //checking lower bounds fixes bug #2476
if(elem->strongInterest || Rect::createAround(elem->pos, 1).isInside( motion.x, motion.y)) //checking bounds including border fixes bug #2476
{
(elem)->mouseMoved(motion);
}

View File

@ -9,9 +9,9 @@
*/
#pragma once
//#include "../../lib/CStopWatch.h"
#include "Geometries.h"
#include "SDL_Extensions.h"
#include "../../lib/Point.h"
#include <SDL_events.h>
VCMI_LIB_NAMESPACE_BEGIN
@ -19,6 +19,8 @@ template <typename T> struct CondSh;
VCMI_LIB_NAMESPACE_END
union SDL_Event;
class CFramerateManager;
class IStatusBar;
class CIntObject;

View File

@ -14,6 +14,8 @@
#include "SDL_Extensions.h"
#include "../CMessage.h"
#include <SDL_pixels.h>
IShowActivatable::IShowActivatable()
{
type = 0;
@ -176,7 +178,7 @@ void CIntObject::printAtMiddleLoc(const std::string & text, const Point &p, EFon
void CIntObject::blitAtLoc( SDL_Surface * src, int x, int y, SDL_Surface * dst )
{
blitAt(src, pos.x + x, pos.y + y, dst);
CSDL_Ext::blitAt(src, pos.x + x, pos.y + y, dst);
}
void CIntObject::blitAtLoc(SDL_Surface * src, const Point &p, SDL_Surface * dst)
@ -219,16 +221,6 @@ void CIntObject::enable()
recActions = 255;
}
bool CIntObject::isItInLoc( const SDL_Rect &rect, int x, int y )
{
return isItIn(&rect, x - pos.x, y - pos.y);
}
bool CIntObject::isItInLoc( const SDL_Rect &rect, const Point &p )
{
return isItIn(&rect, p.x - pos.x, p.y - pos.y);
}
void CIntObject::fitToScreen(int borderWidth, bool propagate)
{
Point newPos = pos.topLeft();

View File

@ -9,8 +9,7 @@
*/
#pragma once
#include <SDL_events.h>
#include "Geometries.h"
#include "../../lib/Rect.h"
#include "../Graphics.h"
struct SDL_Surface;
@ -18,6 +17,9 @@ class CGuiHandler;
class CPicture;
struct SDL_KeyboardEvent;
struct SDL_TextInputEvent;
struct SDL_TextEditingEvent;
struct SDL_MouseMotionEvent;
using boost::logic::tribool;
@ -165,8 +167,6 @@ public:
//request complete redraw of this object
void redraw() override;
bool isItInLoc(const SDL_Rect &rect, int x, int y);
bool isItInLoc(const SDL_Rect &rect, const Point &p);
const Rect & center(const Rect &r, bool propagate = true); //sets pos so that r will be in the center of screen, assigns sizes of r to pos, returns new position
const Rect & center(const Point &p, bool propagate = true); //moves object so that point p will be in its center
const Rect & center(bool propagate = true); //centers when pos.w and pos.h are set, returns new position

View File

@ -11,11 +11,12 @@
#include "Canvas.h"
#include "SDL_Extensions.h"
#include "Geometries.h"
#include "CAnimation.h"
#include "../Graphics.h"
#include <SDL_surface.h>
Canvas::Canvas(SDL_Surface * surface):
surface(surface),
renderOffset(0,0)
@ -34,10 +35,10 @@ Canvas::Canvas(Canvas & other, const Rect & newClipRect):
Canvas(other)
{
clipRect.emplace();
SDL_GetClipRect(surface, clipRect.get_ptr());
CSDL_Ext::getClipRect(surface, clipRect.get());
Rect currClipRect = newClipRect + renderOffset;
SDL_SetClipRect(surface, &currClipRect);
CSDL_Ext::setClipRect(surface, currClipRect);
renderOffset += newClipRect.topLeft();
}
@ -51,7 +52,7 @@ Canvas::Canvas(const Point & size):
Canvas::~Canvas()
{
if (clipRect)
SDL_SetClipRect(surface, clipRect.get_ptr());
CSDL_Ext::setClipRect(surface, clipRect.get());
SDL_FreeSurface(surface);
}
@ -70,16 +71,9 @@ void Canvas::draw(std::shared_ptr<IImage> image, const Point & pos, const Rect &
image->draw(surface, renderOffset.x + pos.x, renderOffset.y + pos.y, &sourceRect);
}
void Canvas::draw(std::shared_ptr<IImage> image, const Point & pos, const Rect & sourceRect, uint8_t alpha)
{
assert(image);
if (image)
image->draw(surface, renderOffset.x + pos.x, renderOffset.y + pos.y, &sourceRect, alpha);
}
void Canvas::draw(Canvas & image, const Point & pos)
{
blitAt(image.surface, renderOffset.x + pos.x, renderOffset.y + pos.y, surface);
CSDL_Ext::blitAt(image.surface, renderOffset.x + pos.x, renderOffset.y + pos.y, surface);
}
void Canvas::drawLine(const Point & from, const Point & dest, const SDL_Color & colorFrom, const SDL_Color & colorDest)

View File

@ -9,7 +9,8 @@
*/
#pragma once
#include "Geometries.h"
#include "TextAlignment.h"
#include "../../lib/Rect.h"
struct SDL_Color;
struct SDL_Surface;
@ -51,10 +52,6 @@ public:
/// renders section of image bounded by sourceRect at specified position
void draw(std::shared_ptr<IImage> image, const Point & pos, const Rect & sourceRect);
/// renders section of image bounded by sourceRect at specified position at specific transparency value
void draw(std::shared_ptr<IImage> image, const Point & pos, const Rect & sourceRect, uint8_t alpha);
/// renders another canvas onto this canvas
void draw(Canvas & image, const Point & pos);

View File

@ -10,7 +10,7 @@
#include "StdInc.h"
#include "ColorFilter.h"
#include <SDL2/SDL_pixels.h>
#include <SDL_pixels.h>
#include "../../lib/JsonNode.h"

View File

@ -0,0 +1,439 @@
/*
* CCursorHandler.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "CursorHandler.h"
#include "SDL_Extensions.h"
#include "CGuiHandler.h"
#include "CAnimation.h"
#include "../../lib/CConfigHandler.h"
std::unique_ptr<ICursor> CursorHandler::createCursor()
{
if (settings["video"]["cursor"].String() == "auto")
{
#if defined(VCMI_ANDROID) || defined(VCMI_IOS)
return std::make_unique<CursorSoftware>();
#else
return std::make_unique<CursorHardware>();
#endif
}
if (settings["video"]["cursor"].String() == "hardware")
return std::make_unique<CursorHardware>();
assert(settings["video"]["cursor"].String() == "software");
return std::make_unique<CursorSoftware>();
}
CursorHandler::CursorHandler()
: cursor(createCursor())
, frameTime(0.f)
, showing(false)
, pos(0,0)
{
type = Cursor::Type::DEFAULT;
dndObject = nullptr;
cursors =
{
std::make_unique<CAnimation>("CRADVNTR"),
std::make_unique<CAnimation>("CRCOMBAT"),
std::make_unique<CAnimation>("CRDEFLT"),
std::make_unique<CAnimation>("CRSPELL")
};
for (auto & cursor : cursors)
cursor->preload();
set(Cursor::Map::POINTER);
}
Point CursorHandler::position() const
{
return pos;
}
void CursorHandler::changeGraphic(Cursor::Type type, size_t index)
{
assert(dndObject == nullptr);
if (type == this->type && index == this->frame)
return;
this->type = type;
this->frame = index;
cursor->setImage(getCurrentImage(), getPivotOffset());
}
void CursorHandler::set(Cursor::Default index)
{
changeGraphic(Cursor::Type::DEFAULT, static_cast<size_t>(index));
}
void CursorHandler::set(Cursor::Map index)
{
changeGraphic(Cursor::Type::ADVENTURE, static_cast<size_t>(index));
}
void CursorHandler::set(Cursor::Combat index)
{
changeGraphic(Cursor::Type::COMBAT, static_cast<size_t>(index));
}
void CursorHandler::set(Cursor::Spellcast index)
{
//Note: this is animated cursor, ignore specified frame and only change type
changeGraphic(Cursor::Type::SPELLBOOK, frame);
}
void CursorHandler::dragAndDropCursor(std::shared_ptr<IImage> image)
{
dndObject = image;
cursor->setImage(getCurrentImage(), getPivotOffset());
}
void CursorHandler::dragAndDropCursor (std::string path, size_t index)
{
CAnimation anim(path);
anim.load(index);
dragAndDropCursor(anim.getImage(index));
}
void CursorHandler::cursorMove(const int & x, const int & y)
{
pos.x = x;
pos.y = y;
cursor->setCursorPosition(pos);
}
Point CursorHandler::getPivotOffsetDefault(size_t index)
{
return {0, 0};
}
Point CursorHandler::getPivotOffsetMap(size_t index)
{
static const std::array<Point, 43> offsets = {{
{ 0, 0}, // POINTER = 0,
{ 0, 0}, // HOURGLASS = 1,
{ 12, 10}, // HERO = 2,
{ 12, 12}, // TOWN = 3,
{ 15, 13}, // T1_MOVE = 4,
{ 13, 13}, // T1_ATTACK = 5,
{ 16, 32}, // T1_SAIL = 6,
{ 13, 20}, // T1_DISEMBARK = 7,
{ 8, 9}, // T1_EXCHANGE = 8,
{ 14, 16}, // T1_VISIT = 9,
{ 15, 13}, // T2_MOVE = 10,
{ 13, 13}, // T2_ATTACK = 11,
{ 16, 32}, // T2_SAIL = 12,
{ 13, 20}, // T2_DISEMBARK = 13,
{ 8, 9}, // T2_EXCHANGE = 14,
{ 14, 16}, // T2_VISIT = 15,
{ 15, 13}, // T3_MOVE = 16,
{ 13, 13}, // T3_ATTACK = 17,
{ 16, 32}, // T3_SAIL = 18,
{ 13, 20}, // T3_DISEMBARK = 19,
{ 8, 9}, // T3_EXCHANGE = 20,
{ 14, 16}, // T3_VISIT = 21,
{ 15, 13}, // T4_MOVE = 22,
{ 13, 13}, // T4_ATTACK = 23,
{ 16, 32}, // T4_SAIL = 24,
{ 13, 20}, // T4_DISEMBARK = 25,
{ 8, 9}, // T4_EXCHANGE = 26,
{ 14, 16}, // T4_VISIT = 27,
{ 16, 32}, // T1_SAIL_VISIT = 28,
{ 16, 32}, // T2_SAIL_VISIT = 29,
{ 16, 32}, // T3_SAIL_VISIT = 30,
{ 16, 32}, // T4_SAIL_VISIT = 31,
{ 6, 1}, // SCROLL_NORTH = 32,
{ 16, 2}, // SCROLL_NORTHEAST = 33,
{ 21, 6}, // SCROLL_EAST = 34,
{ 16, 16}, // SCROLL_SOUTHEAST = 35,
{ 6, 21}, // SCROLL_SOUTH = 36,
{ 1, 16}, // SCROLL_SOUTHWEST = 37,
{ 1, 5}, // SCROLL_WEST = 38,
{ 2, 1}, // SCROLL_NORTHWEST = 39,
{ 0, 0}, // POINTER_COPY = 40,
{ 14, 16}, // TELEPORT = 41,
{ 20, 20}, // SCUTTLE_BOAT = 42
}};
assert(offsets.size() == size_t(Cursor::Map::COUNT)); //Invalid number of pivot offsets for cursor
assert(index < offsets.size());
return offsets[index];
}
Point CursorHandler::getPivotOffsetCombat(size_t index)
{
static const std::array<Point, 20> offsets = {{
{ 12, 12 }, // BLOCKED = 0,
{ 10, 14 }, // MOVE = 1,
{ 14, 14 }, // FLY = 2,
{ 12, 12 }, // SHOOT = 3,
{ 12, 12 }, // HERO = 4,
{ 8, 12 }, // QUERY = 5,
{ 0, 0 }, // POINTER = 6,
{ 21, 0 }, // HIT_NORTHEAST = 7,
{ 31, 5 }, // HIT_EAST = 8,
{ 21, 21 }, // HIT_SOUTHEAST = 9,
{ 0, 21 }, // HIT_SOUTHWEST = 10,
{ 0, 5 }, // HIT_WEST = 11,
{ 0, 0 }, // HIT_NORTHWEST = 12,
{ 6, 0 }, // HIT_NORTH = 13,
{ 6, 31 }, // HIT_SOUTH = 14,
{ 14, 0 }, // SHOOT_PENALTY = 15,
{ 12, 12 }, // SHOOT_CATAPULT = 16,
{ 12, 12 }, // HEAL = 17,
{ 12, 12 }, // SACRIFICE = 18,
{ 14, 20 }, // TELEPORT = 19
}};
assert(offsets.size() == size_t(Cursor::Combat::COUNT)); //Invalid number of pivot offsets for cursor
assert(index < offsets.size());
return offsets[index];
}
Point CursorHandler::getPivotOffsetSpellcast()
{
return { 18, 28};
}
Point CursorHandler::getPivotOffset()
{
if (dndObject)
return dndObject->dimensions() / 2;
switch (type) {
case Cursor::Type::ADVENTURE: return getPivotOffsetMap(frame);
case Cursor::Type::COMBAT: return getPivotOffsetCombat(frame);
case Cursor::Type::DEFAULT: return getPivotOffsetDefault(frame);
case Cursor::Type::SPELLBOOK: return getPivotOffsetSpellcast();
};
assert(0);
return {0, 0};
}
std::shared_ptr<IImage> CursorHandler::getCurrentImage()
{
if (dndObject)
return dndObject;
return cursors[static_cast<size_t>(type)]->getImage(frame);
}
void CursorHandler::centerCursor()
{
Point screenSize {screen->w, screen->h};
pos = screenSize / 2 - getPivotOffset();
SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
CSDL_Ext::warpMouse(pos.x, pos.y);
SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
cursor->setCursorPosition(pos);
}
void CursorHandler::updateSpellcastCursor()
{
static const float frameDisplayDuration = 0.1f;
frameTime += GH.mainFPSmng->getElapsedMilliseconds() / 1000.f;
size_t newFrame = frame;
while (frameTime >= frameDisplayDuration)
{
frameTime -= frameDisplayDuration;
newFrame++;
}
auto & animation = cursors.at(static_cast<size_t>(type));
while (newFrame >= animation->size())
newFrame -= animation->size();
changeGraphic(Cursor::Type::SPELLBOOK, newFrame);
}
void CursorHandler::render()
{
if(!showing)
return;
if (type == Cursor::Type::SPELLBOOK)
updateSpellcastCursor();
cursor->render();
}
void CursorHandler::hide()
{
if (!showing)
return;
showing = false;
cursor->setVisible(false);
}
void CursorHandler::show()
{
if (showing)
return;
showing = true;
cursor->setVisible(true);
}
void CursorSoftware::render()
{
//texture must be updated in the main (renderer) thread, but changes to cursor type may come from other threads
if (needUpdate)
updateTexture();
Point renderPos = pos - pivot;
SDL_Rect destRect;
destRect.x = renderPos.x;
destRect.y = renderPos.y;
destRect.w = 40;
destRect.h = 40;
SDL_RenderCopy(mainRenderer, cursorTexture, nullptr, &destRect);
}
void CursorSoftware::createTexture(const Point & dimensions)
{
if(cursorTexture)
SDL_DestroyTexture(cursorTexture);
if (cursorSurface)
SDL_FreeSurface(cursorSurface);
cursorSurface = CSDL_Ext::newSurface(dimensions.x, dimensions.y);
cursorTexture = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, dimensions.x, dimensions.y);
SDL_SetSurfaceBlendMode(cursorSurface, SDL_BLENDMODE_NONE);
SDL_SetTextureBlendMode(cursorTexture, SDL_BLENDMODE_BLEND);
}
void CursorSoftware::updateTexture()
{
Point dimensions(-1, -1);
if (!cursorSurface || Point(cursorSurface->w, cursorSurface->h) != cursorImage->dimensions())
createTexture(cursorImage->dimensions());
CSDL_Ext::fillSurface(cursorSurface, Colors::TRANSPARENCY);
cursorImage->draw(cursorSurface);
SDL_UpdateTexture(cursorTexture, NULL, cursorSurface->pixels, cursorSurface->pitch);
needUpdate = false;
}
void CursorSoftware::setImage(std::shared_ptr<IImage> image, const Point & pivotOffset)
{
assert(image != nullptr);
cursorImage = image;
pivot = pivotOffset;
needUpdate = true;
}
void CursorSoftware::setCursorPosition( const Point & newPos )
{
pos = newPos;
}
void CursorSoftware::setVisible(bool on)
{
visible = on;
}
CursorSoftware::CursorSoftware():
cursorTexture(nullptr),
cursorSurface(nullptr),
needUpdate(false),
visible(false),
pivot(0,0)
{
SDL_ShowCursor(SDL_DISABLE);
}
CursorSoftware::~CursorSoftware()
{
if(cursorTexture)
SDL_DestroyTexture(cursorTexture);
if (cursorSurface)
SDL_FreeSurface(cursorSurface);
}
CursorHardware::CursorHardware():
cursor(nullptr)
{
SDL_ShowCursor(SDL_DISABLE);
}
CursorHardware::~CursorHardware()
{
if(cursor)
SDL_FreeCursor(cursor);
}
void CursorHardware::setVisible(bool on)
{
if (on)
SDL_ShowCursor(SDL_ENABLE);
else
SDL_ShowCursor(SDL_DISABLE);
}
void CursorHardware::setImage(std::shared_ptr<IImage> image, const Point & pivotOffset)
{
auto cursorSurface = CSDL_Ext::newSurface(image->dimensions().x, image->dimensions().y);
CSDL_Ext::fillSurface(cursorSurface, Colors::TRANSPARENCY);
image->draw(cursorSurface);
auto oldCursor = cursor;
cursor = SDL_CreateColorCursor(cursorSurface, pivotOffset.x, pivotOffset.y);
if (!cursor)
logGlobal->error("Failed to set cursor! SDL says %s", SDL_GetError());
SDL_FreeSurface(cursorSurface);
SDL_SetCursor(cursor);
if (oldCursor)
SDL_FreeCursor(oldCursor);
}
void CursorHardware::setCursorPosition( const Point & newPos )
{
//no-op
}
void CursorHardware::render()
{
//no-op
}

View File

@ -8,11 +8,14 @@
*
*/
#pragma once
class CIntObject;
class CAnimImage;
class CAnimation;
class IImage;
struct SDL_Surface;
struct SDL_Texture;
struct Point;
struct SDL_Cursor;
#include "../../lib/Point.h"
namespace Cursor
{
@ -51,7 +54,9 @@ namespace Cursor
SHOOT_CATAPULT = 16,
HEAL = 17,
SACRIFICE = 18,
TELEPORT = 19
TELEPORT = 19,
COUNT
};
enum class Map {
@ -97,7 +102,9 @@ namespace Cursor
SCROLL_NORTHWEST = 39,
//POINTER_COPY = 40, // probably unused
TELEPORT = 41,
SCUTTLE_BOAT = 42
SCUTTLE_BOAT = 42,
COUNT
};
enum class Spellcast {
@ -105,49 +112,96 @@ namespace Cursor
};
}
/// handles mouse cursor
class CCursorHandler final
class ICursor
{
public:
virtual ~ICursor() = default;
virtual void setImage(std::shared_ptr<IImage> image, const Point & pivotOffset) = 0;
virtual void setCursorPosition( const Point & newPos ) = 0;
virtual void render() = 0;
virtual void setVisible( bool on) = 0;
};
class CursorHardware : public ICursor
{
std::shared_ptr<IImage> cursorImage;
SDL_Cursor * cursor;
public:
CursorHardware();
~CursorHardware();
void setImage(std::shared_ptr<IImage> image, const Point & pivotOffset) override;
void setCursorPosition( const Point & newPos ) override;
void render() override;
void setVisible( bool on) override;
};
class CursorSoftware : public ICursor
{
std::shared_ptr<IImage> cursorImage;
SDL_Texture * cursorTexture;
SDL_Surface * cursorSurface;
Point pos;
Point pivot;
bool needUpdate;
SDL_Texture * cursorLayer;
bool visible;
SDL_Surface * buffer;
CAnimImage * currentCursor;
void createTexture(const Point & dimensions);
void updateTexture();
public:
CursorSoftware();
~CursorSoftware();
std::unique_ptr<CAnimImage> dndObject; //if set, overrides currentCursor
void setImage(std::shared_ptr<IImage> image, const Point & pivotOffset) override;
void setCursorPosition( const Point & newPos ) override;
void render() override;
void setVisible( bool on) override;
};
std::array<std::unique_ptr<CAnimImage>, 4> cursors;
/// handles mouse cursor
class CursorHandler final
{
std::shared_ptr<IImage> dndObject; //if set, overrides currentCursor
std::array<std::unique_ptr<CAnimation>, 4> cursors;
bool showing;
void clearBuffer();
void updateBuffer(CIntObject * payload);
void replaceBuffer(CIntObject * payload);
void shiftPos( int &x, int &y );
void updateTexture();
/// Current cursor
Cursor::Type type;
size_t frame;
float frameTime;
Point pos;
void changeGraphic(Cursor::Type type, size_t index);
/// position of cursor
int xpos, ypos;
Point getPivotOffsetDefault(size_t index);
Point getPivotOffsetMap(size_t index);
Point getPivotOffsetCombat(size_t index);
Point getPivotOffsetSpellcast();
Point getPivotOffset();
void updateSpellcastCursor();
std::shared_ptr<IImage> getCurrentImage();
std::unique_ptr<ICursor> cursor;
static std::unique_ptr<ICursor> createCursor();
public:
CCursorHandler();
~CCursorHandler();
CursorHandler();
~CursorHandler();
/**
* Replaces the cursor with a custom image.
*
* @param image Image to replace cursor with or nullptr to use the normal
* cursor. CursorHandler takes ownership of object
*/
void dragAndDropCursor (std::unique_ptr<CAnimImage> image);
/// Replaces the cursor with a custom image.
/// @param image Image to replace cursor with or nullptr to use the normal cursor.
void dragAndDropCursor(std::shared_ptr<IImage> image);
void dragAndDropCursor(std::string path, size_t index);
/// Returns current position of the cursor
Point position() const;
@ -172,8 +226,8 @@ public:
void render();
void hide() { showing=false; };
void show() { showing=true; };
void hide();
void show();
/// change cursor's positions to (x, y)
void cursorMove(const int & x, const int & y);

View File

@ -130,11 +130,11 @@ size_t CBitmapFont::getGlyphWidth(const char * data) const
void CBitmapFont::renderCharacter(SDL_Surface * surface, const BitmapChar & character, const SDL_Color & color, int &posX, int &posY) const
{
Rect clipRect;
SDL_GetClipRect(surface, &clipRect);
CSDL_Ext::getClipRect(surface, clipRect);
posX += character.leftOffset;
TColorPutter colorPutter = CSDL_Ext::getPutterFor(surface, 0);
CSDL_Ext::TColorPutter colorPutter = CSDL_Ext::getPutterFor(surface, 0);
Uint8 bpp = surface->format->BytesPerPixel;
@ -270,7 +270,7 @@ void CTrueTypeFont::renderText(SDL_Surface * surface, const std::string & data,
if (color.r != 0 && color.g != 0 && color.b != 0) // not black - add shadow
{
SDL_Color black = { 0, 0, 0, SDL_ALPHA_OPAQUE};
renderText(surface, data, black, Point(pos.x + 1, pos.y + 1));
renderText(surface, data, black, pos + Point(1,1));
}
if (!data.empty())
@ -283,8 +283,7 @@ void CTrueTypeFont::renderText(SDL_Surface * surface, const std::string & data,
assert(rendered);
Rect rect(pos.x, pos.y, rendered->w, rendered->h);
SDL_BlitSurface(rendered, nullptr, surface, &rect);
CSDL_Ext::blitSurface(rendered, surface, pos);
SDL_FreeSurface(rendered);
}
}
@ -308,9 +307,9 @@ void CBitmapHanFont::renderCharacter(SDL_Surface * surface, int characterIndex,
{
//TODO: somewhat duplicated with CBitmapFont::renderCharacter();
Rect clipRect;
SDL_GetClipRect(surface, &clipRect);
CSDL_Ext::getClipRect(surface, clipRect);
TColorPutter colorPutter = CSDL_Ext::getPutterFor(surface, 0);
CSDL_Ext::TColorPutter colorPutter = CSDL_Ext::getPutterFor(surface, 0);
Uint8 bpp = surface->format->BytesPerPixel;
// start of line, may differ from 0 due to end of surface or clipped surface

View File

@ -12,10 +12,10 @@
VCMI_LIB_NAMESPACE_BEGIN
class JsonNode;
class Point;
VCMI_LIB_NAMESPACE_END
struct Point;
struct SDL_Surface;
struct SDL_Color;

Some files were not shown because too many files have changed in this diff Show More