Units currently get extra range if they're on a hill, but it would be nice if they got a damage bonus as well. See #4028.

The question is, what shape should the bonus take?

Here's one way to think about it. The damage should be something like the kinetic energy of the projectile when it lands. Of course the real picture is messy, but we can think of energy being conserved so it's the same as the energy when we shoot the projectile, which is the kinetic energy mv^2/2 plus the potential energy mgh, where h is the elevation difference between ourselves and the target. Compared to the case with no elevation bonus (h = 0), this is a ratio of 1 + 2gh/v^2.

So the damage multiplier formula should look like 1 + k * h, where k = 2g/v^2. Here's some values of k for g = 9.81 and various projectile speeds:

v k unit 37.5 0.0140 catapult 62.5 0.0050 javelin, slinger 75 0.0035 archer, tower 150 0.0009 bolt shooter

We could have the bonus depend on the speed of the projectile (so catapults would get a huge elevation bonus and bolt shooters would get a very little one), or we could simplify the situation and choose one value of k for all ranged units. I prefer the second approach.

I did some testing and I think I like k = 0.01. This has a nice english meaning too: an elevation bonus of 10m corresponds to a damage bonus of 10%. We can always change it later if it turns out to be too much (or too little).

Playing on the "Empire" map for example, small hills were 10-20m high, and a 10-20% bonus there seems reasonable. Blacksmith upgrades are 20% damage each, for comparison. Cliffs and huge hills were 30-40m, but I think that's fine too. Note that the bonus becomes a penalty for ranged units that have targets above them, e.g. a unit 20m below their target has a damage multiplier of 0.8. (I added a minimum value, 0.1, otherwise mountains over 100m tall would break the formula.) So there's a double effect for ranged vs ranged battles.

Here's the units that have an elevation bonus.

template_structure_defense_defense_tower.xml: <ElevationBonus>15</ElevationBonus> template_structure_defense_sentry_tower.xml: <ElevationBonus>9.0</ElevationBonus> template_unit_mechanical_siege_tower.xml: <ElevationBonus>10</ElevationBonus>

To get the multiplier for them, we just add this height (maybe "ElevationBonus" is a bad name?) to the elevation difference before plugging it into the formula. Note that enemies attack the base of the towers, so the damage bonus is only in one direction.

However, for units standing on walls, the bonus goes both ways, since the units are actually ~10m higher (the exact number depends on the civ). (Units on walls also have an aura for +3 armor and +20 vision, and melee units can't attack them.)

The elevation difference should use the y-coordinate of the real target position, i.e. where the missile lands. But in the Attack.js code that's not important so it's approximated by the target's current y-coordinate. That's probably okay, but I'd like to use the correct value if anyone can tell me how to do that and if it's not a big performance cost.

To get the damage multplier, we multiply the elevation bonus by the attack bonus. The attack bonus seems to be for hard counters, which are pike/spears vs cav, hunted elephants vs cav, and mace hero alexander sword cav vs other heroes. That is, there's currently no ranged hard counters, so the attack bonus here should always be 1.

In the damage component, the multiplier's applied to missiles that hit the main target and missiles that miss but hit another unit, but not to splash damage. We want the elevation bonus to apply to splash damage as well, so I've added the multiplier there. In the future, if we add hard counters for ranged units, we should do the attack bonus differently, because it should depend on the unit that was actually hit (with splash damage or a missed missile) not the unit that was aimed at. But again, since the attack bonus is always 1, the code's not doing any harm right now.

In #4028, the idea is to then remove the range bonus for units. I'm ambivalent about this at the moment (haven't thought about it enough or gone through the calculations carefully), but anyway it should be done in a separate patch.