I'm a fairly new player, and I have spent some time playing by myself (attacking my own units) to get the hang of game mechanics and stuff. One thing I noticed was that if a unit fires a splash attack, then moves before the attack hits the target, the target will suffer less damage than usual.
Today I also noticed that archmages, when doing "hold position", always seem to do the damage of a direct hit to their targets, while units that were closer to the splash center than the target received less damage than the target.
Since I know some programming and this bug was starting to bother me, I decided to look at the source code.
The problem appears to be an incorrect splash center. If the attacking unit moves, the splash center, for the purpose of calculating damage, becomes the attacking unit's position. If the attacking unit targets another thing in its range, the splash center is the position of that other thing. The area of effect, however, is still centered on the attack impact position. If the attacker does not move, the splash center is the position of the targeted unit.
As a test, I put an archmage at range 4 from its target (another archmage), and fired an ice nova at the target. By reducing the game speed and pausing frequently, I was able to tell the attacking archmage to move as soon as it fired its attack. When the attack hit, the
attacking archmage had <100 health left (out of 450), and the target had >300.
What I believe is the offending code, in source/glest_game/world/unit_updater.cpp, starting at line 1900:
void UnitUpdater::hit(Unit *attacker, const AttackSkillType* ast, const Vec2i &targetPos, Field targetField){
//hit attack positions
if(ast->getSplash()){
PosCircularIterator pci(map, targetPos, ast->getSplashRadius());
while(pci.next()) {
Unit *attacked= map->getCell(pci.getPos())->getUnit(targetField);
if(attacked != NULL) {
if(ast->getSplashDamageAll()
|| attacker->isAlly(attacked) == false
|| ( targetPos.x==pci.getPos().x && targetPos.y==pci.getPos().y )) {
attacker->setLastAttackedUnitId(attacked->getId());
scriptManager->onUnitAttacking(attacker);
damage(attacker, ast, attacked, pci.getPos().dist(attacker->getTargetPos())); // BUG IS HERE
}
}
}
}
else{
Unit *attacked= map->getCell(targetPos)->getUnit(targetField);
if(attacked!=NULL){
damage(attacker, ast, attacked, 0.f);
}
}
}
Unit::getTargetPos() returns the target position. In source/glest_game/world/world.cpp, Unit::setTargetPos is called in World::setUnitPosition:
void World::setUnitPosition(int unitId, Vec2i pos) {
Unit* unit= findUnitById(unitId);
if(unit == NULL) {
throw megaglest_runtime_error("Can not find unit to set position unitId = " + intToStr(unitId));
}
unit->setTargetPos(pos);
//unit->setPos(pos,true);
this->moveUnitCells(unit);
}
I didn't look for where this function is called, but I am guessing it is called whenever a unit moves into a new cell. Basically, we can't use the attacker's getTargetPos() to determine the splash center.
But the area of effect center is still the projectile impact position, because it uses the function's targetPos argument:
PosCircularIterator pci(map, targetPos, ast->getSplashRadius());
Non-splash projectiles also use targetPos to determine impact position.
So replacing
attacker->getTargetPos() with
targetPos should fix the bug.
Also,
http://megaglest.org/linux-packages.html doesn't list Gentoo. Running
emerge megaglest as root will install MegaGlest on Gentoo.