Skip to content

Commit 9d944ad

Browse files
committed
projectile cleanup
1 parent b1b1454 commit 9d944ad

File tree

8 files changed

+120
-109
lines changed

8 files changed

+120
-109
lines changed

src/SUMMARY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
- [Hitboxes](./ships/sea-battles/hitboxes.md)
3737
- [Projectiles](./ships/sea-battles/projectiles.md)
3838
- [Range](./ships/sea-battles/projectiles/range.md)
39-
- [Damage](./ships/sea-battles/projectiles/damage.md)
39+
- [Impact](./ships/sea-battles/projectiles/impact.md)
4040
- [Reefs](./ships/sea-battles/reefs.md)
4141
- [Modloader](./modloader.md)
4242
- [Known Bugs](./bugs.md)

src/ships/sea-battles/damage.md

Lines changed: 0 additions & 1 deletion
This file was deleted.
File renamed without changes.
File renamed without changes.
File renamed without changes.

src/ships/sea-battles/projectiles.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,96 @@
33
## Ship Artillery Projectiles
44
The `sea_battle_local_map_ship_fire_volley` function at `0x0061E8EF` loops over the artillery slots of the selected side.
55
It restricts the target coordinates to be in the cone of fire, and calls `sea_battle_local_map_ship_fire_shot` at `0x006214CB` for every artillery piece.
6+
The code for all plots can be found in the source code of this book.
7+
8+
### Raw Damage
9+
The *raw damage* of all projectiles of a volley is controlled through 3 tables in the `sea_battle_local_map_ship_fire_volley` function: Damage Component 1 at `0x00672CC0`, Damage Component 2 at `0x00672CD0`, and Damage Reduction at `0x00672CE4`.
10+
The vanilla values are shown in the following table:
11+
12+
|Artillery Type|Damage 1|Damage 2|Damage Reduction|
13+
|-|-|-|-|
14+
|Small Catapult|32|60|480|
15+
|Small Ballista|32|80|160|
16+
|Large Catapult|77|60|480|
17+
|Large Ballista|77|80|160|
18+
|Bombard|96|90|120|
19+
|Cannon|58|90|120|
20+
21+
The formula of the raw damage is as follows:
22+
```python
23+
def calc_raw_damage(distance: int, artillery_type: int):
24+
return max(0, distance \
25+
* damage1[artillery_type] \
26+
* damage2[artillery_type] \
27+
// (-6 * reduction[artillery_type]) \
28+
+ \
29+
damage1[artillery_type] \
30+
* damage2[artillery_type])
31+
```
32+
The following image shows the plot of raw damage and distance, with the damage values of double slot weapons adjusted by `0.5`.
33+
![image](damage_raw.png)
34+
35+
While every individual projectile is subject to minor source and destination adjustments, the distance raw damage calculation is done once for the entire volley.
36+
37+
### Scaling
38+
The raw damage is scaled linearly in the `sea_battle_local_map_ship_fire_shot` function at `0x006214CB`:
39+
```python
40+
def calc_scaled_damage(raw_damage: int):
41+
return 2800 * (raw_damage // 64) // 100
42+
```
43+
44+
The precision loss caused by the division by 64 has a slight effect on the granularity:
45+
![image](damage_scaled.png)
46+
47+
While the function is called for every individual projectile, this calculation will yield the same values for every projectile of a volley.
48+
49+
### Captain
50+
Then the function uses the captain's combat experience (a value between `0` for a combat level `0` and `250` for a combat level `5` captain) to increase the damage:
51+
52+
```python
53+
def apply_captain_factor(scaled_damage: int, combat_experience: int):
54+
if not combat_experience:
55+
return scaled_damage
56+
else:
57+
return scaled_damage * (6 * combat_experience // 17 + 100) // 100
58+
```
59+
This factor is roughly (ignoring precision loss through divisions) equivalent to \\(\frac{3 * combat\\_experience}{850} + 1\\) or \\(0.17647058823 * combat\\_level + 1\\).
60+
The following figure highlights the impact of a captain on a projectile's damage:
61+
![image](damage_captain.png)
62+
63+
While the function is called for every individual projectile, this calculation will yield the same values for every projectile of a volley.
64+
65+
### Difficulty and Maintenance
66+
Then the function considers sea battle difficulty setting and the ship's current maintenance value to affect the damage as follows:
67+
```python
68+
def apply_difficulty_and_maintenance(
69+
damage: int, difficulty: int, ship_maintenance: int, is_ai: bool
70+
):
71+
f = min(4, max(ship_maintenance >> 8, 0))
72+
if not is_ai:
73+
match difficulty:
74+
case 0: # Easy
75+
f += 2
76+
case 2: # Hard
77+
f -= 2
78+
if f > 0:
79+
return damage + damage * (f - 2) // 20
80+
else:
81+
return damage + damage * (f - 1) // 20
82+
```
83+
Since `f` cannot exceed `6`, the bonus damage from difficulty and maintenance will not exceed \\(\frac{1}{5} * damage \\).
84+
85+
While the function is called for every individual projectile, this calculation will yield the same values for every projectile of a volley.
86+
87+
### Normal Distribution and Minimum
88+
Then the function applies a factor with a discrete uniform (assuming the sea battle's PRNG works as intended) distribution in the discrete (up to the second decimal point) interval from `0.85` to `1.15`, and enforces a minimum damage of `1`:
89+
```python
90+
# Discrete distribution
91+
damage = damage * (battle_rand() % 31 + 85) // 100
92+
93+
# Minimum
94+
damage = max(damage, 1)
95+
```
96+
97+
### Final Scaling
98+
Finally the `init_sea_battle_projectile` function at `0x00602A90` multiplies the damage by `1.5`, and stores the final damage in the projectile.

src/ships/sea-battles/projectiles/damage.md

Lines changed: 0 additions & 107 deletions
This file was deleted.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Impact
2+
Once the `get_sea_battle_projectile_impact_direction` at `0x0060A73C` has detected an impact, the projectile's damage is applied, and the object is freed.
3+
Projectiles can deal damage to a ship's HP, sailors and artillery.
4+
5+
## HP Damage
6+
The projectile's damage value is subtracted from its hitpoints, and the ship is killed if they drop to or below `0`.
7+
8+
## Sailor Damage
9+
Damage to sailors is calculated as follows:
10+
```python
11+
def calc_killed_sailors(
12+
sailors: int,
13+
damage: int,
14+
rng: int,
15+
pending_sailor_damage: int,
16+
ship_max_hp: int
17+
) -> Tuple[int, int]:
18+
sailor_damage_rng = (rng & 0x400) + 3072 # 3072 or 4096
19+
scaled_sailor_damage = (sailors + 1) * sailor_damage_rng
20+
final_sailor_damage = pending_sailor_damage + scaled_sailor_damage / (ship_max_hp // 16)
21+
killed_sailors = final_sailor_damage >> 16
22+
new_pending_sailor_damage = final_sailor_damage & 0xffff
23+
return killed_sailors, new_pending_sailor_damage
24+
```
25+
26+
## Ship Artillery Slot Damage

0 commit comments

Comments
 (0)