Skip to content

[General Purpose] New trajectory system #1582

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 143 commits into
base: develop
Choose a base branch
from

Conversation

CrimRecya
Copy link
Contributor

@CrimRecya CrimRecya commented Mar 20, 2025


Projectile trajectories

  • Projectiles can now have customizable trajectories.

    抛射体现在可以有自定义的轨迹。

    • Trajectory should not be combined with original game's projectile trajectory logics (Arcing, ROT, Vertical or Inviso). Attempt to do so will result in the other logics being disabled and a warning being written to log file.

      Trajectory 不应与原游戏的抛射体逻辑(ArcingROTVerticalInviso)结合使用。尝试这样做将导致其他逻辑被禁用并在日志文件中写入一条警告。

    • Trajectory.Speed defines the speed of the projectile, which unlike Speed used by ROT > 0 projectiles is defined on projectile not weapon.

      Trajectory.Speed 定义抛射体速度,与 ROT > 0 抛射体不同的是 Trajectory.Speed 定义在抛射体上而不是武器上。

      • In Trajectory=Straight, it refers to the whole distance speed of the projectile.

        Trajectory=Straight 中,它指抛射体的全程速度。

      • In Trajectory=Bombard, it refers to the initial speed of the projectile.

        Trajectory=Bombard 中,它指抛射体的初始速度。

      • In Trajectory=Missile, it refers to the final speed of the projectile. Trajectory.Speed will be fixed at 192 by setting Trajectory.Missile.UniqueCurve=true.

        Trajectory=Missile 中,它指抛射体的最终速度。若设置了 Trajectory.Missile.UniqueCurve=true ,则 Trajectory.Speed 固定为 192 。

      • In Trajectory=Engrave, it refers to the horizontal engrave speed of the projectile and it cannot exceed 128. Recommend set as about 40.

        Trajectory=Engrave 中,它指抛射体的水平刻线速度,且不能超过 128 。建议设置为 40 左右。

      • In Trajectory=Parabola, it refers to the horizontal velocity of the projectile and is only used for modes Speed, SpeedAndHeight, or SpeedAndAngle.

        Trajectory=Parabola 中,它指抛射体的水平速度,且仅适用于 SpeedSpeedAndHeightSpeedAndAngle 模式。

      • In Trajectory=Tracing, it refers to the moving speed of the projectile.

        Trajectory=Tracing 中,它指抛射体的移动速度。

    • Trajectory.Duration controls the duration the projectile can exist, and at the end of the time, the projectile will detonate. If it is a non positive number, there will be no timing. The following are exceptions.

      Trajectory.Duration 控制抛射体的存在时限,超时后会引爆。非正值表示无时限。以下为特例。

      • In Trajectory=Engrave, if it is a non positive number, automatically use Trajectory.Engrave.SourceCoord and Trajectory.Engrave.TargetCoord to calculate the process duration. At this point, Trajectory.Engrave.TargetCoord can be regarded as the endpoint coordinates of the cutting line segment.

        Trajectory=Engrave 中,如果它为非正数,则会自动根据 Trajectory.Engrave.SourceCoordTrajectory.Engrave.TargetCoord 计算时长。在这种情况下,Trajectory.Engrave.TargetCoord 可被视作是切割线段的终点坐标。

      • In Trajectory=Tracing, if set to zero, use weapon's ROF-10 as the duration. At least 1 frame. If it is negative, do not time it.

        Trajectory=Tracing 中,如果它被设置为 0 ,则会用武器的 ROF - 10 作为时长,至少1帧。如果是负数则不计时。

    • Trajectory.TolerantTime controls how long the projectile will detonate after losing the target. If it is 0, it will detonate directly when switching targets.

      Trajectory.TolerantTime 控制丢失目标后抛射体的爆炸延迟。如果设置为 0 ,则会在更换目标时立刻爆炸。

    • Trajectory.CreateCapacity controls the capacity that this type of trajectory projectile can be fired. When it is set to a non negative number, the trajectory projectile can only be fired when number of this trajectory type fired by the firer on the map is less than this value, namely effective.

      Trajectory.CreateCapacity 控制能发射的同类型轨迹抛射体的最大数量。当它设置为非负数时,只有当发射者在地图上发射的这种轨迹抛射体类型的数量小于该值时,才能发射这种轨迹抛射体,即生效。

    • Trajectory.BulletROT controls the rotational speed of the projectile's orientation (facing direction). When it is 0, it will always face the direction defined by Trajectory.BulletFacing. Otherwise, it will rotate towards the direction defined by Trajectory.BulletFacing according to this speed.

      Trajectory.BulletROT 控制抛射体朝向(面朝方向)的旋转速度。当其为 0 时,会一直朝向 Trajectory.BulletFacing 所定义的方向。否则,它将根据此速度朝向 Trajectory.BulletFacing 定义的目标旋转。

    • Trajectory.BulletFacing controls what direction the projectile should face. This has the following 7 modes.

      Trajectory.BulletFacing 控制抛射体应该朝向哪个方向。它有如下 7 种模式:

      • Velocity - Towards the direction of motion of the projectile. If Trajectory.BulletROT is negative, it will only rotate on the horizontal plane.

        Velocity - 朝向抛射体运动的方向。如果 Trajectory.BulletROT 为负数,则会只在水平面上旋转。

      • Spin - Continuously rotating itself on a horizontal plane. The positive and negative of Trajectory.BulletROT can control the direction.

        Spin - 在水平面上持续自转。Trajectory.BulletROT 的正负能控制自转的方向。

      • Stable - Static after launch and no longer rotates towards the direction. If Trajectory.BulletROT is negative, only the direction on the horizontal plane exists.

        Stable - 发射后固定方向不再旋转。如果 Trajectory.BulletROT 为负数,则只保留水平面上的朝向。

      • Target - Towards the direction of the projectile target unit. If Trajectory.BulletROT is negative, it will only rotate on the horizontal plane.

        Target - 朝向抛射体的目标单位。如果 Trajectory.BulletROT 为负数,则会只在水平面上旋转。

      • Destination - Towards the direction of the projectile destination. If Trajectory.BulletROT is negative, it will only rotate on the horizontal plane.

        Target - 朝向抛射体的目的地。如果 Trajectory.BulletROT 为负数,则会只在水平面上旋转。

      • FirerBody - Follow the orientation of the firer's body, and remain still after the launcher is killed. Only rotates on a horizontal plane.

        FirerBody - 跟随发射者的身体朝向,并在发射者被摧毁后保持。只会在水平面上旋转。

      • FirerTurret - Follow the orientation of the firer's turret, and remain still after the launcher is killed. Only rotates on a horizontal plane.

        FirerTurret - 跟随发射者的炮塔朝向,并在发射者被摧毁后保持。只会在水平面上旋转。

    • Trajectory.RetargetRadius controls the radius of the projectile to search for a new target after losing its original target. The projectile will search for new target at the original target's location. The following have exceptions.

      Trajectory.RetargetRadius 控制抛射体在丢失原目标后搜索新目标的范围的半径。抛射体将在原本目标位置搜索新目标。以下有特例。

      • In Trajectory=Missile, if the projectile hasn't arrived Trajectory.Missile.PreAimCoord yet, the last coordinate of the original target is taken as the center of the searching circle. Otherwise, the coordinate of the distance in front of the projectile is taken as the center of the circle. Set to 0 indicates that this function is not enabled, and it will still attempt to attack the original target's location. If it is set to a negative value, it will self explode in place when it starts searching.

        Trajectory=Missile 中,若抛射体还未到达过 Trajectory.Missile.PreAimCoord ,原本目标最后所在的坐标将被用作搜索范围的中心。否则,会将抛射体前方这个距离的坐标用作搜索范围的中心。设置为 0 时代表功能不启用,抛射体仍将尝试攻击原目标的位置。如果设置为负值,则会在开始索敌时原地自爆。

      • In Trajectory=Tracing, the projectile will search for new target at the current position of itself.

        Trajectory=Tracing 中,抛射体将在抛射体自己的位置搜索新目标。

      • Trajectory.RetargetInterval controls the interval between each search for a new target again.

        Trajectory.RetargetInterval 控制每次再次搜索新目标的间隔。

      • Trajectory.RetargetHouses controls the projectile can find new target from which houses.

        Trajectory.RetargetHouses 控制抛射体能从哪些所属方寻找到新目标。

    • Trajectory.Synchronize controls whether the target of the projectile is synchronized with the target of its firer. If not, the projectile will not update the target.

      Trajectory.Synchronize 控制抛射体的目标是否同步发射者的目标,如果不同步,抛射体不会直接更新目标。

    • Trajectory.PeacefulVanish controls whether the projectile disappears directly when it is about to detonate, without producing animation or causing damage. The default value is Trajectory=Engrave or Trajectory.ProximityImpact not equal to 0 or Trajectory.DisperseCycle not equal to 0.

      Trajectory.PeacefulVanish 控制抛射体在最后要引爆时是否直接消失,不生成动画和造成伤害。默认值为 Trajectory=EngraveTrajectory.ProximityImpact 不等于 0 或 Trajectory.DisperseCycle 不等于 0 。

    • Trajectory.ApplyRangeModifiers controls whether any applicable weapon range modifiers from the firer are applied to the projectile. Effective options include Trajectory.Duration, Trajectory.DetonationDistance and Trajectory.EdgeAttenuation.

      Trajectory.ApplyRangeModifiers 控制是否将任何适用的武器射程修改应用于抛射体。有效的包括 Trajectory.DurationTrajectory.DetonationDistanceTrajectory.EdgeAttenuation

    • Trajectory.UseDisperseCoord controls whether the fire position need to replaced with the FLH of its superior's trajectory, which set Trajectory.RecordSourceCoord to true. Only takes effect when it is fired from one of the Trajectory.DisperseWeapons.

      Trajectory.UseDisperseCoord 控制是否使用它的设置了 Trajectory.RecordSourceCoord 的上级轨迹抛射体的 FLH 替换发射位置。仅当它自身是从 Trajectory.DisperseWeapons 中发射时生效.

    • Trajectory.RecordSourceCoord controls whether the projectile needs to record the launch position, which will be used for the appropriate weapons in Trajectory.DisperseWeapons. It can be nested and inherited, which need subordinates to enable Trajectory.UseDisperseCoord. The default value is Trajectory=Engrave or have set Trajectory.DisperseWeapons.

      Trajectory.RecordSourceCoord 控制抛射体是否需要记录发射位置,用于 Trajectory.DisperseWeapons 中合适的武器。可以嵌套继承,成功继承需要下级拥有 Trajectory.UseDisperseCoord 。默认值为 Trajectory=Engrave 或设置了 Trajectory.DisperseWeapons

      • In Trajectory=Engrave, it will also be used as a starting point for laser drawing.

        Trajectory=Engrave 中,它将同样被用作激光绘制的起点。

    • Trajectory.OffsetCoord controls the offsets of the target. Projectile will aim at the relative coordinates of the target to attack. It also supports Inaccurate and Trajectory.LeadTimeCalculate on this basis.

      Trajectory.OffsetCoord 控制目标位置的额外偏移,抛射体将会瞄准目标的这个相对坐标进行攻击。它也支持在此基础上使用 InaccurateTrajectory.LeadTimeCalculate

      • In Trajectory=Engrave or Trajectory=Tracing, these are invalid.

        Trajectory=EngraveTrajectory=Tracing 中,这些是无效的。

      • Trajectory.RotateCoord controls whether to rotate the projectile's firing direction within the angle bisector of Trajectory.OffsetCoord (or Trajectory.Missile.PreAimCoord in Trajectory=Missile) according to the most superior's weapon's Burst. Set to 0 to disable this function. Negative values will reverse the direction of rotation.

        Trajectory.RotateCoord 控制是否按照武器的 Burst 在以 Trajectory.OffsetCoord(或 Trajectory=Missile 中的 Trajectory.Missile.PreAimCoord)为角平分线的角度内旋转抛射体发射方向,为 0 代表不启用该逻辑。负值会反转旋转方向。

      • Trajectory.MirrorCoord controls whether Trajectory.OffsetCoord (and Trajectory.Missile.PreAimCoord in Trajectory=Missile) need to automatically mirror the lateral value to adapt to the firer's current burst index. At the same time as mirroring, the rotation direction calculated by Trajectory.RotateCoord will also be reversed, and the rotation angle between each adjacent projectile on each side will not change as a result.

        Trajectory.MirrorCoord 控制是否需要自动镜像 Trajectory.OffsetCoord(和 Trajectory=Missile 中的 Trajectory.Missile.PreAimCoord)中的横向坐标以适应发射者当前的爆发计数。镜像的同时 Trajectory.RotateCoord 计算的旋转方向也会反转,每侧相邻抛射体间的旋转角度不变。

      • Trajectory.AxisOfRotation controls the rotation axis when calculating Trajectory.RotateCoord. The axis will rotates with the unit orientation or the vector that from target position to the source position. The length is not important, but the direction is important (the opposite vector will also reverse the rotation direction).

        Trajectory.AxisOfRotation 控制计算 Trajectory.RotateCoord 时的旋转轴,旋转轴会随单位朝向或目标位置到发射位置的向量旋转。长度不重要,但方向很重要(相反的向量也会反转旋转方向)。

    • Trajectory.LeadTimeCalculate controls whether the projectile need to calculate the lead time of the target when firing.

      Trajectory.LeadTimeCalculate 控制抛射体在开火时是否计算目标的提前量。

      • Trajectory.LeadTimeMaximum controls the projectile to predict how long the target will continue to move (used to prevent the projectile from flying too far).

        Trajectory.LeadTimeMaximum 控制抛射体最多能够预测目标还会继续运动多久(用于让抛射体不会飞得太远)。

    • Trajectory.DetonationDistance controls the maximum distance in cells from intended target at which the projectile will be forced to detonate. Set to 0 to disable forced detonation. The following are exceptions.

      Trajectory.DetonationDistance 控制抛射体距离预期目标多少格内会强制引爆。设置为 0 可禁用强制引爆。以下为特例。

      • In Trajectory=Straight, if Trajectory.ApplyRangeModifiers is set to true, any applicable weapon range modifiers from the firer are applied here as well. By setting Trajectory.Straight.PassThrough=true, it refers to the distance that projectile should travel from its firer when it above 0, and the distance that projectile should move behind the target when it below 0 (use the absolute value), and keep moving without distance restrictions when it is zero.

        Trajectory=Straight 中,如果启用了 Trajectory.ApplyRangeModifiers ,发射者任何适用的武器射程修改也会在此处应用。通过设置 Trajectory.Straight.PassThrough=true ,此值代表了其为正时抛射体会从发射者位置行进这么长的距离,为负时抛射体会行进到目标身后这么长的距离(使用绝对值),为零时无距离限制地保持行进。

      • In Trajectory=Bombard and Trajectory=Parabola, when it is set to a negative value, if the target is movable, it will change its target to the cell where the target is located (This is a function expanded for Trajectory.DisperseWeapons and AirburstWeapon).

        Trajectory=BombardTrajectory=Parabola 中,当设置为负数时,若目标是可移动的,它将会把目标更改为目标所在的单元格(这是一个为 Trajectory.DisperseWeaponsAirburstWeapon 扩展的功能)。

    • Trajectory.TargetSnapDistance controls the maximum distance in cells from intended target the projectile can be at moment of detonation to make the projectile 'snap' on the intended target. Set to 0 to disable snapping.

      Trajectory.TargetSnapDistance 控制抛射体距离预期目标多少格内时,抛射体能 “啪” 的一下吸附到目标身上。设为 0 将禁用吸附。

    • Trajectory.DetonationHeight controls when the projectile is in a descending state and below the height of the launch position plus this value, it will detonate prematurely. Taking effect when it is set to non negative value. If Trajectory.EarlyDetonation is set to true, it'll take effect during the ascending stage instead, which makes it detonate when its height is above the launch position plus this value.

      Trajectory.DetonationHeight 控制当抛射体处于下落阶段且低于发射位置的高度加上此值时会提前引爆。设置为非负数时生效。如果启用了 Trajectory.EarlyDetonation ,它将在上升阶段且高度高于发射位置的高度加上此值时被提前引爆。

      • Only in Trajectory=Bombard or Trajectory=Parabola, these are valid.

        只有在 Trajectory=BombardTrajectory=Parabola 中,这些会生效。

    • Trajectory.AllowFirerTurning controls whether the projectile allow for significant changes in the orientation of the firer, otherwise it will be immediately detonated.

      Trajectory.AllowFirerTurning 控制抛射体在发射后是否允许发射者的朝向大幅度变动,否则将被立刻引爆。

In rulesmd.ini:

[SOMEPROJECTILE]                      ; Projectile
Trajectory=                           ; Trajectory type enumeration (Straight|Bombard|Missile|Engrave|Parabola|Tracing)
Trajectory.Speed=100.0                ; floating point value
Trajectory.Duration=0                 ; integer
Trajectory.TolerantTime=-1            ; integer
Trajectory.CreateCapacity=-1          ; integer
Trajectory.BulletROT=0                ; integer
Trajectory.BulletFacing=velocity      ; Bullet facing enumeration (Velocity|Spin|Stable|Target|Destination|FirerBody|FirerTurret)
Trajectory.RetargetRadius=0           ; floating point value
Trajectory.RetargetInterval=1         ; integer
Trajectory.RetargetHouses=enemies     ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all)
Trajectory.Synchronize=false          ; boolean
Trajectory.PeacefulVanish=            ; boolean
Trajectory.ApplyRangeModifiers=false  ; boolean
Trajectory.UseDisperseCoord=false     ; boolean
Trajectory.RecordSourceCoord=         ; boolean
Trajectory.OffsetCoord=0,0,0          ; integer - Forward,Lateral,Height
Trajectory.RotateCoord=0              ; floating point value
Trajectory.MirrorCoord=true           ; boolean
Trajectory.AxisOfRotation=0,0,1       ; integer - Forward,Lateral,Height
Trajectory.LeadTimeCalculate=false    ; boolean
Trajectory.LeadTimeMaximum=0          ; integer, game frames
Trajectory.DetonationDistance=0.4     ; floating point value
Trajectory.TargetSnapDistance=0.5     ; floating point value
Trajectory.EarlyDetonation=false      ; boolean
Trajectory.DetonationHeight=-1        ; integer
Trajectory.AllowFirerTurning=true     ; boolean

  • Make sure you set a low Trajectory.RetargetRadius value unless necessary.

    除非必要,否则请确保你设置了一个较低的 Trajectory.RetargetRadius 值。

  • Trajectory.LeadTimeCalculate will not affect the facing of the turret.

    Trajectory.LeadTimeCalculate 不会影响炮塔的朝向。



  • Trajectory.LeadTimeCalculate performs best when the projectile speed is 2 to 3 times the target speed.

    Trajectory.LeadTimeCalculate 在抛射体的速度是目标的速度的二至三倍时表现最佳。


  • It also has linkage functions with Inaccurate, BallisticScatter.Min, BallisticScatter.Max, Gravity, SubjectToGround.

    它也具有与 InaccurateBallisticScatter.MinBallisticScatter.MaxGravitySubjectToGround 联动的功能。

  • The following table will briefly display the support of various types for various general functions. (⚪ - effective / · - invalid)

    下表将简要显示各种类型对各种一般功能的支持。 (⚪ - 有效 / · - 无效)

Key Straight Bombard Missile Engrave Parabola Tracing
Inaccurate · ·
BallisticScatter · ·
Gravity · · ·
SubjectToGround · · · ·
ProjectileRange(Weapon's) · ·
Trajectory.Speed
Trajectory.Duration
Trajectory.TolerantTime
Trajectory.CreateCapacity
Trajectory.BulletROT
Trajectory.BulletFacing
Trajectory.RetargetRadius
Trajectory.Synchronize
Trajectory.PeacefulVanish
Trajectory.ApplyRangeModifiers
Trajectory.UseDisperseCoord
Trajectory.RecordSourceCoord
Trajectory.OffsetCoord · ·
Trajectory.RotateCoord · ·
Trajectory.MirrorCoord
Trajectory.AxisOfRotation · ·
Trajectory.LeadTimeCalculate · ·
Trajectory.LeadTimeMaximum · · ·
Trajectory.DetonationDistance · ·
Trajectory.TargetSnapDistance · ·
Trajectory.EarlyDetonation · · · ·
Trajectory.DetonationHeight · · · ·
Trajectory.AllowFirerTurning · · · ·

  • SubjectToGround can cause the projectile with Trajectory=Straight during the entire process or the projectile with Trajectory=Bombard during the ascent phase to detonate prematurely due to impact with the ground.

    SubjectToGround 能够使得 Trajectory=Straight 的抛体在全程中或 Trajectory=Bombard 的抛体在上升阶段中,因撞击到地面而提前引爆

  • Setting Trajectory.Missile.UniqueCurve will ignore all of these settings.

    使用 Trajectory.Missile.UniqueCurve 将忽略所有这些设置。



  • In addition, these types of projectile also have some general functions for detonating warheads. Effective for all types.

    此外,这些类型的抛射体还具有一些引爆弹头的通用功能。对所有类型都有效。

    • Trajectory.PassDetonate enables extra detonations when the projectile is traveling. (You can use this when you want the projectile to detonate warheads every other distance/time during the flight.)

      Trajectory.PassDetonate 启用抛射体在飞行过程中的额外爆炸。(如果你希望抛射体在飞行过程中每隔一定距离/时间引爆一次那么可以使用此功能。)

      • Trajectory.PassDetonateWarhead defines the warhead detonated by Trajectory.PassDetonate. If not set, use the original warhead of the projectile.

        Trajectory.PassDetonateWarhead 定义了 Trajectory.PassDetonate 引爆的弹头。如果未设置,则使用抛射体自身原本的弹头。

      • Trajectory.PassDetonateDamage defines the damage caused by Trajectory.PassDetonateWarhead. If not set, use the original damage of the projectile.

        Trajectory.PassDetonateDamage 定义了 Trajectory.PassDetonateWarhead 造成的伤害。如果未设置,则使用抛射体自身原本的伤害。

      • Trajectory.PassDetonateDelay controls the delay for detonating the warhead defined by Trajectory.PassDetonateWarhead.

        Trajectory.PassDetonateDelay 控制 Trajectory.PassDetonateWarhead 所定义弹头的引爆间隔。

      • Trajectory.PassDetonateInitialDelay controls the initial delay for detonating the warhead defined by Trajectory.PassDetonateWarhead.

        Trajectory.PassDetonateInitialDelay 控制 Trajectory.PassDetonateWarhead 所定义弹头的初始引爆延迟。

      • Trajectory.PassDetonateLocal controls whether Trajectory.PassDetonateWarhead and weapon's Warhead are always detonate at ground level.

        Trajectory.PassDetonateLocal 控制 Trajectory.PassDetonateWarhead 和武器 Warhead 是否始终在地面高度引爆。

    • Trajectory.ProximityImpact controls the initial proximity fuse times. When there are enough remaining times and the projectile approaches another valid target, it will detonate a warhead defined by Trajectory.ProximityWarhead on it. If the times is about to run out, it will also detonate itself at its location. This function can be cancelled by setting to 0. A negative integer means unlimited times. By the way, you can use the weapon's Warhead with low Versus only to aim at the target, and use the Trajectory.ProximityWarhead to causing actual harm. (You can use this to cause non repeated damage to all units encountered during the flight of the projectile.)

      Trajectory.ProximityImpact 控制初始的近炸引信次数。当剩余次数足够且抛射体靠近另一个有效目标时它将引爆由 Trajectory.ProximityWarhead 定义的弹头,如果剩余次数即将用完它将在当前位置引爆。此功能可以通过设为 0 禁用。负值整数表示无限次数。顺带一提,你可以使用带有低 Versus 的武器 Warhead 以瞄准目标,使用 Trajectory.ProximityWarhead 造成实际伤害。(你可以使用此功能在抛射体飞行期间对遇到的所有单位造成非重复伤害。)

      • Trajectory.ProximityWarhead defines the warhead detonated by Trajectory.ProximityImpact. If not set, use the original warhead of the projectile.

        Trajectory.ProximityWarhead 定义了 Trajectory.ProximityImpact 引爆的弹头。如果未设置,则使用抛射体自身原本的弹头。

      • Trajectory.ProximityDamage defines the damage caused by Trajectory.ProximityWarhead. If not set, use the original damage of the projectile.

        Trajectory.ProximityDamage 定义了 Trajectory.ProximityWarhead 造成的伤害。如果未设置,则使用抛射体自身原本的伤害。

      • Trajectory.ProximityRadius controls the range of proximity fuse. It can NOT be set as a negative value.

        Trajectory.ProximityRadius 控制近炸引信的范围。不支持负数。

      • Trajectory.ProximityDirect controls whether let the target receive damage instead of detonating the warhead.

        Trajectory.ProximityDirect 控制是否直接伤害目标而不是引爆弹头。

      • Trajectory.ProximityMedial controls whether to detonate Trajectory.ProximityWarhead at the bullet's location rather than the proximity target's location. If Trajectory.ProximityDirect is set to true, this will only affect the calculation result of Trajectory.DamageEdgeAttenuation.

        Trajectory.ProximityMedial 控制是否在抛射体位置而不是近炸目标位置引爆 Trajectory.ProximityWarhead 。如果启用 Trajectory.ProximityDirect ,这只会影响 Trajectory.DamageEdgeAttenuation 的计算结果。

      • Trajectory.ProximityAllies controls whether allies will also trigger the proximity fuse.

        Trajectory.ProximityAllies 控制友军是否也会触发近炸引信。

      • Trajectory.ProximityFlight controls whether to count units in the air.

        Trajectory.ProximityFlight 控制是否考虑空中的单位。

    • Trajectory.ThroughVehicles controls whether the projectile will not be obstructed by vehicles or aircrafts on the ground. When it is obstructed, it will be directly detonated at its location. If it still have Trajectory.ProximityImpact times, it will also detonate a Trajectory.ProximityWarhead at the location of the obstacle. Before the projectile being blocked, Trajectory.ProximityImpact will also not cause damage to vehicles or aircrafts.

      Trajectory.ThroughVehicles 控制抛射体是否不会被地面上的载具或战机阻挡。当它被阻挡时将直接在其位置引爆。如果它的 Trajectory.ProximityImpact 次数还没耗尽,它也会在障碍物位置引爆一个 Trajectory.ProximityWarhead。在它被阻挡前,Trajectory.ProximityImpact 也将不会对载具或战机造成伤害。

    • Trajectory.ThroughBuilding controls whether the projectile will not be obstructed by buildings. When it is obstructed, it will be directly detonated at its location. If it still have Trajectory.ProximityImpact times, it will also detonate a Trajectory.ProximityImpact at the location of the obstacle. Before the projectile being blocked, Trajectory.ProximityImpact will also not cause damage to buildings.

      Trajectory.ThroughBuilding 控制抛射体是否不会被建筑阻挡。当它被阻挡时将直接在其位置引爆。如果它的 Trajectory.ProximityImpact 次数还没耗尽,它也会在障碍物位置引爆一个 Trajectory.ProximityWarhead。在它被阻挡前,Trajectory.ProximityImpact 也将不会对建筑造成伤害。

    • Trajectory.DamageEdgeAttenuation controls the edge attenuation ratio of projectile damage (includes all types of the trajectory's damage), that is, the actual damage caused will be this value multiplied by the ratio of the current distance to the weapon's range. Can NOT be set to a negative value.

      Trajectory.DamageEdgeAttenuation 控制抛射体伤害的边缘衰减比例(包括轨迹添加的所有伤害类型),即实际造成的伤害是将此值乘以当前距离与武器射程的比值。不能设为负数。

    • Trajectory.DamageCountAttenuation controls the attenuation coefficient related to frequency of projectile damage (includes all types of the trajectory's damage), that is, how many times the next damage after each bounce is the damage just caused. Can NOT be set to a negative value.

      Trajectory.DamageCountAttenuation 控制抛射体伤害的次数衰减系数(包括轨迹添加的所有伤害类型),即每次造成伤害后的下一次伤害会是多少倍。不能设为负数。

In rulesmd.ini:

[SOMEPROJECTILE]                       ; Projectile
Trajectory.PassDetonate=false          ; boolean
Trajectory.PassDetonateWarhead=        ; WarheadType
Trajectory.PassDetonateDamage=         ; integer
Trajectory.PassDetonateDelay=1         ; integer, game frames
Trajectory.PassDetonateInitialDelay=0  ; integer, game frames
Trajectory.PassDetonateLocal=false     ; boolean
Trajectory.ProximityImpact=0           ; integer
Trajectory.ProximityWarhead=           ; WarheadType
Trajectory.ProximityDamage=            ; integer
Trajectory.ProximityRadius=0.7         ; floating point value
Trajectory.ProximityDirect=false       ; boolean
Trajectory.ProximityMedial=false       ; boolean
Trajectory.ProximityAllies=false       ; boolean
Trajectory.ProximityFlight=false       ; boolean
Trajectory.ThroughVehicles=true        ; boolean
Trajectory.ThroughBuilding=true        ; boolean
Trajectory.DamageEdgeAttenuation=1.0   ; floating point value
Trajectory.DamageCountAttenuation=1.0  ; floating point value

  • The listed Warheads in Trajectory.PassDetonateWarhead and Trajectory.ProximityWarhead must be listed in [Warheads] for them to work.

    Trajectory.PassDetonateWarheadTrajectory.ProximityWarhead 中使用的弹头必须注册在 [Warheads] 中以保证正确工作。

  • Make sure you set a low Trajectory.ProximityRadius value unless necessary.

    除非必要,否则请确保你设置了一个较低的 Trajectory.ProximityRadius 值。



  • SubjectToBuildings and Trajectory.ThroughBuilding are different. But the two are not in conflict and can take effect simultaneously. The former can affect the search for enemies and ignore the main target, follows the settings of Ares and will only self destruct when conditions are met. While the latter will self destruct when touching only non-allies building (including main target) and trigger its effect if Trajectory.ProximityImpact is set.

    SubjectToBuildingsTrajectory.ThroughBuilding 是不同的。但两者并不冲突,且可以同时生效。前者可以影响对敌人的搜索,忽略主要目标,遵循 Ares 的设置,只有在条件满足时才会自毁。而后者在接触非盟友建筑(包括主要目标)时都会自毁,如果设置了 Trajectory.ProximityImpact ,则还会触发其效果。

  • Simply put, Trajectory.PassDetonate is periodically effect and Trajectory.ProximityImpact is once per person effect.

    简而言之,Trajectory.PassDetonate 是周期性效果,Trajectory.ProximityImpact 是一人一次的效果。

  • If Trajectory.ProximityImpact is set to non-zero, the default value of Trajectory.PeacefulVanish will be changed.

    如果设置了 Trajectory.ProximityImpact 为非 0 值,Trajectory.PeacefulVanish 的默认值会被改变。



  • Of course, there are also some general functions for launching weapons. Effective for all types too.

    当然,还有一些发射武器的通用功能。同样对所有类型都有效。

    • Trajectory.DisperseWeapons defines the dispersal weapons of the projectile.

      Trajectory.DisperseWeapons 定义抛射体的扩散武器。

    • Trajectory.DisperseBursts defines how many corresponding weapons each time the projectile will fire. When the quantity is lower than Trajectory.DisperseWeapons, the last value in the list will be used.

      Trajectory.DisperseBursts 定义抛射体每次各扩散几个对应的武器。如果数量少于 Trajectory.DisperseWeapons ,将使用该列表的最后一项。

    • Trajectory.DisperseCounts controls how many times the projectile can fire the weapon. Set to a negative value means unlimited times. If set to zero, the cooling will be calculated directly without firing the weapon. If the quantity is less than the number of firing groups, the last value in the list will be used.

      Trajectory.DisperseCounts 控制抛射体需要各扩散几次对应武器。负数视为无限。为零会直接计算冷却而不发射。如果少于发射组数量,将使用该列表的最后一项。

    • Trajectory.DisperseDelays controls the interval delays for dispersing the weapons, at least 1 frame. If the quantity is less than the number of firing groups, the last value in the list will be used.

      Trajectory.DisperseDelays 控制抛射体发射扩散武器的间隔,至少 1 帧。如果少于发射组数量,将使用该列表的最后一项。

    • Trajectory.DisperseCycle controls how many rounds of weapons the projectile can fire, zero will not fire weapons, and negative numbers are considered infinite.

      Trajectory.DisperseCycle 控制抛射体能够发射几轮武器,为零不会发射武器,负数视为无限。

    • Trajectory.DisperseInitialDelay controls the initial delay for dispersing the weapons defined by Trajectory.DisperseWeapons.

      Trajectory.DisperseInitialDelay 控制抛射体发射 Trajectory.DisperseWeapons 的初始间隔。

    • Trajectory.DisperseEffectiveRange controls the weapon dispersing timer to start counting only within this distance of reaching the target. Set to 0 to disable this function. Set to a negative value means it will only Disperse the weapon at most once before detonation.

      Trajectory.DisperseEffectiveRange 控制抛射体扩散武器计时器会在距离目标这么近的距离时才会开始计时。为0代表不启用该功能。为负数代表只有在即将引爆本体的瞬间扩散最多一次的武器。

    • Trajectory.DisperseSeparate controls whether the projectile no longer fire all the weapons in Trajectory.DisperseWeapons at once and instead fire a group of weapons in the list order, following Trajectory.DisperseBursts. And control how to calculate the number of firing groups. In short, if true, group the weapons and fire them the corresponding counts of times in Trajectory.DisperseWeapons order. Otherwise, fire all weapons simultaneously and fire sequentially in Trajectory.DisperseCounts order.

      Trajectory.DisperseSeparate 控制抛射体每次扩散武器时,是否不再一次性发射 Trajectory.DisperseWeapons 中的所有武器,而是按顺序发射列表中的一组武器,遵循其 Trajectory.DisperseBursts 。并控制如何计算发射组的数量。简单来说,如果为 true ,会按照 Trajectory.DisperseWeapons 的顺序将武器分组并发射对应次数。否则会按照 Trajectory.DisperseCounts 顺序依次同时发射所有武器。

    • Trajectory.DisperseRetarget controls whether the Disperse weapons will find new targets on their own. Using the Range, CanTarget, CanTargetHouses, required AttachedEffects of weapons to search new targets.

      Trajectory.DisperseRetarget 控制扩散的武器是否自行寻找新目标。使用武器的 RangeCanTargetCanTargetHouses 和所需的 AttachedEffects 来搜索新目标。

    • Trajectory.DisperseLocation controls whether the Disperse weapons will search for new targets at the center of the spreading position, otherwise they will focus on the original target.

      Trajectory.DisperseLocation 控制扩散的武器在自行寻找新目标时,是否以扩散位置为中心的位置寻找,否则以原目标为中心位置。

    • Trajectory.DisperseTendency controls whether the Disperse weapons will choose the original target as the first new target in each group of weapons.

      Trajectory.DisperseTendency 控制扩散的武器中每组武器的第一个目标是否选择原目标。

    • Trajectory.DisperseHolistic controls whether the Disperse weapons will choose targets that are in different states from the original target (in air and on ground).

      Trajectory.DisperseHolistic 控制扩散的武器是否选择和原目标不同状态的目标(在空中和在地上两种状态)。

    • Trajectory.DisperseMarginal controls whether the Disperse weapons will choose unimportant items such as trees (regard as on ground), streetlights (regard as on ground) or bullets (regard as in air) as secondary targets.

      Trajectory.DisperseMarginal 控制扩散的武器是否会选择数木(视为在地上)、路灯(视为在地上)或可拦截抛体(视为在空中)等物体作为次要目标。

    • Trajectory.DisperseDoRepeat controls whether the Disperse weapons will select duplicate targets when the number of targets is insufficient. If it is set to true, when the weapon can select both the technos and the ground as targets, the technos will be prioritized, then if all non-repeating technos have been selected and the weapon can still be launched at this time (in each round of salvo), it will start selecting duplicate technos. If it is set to false, when the weapon can select both the technos and the ground as targets, the technos will be prioritized, followed by the ground cells, then if all non-repeating targets have been selected and the weapon can still be launched at this time (in each round of salvo), it will stop firing remaining bursts. (The priority of secondary targets is between the technos and the ground.)

      Trajectory.DisperseDoRepeat 控制抛射体扩散的武器是否会因为搜索到的目标数量不足而尝试攻击相同目标。是则,当武器可以攻击单位和地面时,优先攻击单位,如果所有非重复单位都已选择,并且武器此时(在每一轮齐射中)仍然可以发射,它将开始选择重复单位;否则,当武器可以攻击单位和地面时,优先攻击单位,然后尝试攻击所有可被攻击的地面格子,如果所有非重复目标都已被选择,并且武器此时(在每一轮齐射中)仍然可以发射,则会停止发射剩余次数。(次要目标的优先级在单位和地面之间。)

    • Trajectory.DisperseSuicide controls whether the projectile will self destruct after the number of times it spreads the weapon has been exhausted.

      Trajectory.DisperseSuicide 控制抛射体是否在扩散武器的次数耗尽后自爆。

    • Trajectory.DisperseFromFirer controls whether the weapons will be fired by the firer towards the projectile. Otherwise, the tracing weapons will be fired from the projectile towards the target. When Trajectory=Engrave or Trajectory=Tracing, the default is true, while others are false.

      Trajectory.DisperseFromFirer 控制抛射体发射的扩散武器是否由单位朝抛体发射。否则是由抛体朝目标发射。当 Trajectory=EngraveTrajectory=Tracing 时,默认值为 true ,否则为 false 。

    • Trajectory.DisperseFaceCheck controls whether the projectile will check its orientation before firing the weapons. Ignore this if Trajectory.BulletFacing=Velocity or Trajectory.BulletFacing=Spin.

      Trajectory.DisperseFaceCheck 控制抛射体在发射武器前是否检查抛射体的朝向。如果 Trajectory.BulletFacing=VelocityTrajectory.BulletFacing=Spin 会无视该项。

    • Trajectory.DisperseForceFire controls whether still fire disperse weapon when the projectile itself has no target or when Trajectory.Synchronize=true and the target of the projectile is beyond the weapon's range.

      Trajectory.DisperseForceFire 控制抛射体本身在没有目标或在 Trajectory.Synchronize=true 且目标超出射程时是否仍然发射扩散武器。

    • Trajectory.DisperseCoord controls the FLH where the projectile fires the weapon when set Trajectory.DisperseFromFirer to false.

      Trajectory.DisperseCoord 控制当禁用 Trajectory.DisperseFromFirer 时从抛射体上发射武器的 FLH 位置。

In rulesmd.ini:

[SOMEPROJECTILE]                     ; Projectile
Trajectory.DisperseWeapons=          ; list of WeaponTypes
Trajectory.DisperseBursts=           ; list of integers
Trajectory.DisperseCounts=           ; list of integers
Trajectory.DisperseDelays=           ; list of integers, game frames
Trajectory.DisperseCycle=0           ; integer
Trajectory.DisperseInitialDelay=0    ; integer, game frames
Trajectory.DisperseEffectiveRange=0  ; floating point value
Trajectory.DisperseSeparate=false    ; boolean
Trajectory.DisperseRetarget=false    ; boolean
Trajectory.DisperseLocation=false    ; boolean
Trajectory.DisperseTendency=false    ; boolean
Trajectory.DisperseHolistic=false    ; boolean
Trajectory.DisperseMarginal=false    ; boolean
Trajectory.DisperseDoRepeat=false    ; boolean
Trajectory.DisperseSuicide=true      ; boolean
Trajectory.DisperseFromFirer=        ; boolean
Trajectory.DisperseFaceCheck=false   ; boolean
Trajectory.DisperseForceFire=true    ; boolean
Trajectory.DisperseCoord=0,0,0       ; integer - Forward,Lateral,Height

  • The listed Weapons in Trajectory.DisperseWeapons must be listed in [WeaponTypes] for them to work.

    Trajectory.DisperseWeapons 中使用的武器必须注册在 [WeaponTypes] 中以保证正确工作。

  • If you set Trajectory.DisperseRetarget=true, also make sure you set Trajectory.DisperseWeapons a low Range value unless necessary.

    如果你设置了 Trajectory.DisperseRetarget=yes ,那么同样的,除非必要,否则请确保你给 Trajectory.DisperseWeapons 中所列出的所有武器设置了一个较低的 Range 值。



  • Although Trajectory.DisperseDoRepeat=false will disable duplicate target selection, if the weapon is able to attack the ground, it may still attack duplicate targets by locking onto the cell where the target is located.

    尽管 Trajectory.DisperseDoRepeat=no 会禁用重复选择目标,但若武器能够攻击地面,则仍然可能因为锁定到目标所处的格子而攻击到重复目标。

  • Trajectory.DisperseRetarget will not change the true target of the projectile itself.

    Trajectory.DisperseRetarget 不会改变抛射体本身真正的目标。

  • If Trajectory.DisperseCycle is set to non-zero, the default value of Trajectory.PeacefulVanish will be changed.

    如果设置了 Trajectory.DisperseCycle 为非 0 值,Trajectory.PeacefulVanish 的默认值会被改变。



Straight trajectory

  • Self-explanatory, is a straight-shot trajectory.

    不言自明,这是一种直线射击轨迹。

    • Trajectory.Straight.PassThrough enables special case logic where the projectile does not detonate in contact with the target but instead travels up to a distance defined by Trajectory.DetonationDistance. Note that if Trajectory.DetonationDistance is a non negative value, the firing angle of the projectile is adjusted with this in mind, making it fire straight ahead if the target is on same elevation.

      Trajectory.Straight.PassThrough 启用特殊的逻辑:抛射体在接触目标时不会引爆而是继续飞行到由 Trajectory.DetonationDistance 定义的距离。注意如果 Trajectory.DetonationDistance 是一个非负数,抛射体发射角度会受此影响:如果与目标处于同一高度那么直接向前发射。

    • Trajectory.Straight.ConfineAtHeight controls the height above ground that projectile will try to travel as it can. It can not move down from the cliff by setting SubjectToCliffs to true. It can be cancelled by setting as a non positive integer. It will be forcibly cancelled by setting Trajectory.Speed above 256. If Trajectory.PassDetonateLocal is set to true at the same time, the vertical speed will not be limited.

      Trajectory.Straight.ConfineAtHeight 控制抛射体尽可能保持的飞行高度。如果启用了 SubjectToCliffs 会使其不得从悬崖上向下移动。可以通过设置为非正整数来取消。如果 Trajectory.Speed 大于 256,它将被强制取消。如果同时启用了 Trajectory.PassDetonateLocal ,其垂直方向上的速度将不受限制。

In rulesmd.ini:

[SOMEPROJECTILE]                       ; Projectile
Trajectory.Straight.PassThrough=false  ; boolean
Trajectory.Straight.ConfineAtHeight=0  ; integer

Bombard trajectory

  • Similar trajectory to Straight, but targets a coordinate between the attacker and intended target first. When the projectile approaches that turning point, it'll turn to the intended target and explodes when it hits the target or ground.

    类似于 Straight 但首先瞄准攻击者和目标间的某个坐标。当抛射体接近该拐点后它才会转向目标并在击中目标或地面时引爆。

    • Trajectory.Bombard.Height controls the height of the turning point.

      Trajectory.Bombard.Height 控制拐点的高度。

    • Trajectory.Bombard.FallPercent controls the distance of the turning point by its percentage of the total distance between attacker and intended target. If set to 0%, then it'll fly up vertically. If set to 100%, then it'll travel to the top of the intended target.

      Trajectory.Bombard.FallPercent 通过拐点位置占发射位置与目标位置间总距离的百分比来控制拐点的距离。如果设为 0% 则它将在开火位置的正上方。如果设为 100% 则它将位于目标正上方。

      • For each launch the turning point percentage could add or minus a random value, which is not greater than Trajectory.Bombard.FallPercentShift. If set to 0%, random shift will be disabled.

        每次发射时拐点位置的百分比值可以随机浮动,该值不大于 Trajectory.Bombard.FallPercentShift 。如果设为 0 则随机调整将被禁用。

      • You can also makes the turning point scatter randomly in a circle with Trajectory.Bombard.FallScatter.Max as its radius. If set to 0, random scatter will be disabled. Trajectory.Bombard.FallScatter.Min can be used to determine the minimum radius of the circle. If Trajectory.Bombard.FallScatter.Linear set to true, the random scatter will be limited to the line that is vertical to the original direction of the projectile.

        你还可以使拐点在以 Trajectory.Bombard.FallScatter.Max 为半径的圆内随机散布。如果设为 0 则随机散布将被禁用。Trajectory.Bombard.FallScatter.Min 可用于确定圆的最低半径。如果 Trajectory.Bombard.FallScatter.Linear 设为 true,则随机散布范围将被限制在发射位置与目标位置之间连线的垂面。

    • Trajectory.Bombard.FreeFallOnTarget controls how it'll hit the intended target. If set to true, the projectile will be respawned above the intended target and free fall. If set to false, the projectile will travel to the intended target from the turning point.

      Trajectory.Bombard.FreeFallOnTarget 控制它将如何击中目标。如果设为 true,则抛射体将在目标上方重新生成并自由落体。如果设为 false,则抛射体将正常从拐点位置飞向目标。

    • Trajectory.Bombard.NoLaunch controls whether the attacker will fire the projectile by itself. If set to true, projectile will directly fall from the turning point.

      Trajectory.Bombard.NoLaunch 控制攻击者是否会自行发射抛射体。如果设为 true,则抛射体将直接从拐点位置下落。

    • Trajectory.Bombard.FallSpeed controls the initial speed of the projectile after it turns. If set to 0.0, then it'll use Trajectory.Speed. Can't work when Trajectory.Bombard.FreeFallOnTarget set to true.

      Trajectory.Bombard.FallSpeed 控制抛射体经过拐点后的初速度。如果设为 0.0,则它将使用 Trajectory.Speed。当 Trajectory.Bombard.FreeFallOnTarget 设为 true 时将无法工作。

    • Trajectory.Bombard.TurningPointAnims, if set, will play an anim when the projectile reaches the turning point. If Trajectory.Bombard.FreeFallOnTarget is set to true, it'll be spawned above the target with the projectile together. If Trajectory.Bombard.NoLaunch is set to true, it'll be played at where the projectile falls, no matter if it's free fall or not. If more than one animation is listed, a random one is selected.

      Trajectory.Bombard.TurningPointAnims 如果设置将会在抛射体抵达拐点时播放动画。如果 Trajectory.Bombard.FreeFallOnTarget 设为 true,则抛射体将与目标一起在目标上方生成。如果 Trajectory.Bombard.NoLaunch 设为 true,则无论抛射体是否自由落体,它都将在抛射体下落的位置播放动画。如果列出了多个动画,则将随机选择一个。

In rulesmd.ini:

[SOMEPROJECTILE]                             ; Projectile
Trajectory.Bombard.Height=0.0                ; floating point value
Trajectory.Bombard.FallPercent=1.0           ; floating point value
Trajectory.Bombard.FallPercentShift=0.0      ; floating point value
Trajectory.Bombard.FallScatter.Max=0.0       ; floating point value
Trajectory.Bombard.FallScatter.Min=0.0       ; floating point value
Trajectory.Bombard.FallScatter.Linear=false  ; boolean
Trajectory.Bombard.FreeFallOnTarget=true     ; boolean
Trajectory.Bombard.NoLaunch=false            ; boolean
Trajectory.Bombard.FallSpeed=0.0             ; floating point value
Trajectory.Bombard.TurningPointAnims=        ; List of AnimationTypes

Missile trajectory

  • Its trajectory looks a bit like a ROT, but its settings are more flexible. It also has a unique trajectory.

    它的轨迹看起来有点像 ROT ,但它的设置更灵活。它也有独特的轨迹。

    • Trajectory.Missile.UniqueCurve controls whether to enable simulated Qian Xuesen trajectory. After enabling this, it will NOT respect the other items.

      Trajectory.Missile.UniqueCurve 控制是否启用模拟钱学森弹道。启用后,它将不再遵从其它设置。

    • Trajectory.Missile.PreAimCoord controls the initial flight direction of the projectile, and after reaching this coordinate, it will begin to turn towards the target direction. When it is set to 0,0,0 , it will directly face the target.

      Trajectory.Missile.PreAimCoord 控制抛射体初始会朝着哪个方向飞行,在到达这个坐标后,它会开始朝着目标转向。为 0,0,0 时会直接朝着目标前进。

      • Trajectory.Missile.FacingCoord controls whether the forward direction in Trajectory.Missile.PreAimCoord is depending on the orientation of the firer. By default, it will depend on the vector between the firer and the target.

        Trajectory.Missile.FacingCoord 控制抛射体 Trajectory.Missile.PreAimCoord 的朝向是否跟随单位朝向。

      • Trajectory.Missile.ReduceCoord controls whether Trajectory.Missile.PreAimCoord defines the initial movement coordinates when the attack distance is 10 cells, and the actual initial movement coordinates will change with the length of the attack distance. It can be simply understood as an optimization term aimed at ensuring hits at close range.

        Trajectory.Missile.ReduceCoord 控制抛射体 Trajectory.Missile.PreAimCoord 所指定的坐标是否代表距离 10 格远时的坐标,其实际坐标会随攻击距离变化。它可以简单地理解为一个确保近距离命中的优化手段。

    • Trajectory.Missile.LaunchSpeed controls the initial flight speed of the projectile.

      Trajectory.Missile.LaunchSpeed 控制抛射体的初始飞行速度。

      • Trajectory.Missile.Acceleration controls the acceleration of the projectile's flight speed, increasing or decreasing the speed per frame according to this value, the final speed will be Trajectory.Speed. The velocity superposition with gravity will not be limited by this value.

        Trajectory.Missile.Acceleration 控制抛射体飞行速度的加速度,按照此值每帧增加或减少速度,达到 Trajectory.Speed 。与重力的速度叠加不受此值限制。

      • Trajectory.Missile.TurningSpeed controls the turning speed of the projectile's locomotion (moving direction) which refers to the maximum angle that the projectile can rotate per frame in terms of direction.

        Trajectory.Missile.TurningSpeed 控制抛射体运动(移动方向)的转向速度,即抛射体的移动方向每帧最多能旋转多少角度。

    • Trajectory.Missile.LockDirection controls whether after reaching Trajectory.Missile.PreAimCoord and completing the first turn, the projectile will lock the direction of motion without further adjustment.

      Trajectory.Missile.LockDirection 控制抛射体是否在到达 Trajectory.Missile.PreAimCoord 并完成初次转向后是否锁定运动方向不再调整。

    • Trajectory.Missile.CruiseEnable controls whether after reaching Trajectory.Missile.PreAimCoord, the projectile will maintain altitude while moving towards the target.

      Trajectory.Missile.CruiseEnable 控制抛射体在达到 Trajectory.Missile.PreAimCoord 后是否需要保持高度朝着目标前进。

      • Trajectory.Missile.CruiseUnableRange controls how far away it is from the target to end the cruise phase, no longer maintaining the cruise altitude, and begins to move directly towards the target. If the distance is already below this value by the time Trajectory.Missile.PreAimCoord is reached, the cruise phase will be skipped.

        Trajectory.Missile.CruiseUnableRange 控制抛射体距离目标多远时结束巡航阶段,不再保持巡航高度,并开始朝向目标前进,如果在达到 Trajectory.Missile.PreAimCoord 时已经小于该值,会跳过巡航阶段。

      • Trajectory.Missile.CruiseAltitude controls the altitude of the projectile in the cruise phase.

        Trajectory.Missile.CruiseAltitude 控制抛射体的巡航阶段时的巡航高度。

      • Trajectory.Missile.CruiseAlongLevel controls whether to calculate Trajectory.Missile.CruiseAltitude by the ground height of the current position, otherwise it will be calculated by the height of the launch position.

        Trajectory.Missile.CruiseAlongLevel 控制是否根据当前抛射体位置的地面高度计算 Trajectory.Missile.CruiseAltitude ,否则将根据发射位置的高度进行计算。

    • Trajectory.Missile.SuicideAboveRange controls the projectile to self destruct directly after reaching the flight distance. Set to 0 to disable suicide. When set to a negative number, its absolute value represents a multiple of the initial distance.

      Trajectory.Missile.SuicideAboveRange 控制抛射体在飞行距离达到该值后直接自爆。为 0 代表不因此自爆。当设置为负数时其绝对值代表初始距离的倍数。

    • Trajectory.Missile.SuicideShortOfROT controls whether the projectile will explode when it detected its insufficient turning ability.

      Trajectory.Missile.SuicideShortOfROT 控制抛射体是否在发现转向能力不足时自爆。

In rulesmd.ini:

Trajectory.Missile.UniqueCurve=false        ; boolean
Trajectory.Missile.PreAimCoord=0,0,0        ; integer - Forward,Lateral,Height
Trajectory.Missile.FacingCoord=false        ; boolean
Trajectory.Missile.ReduceCoord=true         ; boolean
Trajectory.Missile.LaunchSpeed=0            ; floating point value
Trajectory.Missile.Acceleration=10.0        ; floating point value
Trajectory.Missile.TurningSpeed=10.0        ; floating point value
Trajectory.Missile.LockDirection=false      ; boolean
Trajectory.Missile.CruiseEnable=false       ; boolean
Trajectory.Missile.CruiseUnableRange=5.0    ; floating point value
Trajectory.Missile.CruiseAltitude=800       ; integer
Trajectory.Missile.CruiseAlongLevel=false   ; boolean
Trajectory.Missile.SuicideAboveRange=-3.0   ; floating point value
Trajectory.Missile.SuicideShortOfROT=false  ; boolean

  • The trajectory can be affected by Gravity. If you are sure that you do not need it to be affected by it, you can set Gravity=0 separately.

    这种轨迹会受到 Gravity 的影响,如果你不希望其受到它的影响,你可以单独为其设置 Gravity=0

  • If the value of Trajectory.Missile.CruiseUnableRange is too small, it may cause the projectile to be permanently stay in cruise mode.

    如果 Trajectory.Missile.CruiseUnableRange 过小,则可能导致抛体永久处在巡航状态。



Engrave trajectory

  • Visually, like the thermal lance. Calling it 'trajectory' may not be appropriate. It does not read the settings on the weapon.

    从视觉上看,就像是热能射线枪。称之为 “轨迹” 也许并不合适。它不会读取武器上的设置。

    • Trajectory.Engrave.SourceCoord controls the starting point of engraving line segment. Taking the target as the coordinate center. Specifically, it will start from the firing position when set to 0,0 . The height of the point will always at ground level, unless the target is in the air.

      Trajectory.Engrave.SourceCoord 控制雕刻线段的起点。以目标为坐标中心。特别的是,当设置为 0,0 时,它将从开火位置开始。除非目标在空中,否则点的高度将始终位于地面。

    • Trajectory.Engrave.TargetCoord controls the end point of engraving line segment. If Trajectory.Duration is set to a positive number, it is only used for direction calculation. Taking the target as the coordinate center. The height of the point will always at ground level, unless the target is in the air.

      Trajectory.Engrave.TargetCoord 控制雕刻线段的终点。如果 Trajectory.Duration 设置为正数,它将仅用于方向计算。以目标为坐标中心。除非目标在空中,否则点的高度将始终位于地面。

    • Trajectory.Engrave.IsLaser controls whether laser drawing is required.

      Trajectory.Engrave.IsLaser 控制是否绘制切割激光。

      • Trajectory.Engrave.IsIntense controls whether the engrave laser will be brighter and thicker.

        Trajectory.Engrave.IsIntense 控制切割激光是否会更亮更粗。

      • Trajectory.Engrave.IsHouseColor controls whether set the engrave laser to draw using player's house color.

        Trajectory.Engrave.IsHouseColor 控制切割激光是否使用玩家阵营颜色。

      • Trajectory.Engrave.IsSingleColor controls whether set the engrave laser to draw using only Trajectory.Engrave.LaserInnerColor.

        Trajectory.Engrave.IsSingleColor 控制切割激光的绘制是否只使用 Trajectory.Engrave.LaserInnerColor

      • Trajectory.Engrave.LaserInnerColor controls the inner color of the engrave laser.

        Trajectory.Engrave.LaserInnerColor 控制切割激光的内部颜色

      • Trajectory.Engrave.LaserOuterColor controls the outer color of the engrave laser.

        Trajectory.Engrave.LaserOuterColor 控制切割激光的外围颜色

      • Trajectory.Engrave.LaserOuterSpread controls the spread color of the engrave laser.

        Trajectory.Engrave.LaserOuterSpread 控制切割激光的扩散颜色

      • Trajectory.Engrave.LaserThickness controls the thickness of the engrave laser.

        Trajectory.Engrave.LaserThickness 控制切割激光的粗细程度。

      • Trajectory.Engrave.LaserDuration controls the duration of the engrave laser.

        Trajectory.Engrave.LaserDuration 控制切割激光的持续时间

      • Trajectory.Engrave.LaserDelay controls how often to draw the engrave laser.

        Trajectory.Engrave.LaserDelay 控制切割激光多久绘制一次。

    • Trajectory.Engrave.AttachToTarget controls whether the center position of the engrave laser will update with the target position.

      Trajectory.Engrave.AttachToTarget 控制切割激光的中心位置是否随目标位置变化。

    • Trajectory.Engrave.UpdateDirection controls whether the engrave laser updates the direction with the firer and target position.

      Trajectory.Engrave.UpdateDirection 控制切割激光是否随发射者和目标位置更新角度。

In rulesmd.ini:

Trajectory.Engrave.SourceCoord=0,0         ; integer - Forward,Lateral
Trajectory.Engrave.TargetCoord=0,0         ; integer - Forward,Lateral
Trajectory.Engrave.IsLaser=true            ; boolean
Trajectory.Engrave.IsIntense=false         ; boolean
Trajectory.Engrave.IsHouseColor=false      ; boolean
Trajectory.Engrave.IsSingleColor=false     ; boolean
Trajectory.Engrave.LaserInnerColor=0,0,0   ; integer - Red,Green,Blue
Trajectory.Engrave.LaserOuterColor=0,0,0   ; integer - Red,Green,Blue
Trajectory.Engrave.LaserOuterSpread=0,0,0  ; integer - Red,Green,Blue
Trajectory.Engrave.LaserThickness=3        ; integer
Trajectory.Engrave.LaserDuration=1         ; integer
Trajectory.Engrave.LaserDelay=1            ; integer, game frames
Trajectory.Engrave.AttachToTarget=false    ; boolean
Trajectory.Engrave.UpdateDirection=false   ; boolean

  • It's best not to let it be intercepted.

    最好不要设置让其可被拦截。

  • Trajectory.Engrave.IsIntense and Trajectory.Engrave.LaserThickness require set Trajectory.Engrave.IsHouseColor or Trajectory.Engrave.IsSingleColor to true to take effect.

    Trajectory.Engrave.IsIntenseTrajectory.Engrave.LaserThickness 要求启用 Trajectory.Engrave.IsHouseColorTrajectory.Engrave.IsSingleColor 才能生效。



  • Directly using the laser drawing in Trajectory=Engrave with Trajectory.PassDetonateWarhead is more cost-effective than using Trajectory.DisperseWeapons. If you need the laser to be blocked by the Fire Storm Wall, you can try using the latter.

    直接使用 Trajectory=Engrave 中的激光绘制并使用 Trajectory.PassDetonateWarhead 相比使用 Trajectory.DisperseWeapons 性能更高效,如果你希望激光会被火风暴墙阻挡,你可以尝试使用后者。

  • The default value of Trajectory.PeacefulVanish will be changed when using this type of trajectory.

    Trajectory.PeacefulVanish 的默认值在使用此类轨迹抛射体时会被改变。



Parabola trajectory

  • As the name says, this is a completely reset Arcing with different enhanced functions.

    顾名思义,这是一种完全重置且拥有不同增强功能的类 Arcing 轨迹。

    • Trajectory.Parabola.OpenFireMode controls how should the projectile be launched. This has the following 6 modes.

      Trajectory.Parabola.OpenFireMode 控制抛射体应该如何发射,它有如下 6 种模式:

      • Speed - Automatic calculation mode with fixed horizontal velocity, using Trajectory.Speed and target coordinates as calculation conditions.

        Speed - 固定水平速度的自动计算模式,使用 Trajectory.Speed 和目标坐标作为计算条件。

      • Height - Automatic calculation mode with fixed maximum height, useing Trajectory.Parabola.ThrowHeight and target coordinates as calculation conditions, i.e. the detonation time of the projectile is relatively fixed.

        Height - 固定最大高度的自动计算模式,使用 Trajectory.Parabola.ThrowHeight 和目标坐标作为计算条件,即抛射体的引爆时间将相对固定。

      • Angle - Automatic calculation mode with fixed fire angle, useing Trajectory.Parabola.LaunchAngle and target coordinates as calculation conditions.

        Angle - 固定开火角度的自动计算模式,使用 Trajectory.Parabola.LaunchAngle 和目标坐标作为计算条件。

      • SpeedAndHeight - Fixed horizontal velocity and maximum height mode, using Trajectory.Speed and Trajectory.Parabola.ThrowHeight as calculation conditions, i.e. the trajectory will only undergo altitude changes with the height of the target.

        SpeedAndHeight - 固定水平速度和最大高度模式,使用 Trajectory.SpeedTrajectory.Parabola.ThrowHeight 作为计算条件,即抛射体将只进行高度变化,高度随目标高度变化。

      • HeightAndAngle - Fixed maximum height and fire angle mode, using Trajectory.Parabola.ThrowHeight and Trajectory.Parabola.LaunchAngle as calculation conditions, i.e. the trajectory will change horizontally with the height of the target.

        HeightAndAngle - 固定最大高度和开火角度模式,使用 Trajectory.Parabola.ThrowHeightTrajectory.Parabola.LaunchAngle 作为计算条件,即抛射体将随高度变化而水平变化。

      • SpeedAndAngle - Fixed horizontal velocity and fire angle mode, using Trajectory.Speed and Trajectory.Parabola.LaunchAngle as calculation conditions, i.e. the trajectory will be permanently fixed.

        SpeedAndAngle - 固定水平速度和开火角度模式,使用 Trajectory.SpeedTrajectory.Parabola.LaunchAngle 作为计算条件,即抛射体轨迹恒定。

    • Trajectory.Parabola.ThrowHeight controls the maximum height of the projectile and is only used for modes Height, SpeedAndHeight, or HeightAndAngle. The specific height will be determined by taking the larger of the launch height and the target height then increasing this value. Non positive numbers are not supported.

      Trajectory.Parabola.ThrowHeight 控制抛射体的最大高度,仅用于模式 HeightSpeedAndHeightHeightAndAngle。具体高度将由取发射高度和目标高度中的较大值然后增加此值决定。不支持非正数。

    • Trajectory.Parabola.LaunchAngle controls the fire angle of the projectile and is only used for modes Angle, HeightAndAngle, or SpeedAndAngle. Only supports -90.0 ~ 90.0 (Cannot use boundary values) in Mode Angle or SpeedAndAngle, and 0.0 ~ 90.0 (Cannot use boundary values) in Mode HeightAndAngle.

      Trajectory.Parabola.LaunchAngle 控制抛射体的开火角度,仅用于模式 AngleHeightAndAngleSpeedAndAngle。在模式 AngleSpeedAndAngle 中仅支持 -90.0 ~ 90.0(不能使用边界值),在模式 HeightAndAngle 中则是 0.0 ~ 90.0(不能使用边界值)。

    • Trajectory.Parabola.DetonationAngle controls when the angle between the projectile in the current velocity direction and the horizontal plane is less than this value, it will detonate prematurely. Taking effect when the value is at -90.0 ~ 90.0 (Cannot use boundary values).

      Trajectory.Parabola.DetonationAngle 控制当抛射体当前速度方向与水平方向的角度小于此值时,它将提前引爆。当值为 -90.0 ~ 90.0(不能使用边界值)时生效。

    • Trajectory.Parabola.BounceTimes controls how many times can it bounce back when the projectile hits the ground or cliff. Be aware that excessive projectile speed may cause abnormal operation.

      Trajectory.Parabola.BounceTimes 控制抛射体在击中地面或悬崖时可以弹跳的次数。注意过高的抛射体速度可能导致异常操作。

      • Trajectory.Parabola.BounceOnTarget controls the projectile can bounce on which cells or technos.

        Trajectory.Parabola.BounceOnTarget 控制抛射体能在哪些格子和单位上弹跳。

      • Trajectory.Parabola.BounceOnHouses controls the projectile can bounce on whose technos.

        Trajectory.Parabola.BounceOnHouses 控制抛射体能在哪些所属的单位上弹跳。

      • Trajectory.Parabola.BounceDetonate controls whether it detonates the warhead once extra during each bounce.

        Trajectory.Parabola.BounceDetonate 控制是否在每次弹跳时额外引爆一次弹头。

      • Trajectory.Parabola.BounceAttenuation controls the attenuation coefficient of projectile bounce damage, that is, how many times the next damage after each bounce is the damage just caused. This will also affect the damage of the final detonation.

        Trajectory.Parabola.BounceAttenuation 控制抛射体弹跳伤害的衰减系数,即每次造成伤害后的下一次伤害会是多少倍。这也将影响最终引爆的伤害。

      • Trajectory.Parabola.BounceCoefficient controls the attenuation coefficient of projectile bounce elasticity, that is, how many times the speed after each bounce is the speed before bouncing.

        Trajectory.Parabola.BounceCoefficient 控制抛射体弹跳弹性的衰减系数,即每次弹跳后的速度会是多少倍。

In rulesmd.ini:

Trajectory.Parabola.OpenFireMode=Speed     ; ParabolaFireMode value enumeration (Speed|Height|Angle|SpeedAndHeight|HeightAndAngle|SpeedAndAngle)
Trajectory.Parabola.ThrowHeight=600        ; integer
Trajectory.Parabola.LaunchAngle=30         ; floating point value
Trajectory.Parabola.DetonationAngle=-90.0  ; floating point value
Trajectory.Parabola.BounceTimes=0          ; integer
Trajectory.Parabola.BounceOnTarget=land    ; List of Affected Target Enumeration (none|land|water|empty|infantry|units|buildings|all)
Trajectory.Parabola.BounceOnHouses=all     ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all)
Trajectory.Parabola.BounceDetonate=false   ; boolean
Trajectory.Parabola.BounceAttenuation=0.8  ; floating point value
Trajectory.Parabola.BounceCoefficient=0.8  ; floating point value

  • If Trajectory.Parabola.OpenFireMode=Angle, the performance consumption is high, and may have no solution. It is not recommended to enable SubjectToCliffs or enable AA with a smaller MinimumRange when using this mode.

    Trajectory.Parabola.OpenFireMode=Angle 模式下,性能消耗较高,且由于可能攻击目标过高而无解,不建议使用此模式时启用 SubjectToCliffs 或启用 AA 并使用较小的 MinimumRange



  • Be aware that Trajectory.DetonationDistance do not conflict with Trajectory.Parabola.BounceTimes and will take effect simultaneously. So if you want to explode the bullet only after the times of bounces is exhausted, you should set Trajectory.DetonationDistance to a non positive value.

    注意 Trajectory.DetonationDistanceTrajectory.Parabola.BounceTimes 并不冲突会同时生效。所以如果需要抛体只会在弹跳次数耗尽后再爆炸,请将 Trajectory.DetonationDistance 设置为非正数。



Tracing trajectory

  • A trajectory that can keep following the target and only detonate when its survival time is exhausted.

    一种可以继续跟随目标并仅在其生存时间耗尽时引爆的轨迹。

    • Trajectory.Tracing.TraceMode controls how should the projectile trace the target. This is used to calculate coordinate axis of Trajectory.Tracing.AttachCoord located on the tracking target. The H axis is not affected by the tilt and deflection of the tracking target, and always faces directly above. This has the following 6 modes.

      Trajectory.Tracing.TraceMode 控制抛射体的追踪模式。这用于计算 Trajectory.Tracing.AttachCoord 的坐标轴,原点位于追踪目标上,H 轴不随追踪目标的倾斜偏转影响,永远朝向正上方。它有如下 6 种模式:

      • Connection - Line vector. Take the horizontal component of the vector between the launch position and the target position as the F axis.

        Connection - 连线向量。以发射位置与目标位置之间向量的水平分量为 F 轴。

      • Global - Map direction. Take the lower right side of the map as the F axis.

        Global - 地图方向。以地图的右下方方向为 F 轴。

      • Body - Follow the body. The F axis is the body orientation of the tracking target.

        Body - 跟随身体。以追踪目标的身体朝向为 F 轴。

      • Turret - Follow the turret. The F axis is the turret orientation of the tracking target.

        Turret - 跟随炮塔。以追踪目标的炮塔朝向为 F 轴。

      • RotateCW - Rotate clockwise. Rotate clockwise around the H axis with the resultant offset in the FL direction as the radius.

        RotateCW - 顺时针旋转。以 FL 方向偏移的合成量为半径,绕 H 轴顺时针旋转。

      • RotateCCW - Rotate counterclockwise. Rotate counterclockwise around the H axis with the resultant offset in the FL direction as the radius.

        RotateCCW - 逆时针旋转。以 FL 方向偏移的合成量为半径,绕 H 轴逆时针旋转。

    • Trajectory.Tracing.TrackTarget controls whether the target tracked by the projectile is the target of the projectile. Otherwise, it will trace the firer, and at the same time, the projectile will detonate if the firer dies.

      Trajectory.Tracing.TrackTarget 控制抛射体追踪的目标是否为抛射体的目标,否则为追踪发射者,同时当发射者死亡时抛射体也会一同引爆。

    • Trajectory.Tracing.CreateAtTarget controls whether the projectile is directly generated at the target position.

      Trajectory.Tracing.CreateAtTarget 控制抛射体是否在目标位置生成。

    • Trajectory.Tracing.StableRotation controls whether the projectile will automatically rotate at the same angle interval when Trajectory.Tracing.TraceMode is RotateCW or RotateCCW.

      Trajectory.Tracing.StableRotation 控制当抛射体的追踪模式为 RotateCWRotateCCW 时,是否会减慢旋转速度并自动计算相互之间的夹角。

    • Trajectory.Tracing.CreateCoord controls the generate position. Not related to Trajectory.Tracing.TraceMode.

      Trajectory.Tracing.CreateCoord 控制抛射体的生成位置,与 Trajectory.Tracing.TraceMode 无关。

    • Trajectory.Tracing.AttachCoord controls the tracing position on its target, use Trajectory.Tracing.TraceMode determines the specific location.

      Trajectory.Tracing.AttachCoord 控制抛射体的追踪位置,由 Trajectory.Tracing.TraceMode 决定具体位置。

    • Trajectory.Tracing.ChasableDistance controls the maximum distance between the target's center of the projectile pursuing and the firer's center, the distance will not exceed this value. When the firer dies, if it is a positive number, it will peacefully vanish. And if it is a negative number, the projectile will explode. When it is zero, the weapon's range will be used and considered a positive number.

      Trajectory.Tracing.ChasableDistance 控制抛射体追击目标的中心与发射者中心之间的最大距离,当发射者死亡时,若为正数,抛射体会平静地消失,若为负数,抛射体将被引爆,当为零时,使用武器射程并视为正数。

In rulesmd.ini:

Trajectory.Tracing.TraceMode=Connection  ; TraceMode value enumeration (Connection|Global|Body|Turret|RotateCW|RotateCCW)
Trajectory.Tracing.TrackTarget=true      ; boolean
Trajectory.Tracing.CreateAtTarget=false  ; boolean
Trajectory.Tracing.StableRotation=false  ; boolean
Trajectory.Tracing.CreateCoord=0,0,0     ; integer - Forward,Lateral,Height
Trajectory.Tracing.AttachCoord=0,0,0     ; integer - Forward,Lateral,Height
Trajectory.Tracing.ChasableDistance=0    ; floating point value

  • Trajectory.Tracing.StableRotation need to cooperate with Trajectory.CreateCapacity records to take effect.

    Trajectory.Tracing.StableRotation 需要 Trajectory.CreateCapacity 配合进行进行记录才能生效。



Summary by CodeRabbit

  • New Features

    • Introduced a comprehensive, customizable projectile trajectory system with new trajectory types: Missile, Engrave, and Tracing.
    • Added extensive configuration options for projectile behavior, including advanced retargeting, detonation, proximity effects, and weapon dispersal.
    • Enhanced visual effects such as engraving lasers and tracing projectiles.
    • Improved serialization and save/load support for new trajectory and projectile states.
  • Bug Fixes

    • Refined projectile and weapon logic to ensure proper handling of trajectory flags, firing conditions, and environmental interactions.
  • Documentation

    • Greatly expanded and restructured documentation, providing detailed guides, configuration examples, and feature support matrices for the new trajectory system.
    • Updated changelogs to reflect new features and naming conventions.
  • Chores

    • Updated credits to acknowledge new contributors and features related to projectile trajectories.

Copy link

github-actions bot commented Mar 20, 2025

Nightly build for this pull request:

This comment is automatic and is meant to allow guests to get latest nightly builds for this pull request without registering. It is updated on every successful build.

@Coronia
Copy link
Contributor

Coronia commented Mar 20, 2025

Again, I would concern its backward compatibility. Since Straight, Bombard and Parabola are released logic, there might be modders using it already. Requiring them to modify existing codes is one thing, and those released mods and maps might even not have the chance to modify. I think the 'interface protocol', which means tags in the context of Phobos, should still be compatible when modifying existing functions

For trajectory, we can simply allow these existing 3 projectiles to read previous tags and overwrite the new variables. For example, if Trajectory.Straight.LeadTimeCalculate is already set for a straight trajectory, it should be used to overwrite Trajectory.LeadTimeCalculate. This would be an easy adjustment as we only need to add a few more ini parsing lines

e.g. in StraightTrajectory.cpp

this->LeadTimeCalculate.Read(exINI, pSection, "Trajectory.Straight.LeadTimeCalculate");

@mevitar
Copy link

mevitar commented Mar 20, 2025

I don't think we need to worry about backwards compatibility with this. Like, are there really that many mods that released between changes to Straight and now that aren't developed anymore?
And if they aren't developed, people should not switch to a newer Phobos.dll in the first place!

Everyone else can just update their code to fit the new logic. Just remember to note it in the What's New section (and i guess in the Migration Ulitity? not sure how that one even works, i never used it tbh).

@Metadorius
Copy link
Member

For most renames Migration Utility exists and is a valid option. It searches and replaces en masse using regex. Basically it's automated search and replace tool.

As such any simple tag renames should be put into the migration tool.

@Starkku
Copy link
Contributor

Starkku commented Mar 20, 2025

This PR is pretty much aiming to remove the sort of redundancy that having same value be parsed from multiple keys would add back so that's just not a good idea.

Breaking backwards compatibility is never ideal but it is acceptable in some cases with appropriate migration notice and additions to migration scripts. There will be people who are confused by the changes regardless, whether because they do not read the migration notices or something else, but this one corrects an issue that has potential to snowball into something bigger if even more trajectory types are added in future. In future any new additions that cover or have potential to apply to multiple trajectory types should follow same precedent.

@DeathFishAtEase
Copy link
Collaborator

Perhaps compatibility issues could be addressed through the key-value pair reading process, similar to how Ares' Ripple.Radius and IonCannon.Ripple handle it.

@Metadorius
Copy link
Member

Perhaps compatibility issues could be addressed through the key-value pair reading process, similar to how Ares' Ripple.Radius and IonCannon.Ripple handle it.

Why won't migration tool work?

@DeathFishAtEase
Copy link
Collaborator

DeathFishAtEase commented Mar 20, 2025

Why won't migration tool work?

I am considering the situation described by Starkku.

There will be people who are confused by the changes regardless, whether because they do not read the migration notices or something else


Perhaps compatibility issues could be addressed through the key-value pair reading process, similar to how Ares' Ripple.Radius and IonCannon.Ripple handle it.

However, this effectively reduces the workload for users to perform batch text modifications as well as the need to train users in PowerShell scripting.

@Metadorius
Copy link
Member

I am considering the situation described by Starkku.

There will be people who are confused by the changes regardless, whether because they do not read the migration notices or something else

I think the point was that there will be people like that no matter what you do, so there's no point in inventing something like that and the situation is fine.

@DeathFishAtEase
Copy link
Collaborator

there will be people like that no matter what you do

Well, you're right. :P

@CrimRecya
Copy link
Contributor Author

I mainly integrated functions that can be generalized and also created some new universal functions. Meanwhile, I used inheritance relationships to distinguish between two major categories of trajectories, they also have their own relatively independent functional differences. I have added a large number of comments and also updated the sample for creating new type in the future.
But since I don't have my own mod, I'm not quite sure how to present vivid GIFs in the doc in the most appropriate way. This is quite frustrating.

Regarding the key names, I also thought that there would be no need to add an extra paragraph for the parts that can be shared. A short name indicates its universality while avoiding overly lengthy names.
Actually, I have received some feedback that told me that when the trajectory type increases, the key names become quite lengthy, seems unnecessary because they only change in the middle.
At present, there are only three types that have been merged, and they were all just done in the previous version. I think it is not so complicated even manually replacing them with regular expressions like (Trajectory)(\.).+\.(Proximity.+=) -> $1$2$3.

@TaranDahl TaranDahl added ❓New feature ⚙️T2 T2 maintainer review is sufficient labels Mar 22, 2025
@Coronia Coronia added Needs testing Major Big stuff to do labels Mar 22, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 17

♻️ Duplicate comments (1)
src/Ext/Bullet/Trajectories/TracingTrajectory.cpp (1)

61-66: Avoid const_cast in Save methods.

Same issue as in VirtualTrajectory - using const_cast violates const-correctness.

Also applies to: 101-106

🧹 Nitpick comments (34)
src/Ext/Bullet/Hooks.cpp (1)

341-344: Consider uncommenting the detailed trajectory checks.

The commented-out code provides more explicit trajectory type checks which could be beneficial for maintainability and clarity. The current implementation with flag != TrajectoryFlag::Invalid works but is less specific about which trajectories support snap behavior.

 constexpr bool CheckTrajectoryCanNotAlwaysSnap(const TrajectoryFlag flag)
 {
-	return flag != TrajectoryFlag::Invalid;
-/*	return flag == TrajectoryFlag::Straight
+	return flag == TrajectoryFlag::Straight
 		|| flag == TrajectoryFlag::Bombard
 		|| flag == TrajectoryFlag::Missile
 		|| flag == TrajectoryFlag::Engrave
 		|| flag == TrajectoryFlag::Parabola
-		|| flag == TrajectoryFlag::Tracing;*/
+		|| flag == TrajectoryFlag::Tracing;
 }
src/Ext/BulletType/Body.cpp (1)

55-55: Consider aligning property name with INI key.

The property SubjectToSolid is loaded from INI key "SubjectToBuildings". While the implementation is correct, consider renaming the property to SubjectToBuildings for consistency and clarity.

-	this->SubjectToSolid.Read(exINI, pSection, "SubjectToBuildings");
+	this->SubjectToBuildings.Read(exINI, pSection, "SubjectToBuildings");

And update the property declaration and serialization accordingly.

Also applies to: 171-171

src/Ext/Bullet/Trajectories/PhobosVirtualTrajectory.h (2)

19-21: Remove or implement the commented Read method.

The commented Read method should either be implemented or removed entirely. Keeping commented code creates confusion about the intended design.


43-43: Consider using const parameter if techno is not modified.

The InvalidFireCondition method takes a non-const TechnoClass* parameter. If this method doesn't modify the techno, consider changing it to const TechnoClass* for better const-correctness.

src/Ext/Bullet/Trajectories/PhobosVirtualTrajectory.cpp (2)

28-38: Remove large block of commented code.

This commented implementation should be removed from the codebase. If the functionality is needed later, it can be retrieved from version control.


89-97: Extract magic number and simplify angle calculation.

The magic number 4096 represents a 45-degree threshold and should be a named constant. Also, the multiple casts to short seem redundant.

+    static constexpr short FACING_TOLERANCE = 4096; // 45 degrees in DirStruct units
+    
     const auto tgtDir = DirStruct(-PhobosTrajectory::Get2DOpRadian(pTechno->GetCoords(), this->Bullet->TargetCoords));
     const auto& face = pTechno->HasTurret() && pTechno->WhatAmI() == AbstractType::Unit ? pTechno->SecondaryFacing : pTechno->PrimaryFacing;
     const auto curDir = face.Current();
     // Similar to the vanilla 45 degree turret facing check design
-    return (std::abs(static_cast<short>(static_cast<short>(tgtDir.Raw) - static_cast<short>(curDir.Raw))) >= 4096);
+    return (std::abs(static_cast<short>(tgtDir.Raw - curDir.Raw)) >= FACING_TOLERANCE);
src/Ext/Bullet/Trajectories/TracingTrajectory.cpp (2)

184-187: Document or extract magic numbers in duration calculation.

The calculation pWeapon->ROF - 10 uses an unexplained magic number. Consider documenting why 10 frames are subtracted or extracting it as a named constant.

-        this->DurationTimer.Start((pWeapon->ROF > 10) ? pWeapon->ROF - 10 : 1);
+        constexpr int ROF_OFFSET = 10; // Frames to subtract from ROF for trajectory duration
+        this->DurationTimer.Start((pWeapon->ROF > ROF_OFFSET) ? pWeapon->ROF - ROF_OFFSET : 1);

190-331: Consider refactoring this complex method.

The ChangeVelocity method is over 140 lines with deeply nested logic. Consider extracting the trace mode handling into separate methods for better maintainability.

src/Ext/Bullet/Trajectories/AdditionalWarheads.cpp (2)

77-299: Consider refactoring this complex function into smaller, more manageable pieces.

This function spans over 200 lines with deeply nested logic and multiple state variables (leftNext, rightNext, leftSkip, rightSkip, leftContinue, rightContinue). Breaking it down into smaller helper functions would improve readability and maintainability.

Consider extracting the following logical sections into separate methods:

  • Straight line handling (lines 84-119)
  • Left boundary traversal logic
  • Right boundary traversal logic
  • Center filling logic

This would make the algorithm easier to understand and test.


663-664: Consider using a named constant for the first cell distance.

The magic number 256.0 represents the first cell distance but this isn't immediately clear from the code.

+constexpr double FIRST_CELL_DISTANCE = 256.0; // Leptons distance of one cell
 // Remove the first cell distance for calculation
-if (distance > 256.0)
-  damageMult += (this->GetType()->DamageEdgeAttenuation - 1.0) * ((distance - 256.0) / (this->AttenuationRange - Unsorted::LeptonsPerCell));
+if (distance > FIRST_CELL_DISTANCE)
+  damageMult += (this->GetType()->DamageEdgeAttenuation - 1.0) * ((distance - FIRST_CELL_DISTANCE) / (this->AttenuationRange - Unsorted::LeptonsPerCell));
src/Ext/Bullet/Trajectories/PhobosActualTrajectory.h (2)

34-34: Remove or implement the commented Read method.

The commented virtual Read method suggests incomplete implementation. Either implement it or remove the comment to avoid confusion.


51-51: Track the TODO for future improvement.

The TODO comment indicates a known issue with frame delay and turret orientation that should be addressed.

Would you like me to create an issue to track this TODO item for future implementation?

docs/New-or-Enhanced-Logics.md (6)

807-814: Clarify “Trajectory.Speed” semantics per-type

These bullets currently mix “refers to” and “is used for” jargon, and do not tell the reader which unit the value is in (cells / frame).
Consider rewriting to a tabular form and explicitly stating the unit once, e.g.

| Trajectory | Meaning of `Trajectory.Speed` | Unit |
| ---------- | ---------------------------- | ---- |
| Straight   | Constant horizontal speed    | leptons / frame |

The same re-structure will shrink eight verbose bullet items into one glanceable table.


815-818: “Duration” default rules read ambiguously

The phrase “If it is a non positive number, there will be no timing” can be parsed two ways:

  1. duration ≤ 0 ➜ infinite lifetime
  2. duration < 0 ➜ infinite, 0 ➜ immediate detonation

Please disambiguate and adopt the same wording used later (“negative → infinite, zero → …”).


819-828: Inconsistent casing and missing article in bullet-facing modes

  • “Spin - Continuously rotating itself on a horizontal plane.” → “… around the horizontal plane.”
  • “Stable - Static after launch …” → consider “Fixed” instead, to avoid conflating with aircraft stability.

Also, bullet names use mixed casing (Velocity, Spin, Stable, Target) vs lower-case elsewhere (velocity in INI snippets). Pick one style for docs and INI to avoid confusion.


834-838: Default value sentence is hard to parse

Current:
Trajectory.PeacefulVanish … “The default value is Trajectory=Engrave or Trajectory.ProximityImpact not equal to 0 or Trajectory.DisperseCycle not equal to 0.”

Proposed:

If not set, it defaults to true when any of the following is true:
• the trajectory type is Engrave
Trajectory.ProximityImpact ≠ 0
Trajectory.DisperseCycle ≠ 0


845-846: Typo: “projectile need to”

Use third-person singular:

- controls whether the projectile need to calculate
+ controls whether the projectile needs to calculate

Same typo occurs in bullet right below (“projectile to predict”).


846-853: Straight trajectory sub-note duplicates earlier definition

Lines 846-848 re-explain the sign semantics of DetonationDistance. This duplicates the earlier generic definition and risks drifting. Suggest linking back (“See general note above”) instead of repeating.

src/Ext/Bullet/Trajectories/PhobosActualTrajectory.cpp (1)

120-139: Consider using enum for WaitOneFrame states

The WaitOneFrame member uses magic numbers (0, 1, 2) to track state. Consider using an enum for better readability and maintainability.

+enum class BulletPrepareState : int
+{
+    Ready = 0,
+    WaitingForTargetUpdate = 1,
+    InitialFrame = 2
+};

Then update the logic to use the enum values instead of magic numbers.

src/Ext/Bullet/Trajectories/MissileTrajectory.h (1)

5-5: Remove unnecessary include

The #include <Ext/WeaponType/Body.h> appears to be unused in this header file. If it's only needed in the implementation file, move it there to reduce compilation dependencies.

src/Ext/Bullet/Trajectories/StraightTrajectory.cpp (1)

321-322: Consider extracting the magic number.

The cliff height threshold of 384 appears here without the explanatory comment present in ParabolaTrajectory.h. Consider defining a named constant or adding a comment for consistency.

-	if (std::abs(checkDifference) >= 384 && pBullet->Type->SubjectToCliffs)
+	// 384 = 4 * Unsorted::LevelHeight - 32 (error range)
+	if (std::abs(checkDifference) >= 384 && pBullet->Type->SubjectToCliffs)
src/Ext/Bullet/Trajectories/EngraveTrajectory.cpp (1)

269-270: Magic number should be documented.

Similar to other trajectories, the cliff height threshold of 384 should have an explanatory comment.

 	const auto checkDifference = this->GetFloorCoordHeight(futureCoords) - futureCoords.Z;
-	// When crossing the cliff, directly move the position of the bullet, otherwise change the vertical velocity (384 -> (4 * Unsorted::LevelHeight - 32(error range)))
+	// When crossing the cliff, directly move the position of the bullet, otherwise change the vertical velocity
+	// 384 = 4 * Unsorted::LevelHeight - 32 (error range)
src/Ext/Bullet/Trajectories/PhobosTrajectory.h (1)

287-287: Minor formatting: Add space after comma

-return Math::atan2(target.Y - source.Y , target.X - source.X);
+return Math::atan2(target.Y - source.Y, target.X - source.X);
src/Ext/Bullet/Trajectories/BombardTrajectory.cpp (2)

67-68: Improve FallSpeed validation

The FallSpeed validation only occurs when isset() returns true. Consider always validating the value after reading to ensure it's never invalid.

this->FallSpeed.Read(exINI, pSection, "Trajectory.Bombard.FallSpeed");
-if (this->FallSpeed.isset()) this->FallSpeed = Math::max(0.001, this->FallSpeed);
+if (this->FallSpeed.isset() && this->FallSpeed.Get() <= 0)
+    this->FallSpeed = 0.001;

112-125: Document special DetonationDistance behavior

The code implements special behavior when DetonationDistance <= -1e-10, changing the target to the ground cell. This behavior should be documented in the trajectory documentation or as a comment here.

Add a comment explaining this special case:

// Special case: Set the target to the ground
+// When DetonationDistance is negative, the projectile targets the ground cell instead of the unit
if (this->Type->DetonationDistance.Get() <= -1e-10)
src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp (6)

135-135: Improve floating-point comparison

Using <= -1e-10 for checking negative values is unconventional. Consider using a more standard approach:

-	if (this->Type->DetonationDistance.Get() <= -1e-10)
+	if (this->Type->DetonationDistance.Get() < 0.0)

153-286: Consider refactoring this complex method

The OnVelocityCheck method is over 130 lines with deeply nested logic. Consider extracting the low-speed (lines 167-200) and high-speed (lines 202-272) checking logic into separate helper methods for better readability and maintainability.

Example refactoring approach:

bool ParabolaTrajectory::OnVelocityCheck()
{
    // ... gravity adjustment ...
    
    double ratio = 1.0;
    int velocityCheck = 0;
    
    if (velocity < Unsorted::LeptonsPerCell)
    {
        CheckLowSpeedCollisions(ratio, velocityCheck);
    }
    else
    {
        CheckHighSpeedCollisions(ratio, velocityCheck);
    }
    
    // ... final velocity adjustment ...
}

308-326: Add documentation for angle-based detonation logic

The angle-based detonation calculations involve complex trigonometry. Consider adding comments to explain the mathematical reasoning:

 	// Angle
 	if (std::abs(pType->DetonationAngle) < 1e-10)
 	{
+		// Zero angle means detonate when projectile starts falling
 		if (this->MovingVelocity.Z < 1e-10)
 			return TrajectoryCheckReturnType::Detonate;
 	}
 	else if (std::abs(pType->DetonationAngle) < 90.0)
 	{
 		const auto velocity = PhobosTrajectory::Get2DVelocity(this->MovingVelocity);
 
 		if (velocity > 1e-10)
 		{
+			// Check if current trajectory angle is less than detonation angle
+			// tan(angle) = vertical_velocity / horizontal_velocity
 			if ((this->MovingVelocity.Z / velocity) < Math::tan(pType->DetonationAngle * Math::Pi / 180.0))
 				return TrajectoryCheckReturnType::Detonate;
 		}

370-370: Simplify complex ternary expression

The rotation calculation uses a complex ternary that could be clearer:

-	const auto rotateRadian = this->Get2DOpRadian(((target == source && pBullet->Owner) ? pBullet->Owner->GetCoords() : source), target);
+	const auto& referenceCoords = (target == source && pBullet->Owner) ? pBullet->Owner->GetCoords() : source;
+	const auto rotateRadian = this->Get2DOpRadian(referenceCoords, target);

411-572: Extract mode-specific calculations for better organization

The CalculateBulletVelocityLeadTime method is over 160 lines with a large switch statement. Consider extracting each firing mode calculation into a separate method:

void ParabolaTrajectory::CalculateBulletVelocityLeadTime(const CoordStruct& source, const double gravity)
{
    // ... setup code ...
    
    switch (pType->OpenFireMode)
    {
    case ParabolaFireMode::Height:
        CalculateHeightModeVelocity(source, gravity, meetTime);
        break;
    case ParabolaFireMode::Angle:
        CalculateAngleModeVelocity(source, gravity, meetTime);
        break;
    // ... other cases ...
    }
}

This would improve readability and make the physics calculations easier to maintain and test.


694-694: Consider making iteration limit configurable

The Newton's method uses a fixed iteration limit of 10. For some edge cases, this might not be sufficient for convergence:

-	for (int i = 0; i < 10; ++i)
+	constexpr int MAX_ITERATIONS = 10;
+	for (int i = 0; i < MAX_ITERATIONS; ++i)

This makes it easier to adjust if convergence issues are encountered in practice.

src/Ext/Bullet/Trajectories/EngraveTrajectory.h (2)

23-34: Add documentation for laser configuration parameters

Consider adding documentation comments to explain the purpose and units of each parameter for better maintainability.

+	// Controls whether the projectile displays a laser beam
 	Valueable<bool> IsLaser;
+	// Makes the laser beam more visually intense/bright
 	Valueable<bool> IsIntense;
+	// Uses the firing house's color for the laser
 	Valueable<bool> IsHouseColor;
+	// Uses a single color instead of gradient
 	Valueable<bool> IsSingleColor;
+	// RGB color for the inner/core part of the laser beam
 	Valueable<ColorStruct> LaserInnerColor;
+	// RGB color for the outer part of the laser beam
 	Valueable<ColorStruct> LaserOuterColor;
+	// RGB spread/variance for the outer laser color
 	Valueable<ColorStruct> LaserOuterSpread;
+	// Visual thickness of the laser beam in pixels
 	Valueable<int> LaserThickness;
+	// Duration in frames that the laser remains visible
 	Valueable<int> LaserDuration;
+	// Delay in frames between laser pulses
 	Valueable<int> LaserDelay;
+	// Whether the laser beam follows/attaches to the target
 	Valueable<bool> AttachToTarget;
+	// Whether the laser beam updates its direction during flight
 	Valueable<bool> UpdateDirection;

47-81: Add virtual destructor for consistency

Although this class is marked final, adding a virtual destructor is good practice for consistency with the polymorphic hierarchy.

 class EngraveTrajectory final : public VirtualTrajectory
 {
 public:
 	EngraveTrajectory(noinit_t) { }
 	EngraveTrajectory(EngraveTrajectoryType const* trajType, BulletClass* pBullet)
 		: VirtualTrajectory(trajType, pBullet)
 		, Type { trajType }
 		, LaserTimer {}
 		, RotateRadian { 0 }
 	{ }
+
+	virtual ~EngraveTrajectory() = default;
src/Ext/Bullet/Trajectories/StraightTrajectory.h (1)

27-59: Add virtual destructor for consistency

Even though the class is final, adding a virtual destructor maintains consistency with the polymorphic hierarchy.

 class StraightTrajectory final : public ActualTrajectory
 {
 public:
 	StraightTrajectory(noinit_t) { }
 	StraightTrajectory(StraightTrajectoryType const* trajType, BulletClass* pBullet)
 		: ActualTrajectory(trajType, pBullet)
 		, Type { trajType }
 		, DetonationDistance { trajType->DetonationDistance }
 	{ }
+
+	virtual ~StraightTrajectory() = default;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e89e412 and 29b8cfc.

⛔ Files ignored due to path filters (18)
  • docs/_static/images/Trajectory-demo-A.gif is excluded by !**/*.gif
  • docs/_static/images/Trajectory-demo-B.gif is excluded by !**/*.gif
  • docs/_static/images/Trajectory-demo-C.gif is excluded by !**/*.gif
  • docs/_static/images/Trajectory-demo-D.gif is excluded by !**/*.gif
  • docs/_static/images/Trajectory-demo-E.gif is excluded by !**/*.gif
  • docs/_static/images/Trajectory-demo-F.gif is excluded by !**/*.gif
  • docs/_static/images/Trajectory-demo-G.gif is excluded by !**/*.gif
  • docs/_static/images/Trajectory-demo-I.gif is excluded by !**/*.gif
  • docs/_static/images/Trajectory-demo-M.gif is excluded by !**/*.gif
  • docs/_static/images/Trajectory-demo-N.gif is excluded by !**/*.gif
  • docs/_static/images/Trajectory-demo-O.gif is excluded by !**/*.gif
  • docs/_static/images/Trajectory-demo-P.gif is excluded by !**/*.gif
  • docs/_static/images/Trajectory-demo-R.gif is excluded by !**/*.gif
  • docs/_static/images/Trajectory-demo-S.gif is excluded by !**/*.gif
  • docs/_static/images/Trajectory-demo-T.gif is excluded by !**/*.gif
  • docs/_static/images/Trajectory-demo-U.gif is excluded by !**/*.gif
  • docs/_static/images/Trajectory-demo-V.gif is excluded by !**/*.gif
  • docs/_static/images/Trajectory-demo-W.gif is excluded by !**/*.gif
📒 Files selected for processing (38)
  • CREDITS.md (1 hunks)
  • Phobos.vcxproj (2 hunks)
  • docs/New-or-Enhanced-Logics.md (3 hunks)
  • docs/Whats-New.md (2 hunks)
  • src/Ext/Bullet/Body.cpp (3 hunks)
  • src/Ext/Bullet/Body.h (2 hunks)
  • src/Ext/Bullet/Hooks.DetonateLogics.cpp (1 hunks)
  • src/Ext/Bullet/Hooks.cpp (3 hunks)
  • src/Ext/Bullet/Trajectories/AdditionalWarheads.cpp (1 hunks)
  • src/Ext/Bullet/Trajectories/AdditionalWeapons.cpp (1 hunks)
  • src/Ext/Bullet/Trajectories/BombardTrajectory.cpp (7 hunks)
  • src/Ext/Bullet/Trajectories/BombardTrajectory.h (1 hunks)
  • src/Ext/Bullet/Trajectories/EngraveTrajectory.cpp (1 hunks)
  • src/Ext/Bullet/Trajectories/EngraveTrajectory.h (1 hunks)
  • src/Ext/Bullet/Trajectories/MissileTrajectory.cpp (1 hunks)
  • src/Ext/Bullet/Trajectories/MissileTrajectory.h (1 hunks)
  • src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp (6 hunks)
  • src/Ext/Bullet/Trajectories/ParabolaTrajectory.h (2 hunks)
  • src/Ext/Bullet/Trajectories/PhobosActualTrajectory.cpp (1 hunks)
  • src/Ext/Bullet/Trajectories/PhobosActualTrajectory.h (1 hunks)
  • src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp (6 hunks)
  • src/Ext/Bullet/Trajectories/PhobosTrajectory.h (2 hunks)
  • src/Ext/Bullet/Trajectories/PhobosVirtualTrajectory.cpp (1 hunks)
  • src/Ext/Bullet/Trajectories/PhobosVirtualTrajectory.h (1 hunks)
  • src/Ext/Bullet/Trajectories/SampleTrajectory.cpp (3 hunks)
  • src/Ext/Bullet/Trajectories/SampleTrajectory.h (2 hunks)
  • src/Ext/Bullet/Trajectories/StraightTrajectory.cpp (4 hunks)
  • src/Ext/Bullet/Trajectories/StraightTrajectory.h (1 hunks)
  • src/Ext/Bullet/Trajectories/TracingTrajectory.cpp (1 hunks)
  • src/Ext/Bullet/Trajectories/TracingTrajectory.h (1 hunks)
  • src/Ext/BulletType/Body.cpp (5 hunks)
  • src/Ext/BulletType/Body.h (2 hunks)
  • src/Ext/Techno/Body.cpp (1 hunks)
  • src/Ext/Techno/Body.h (3 hunks)
  • src/Ext/Techno/Hooks.Firing.cpp (4 hunks)
  • src/Phobos.Ext.cpp (1 hunks)
  • src/Utilities/EnumFunctions.cpp (1 hunks)
  • src/Utilities/SavegameDef.h (4 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (10)
src/Phobos.Ext.cpp (1)
src/Utilities/SavegameDef.h (1)
  • ClearSharedRegistry (27-27)
src/Ext/Bullet/Body.cpp (3)
src/Ext/Techno/Body.cpp (1)
  • ExtData (17-53)
src/Ext/Anim/Body.cpp (1)
  • ExtData (13-22)
src/Ext/Bullet/Body.h (1)
  • ExtData (53-55)
src/Ext/Techno/Hooks.Firing.cpp (1)
src/Ext/Techno/Body.Internal.cpp (2)
  • GetFLHAbsoluteCoords (38-72)
  • GetFLHAbsoluteCoords (38-38)
src/Ext/Bullet/Trajectories/PhobosActualTrajectory.cpp (3)
src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp (14)
  • Serialize (937-996)
  • Serialize (937-937)
  • Serialize (1011-1045)
  • Serialize (1011-1011)
  • Serialize (1058-1065)
  • Serialize (1058-1058)
  • OnUnlimbo (199-277)
  • OnUnlimbo (199-199)
  • OnEarlyUpdate (280-314)
  • OnEarlyUpdate (280-280)
  • OnPreDetonate (473-497)
  • OnPreDetonate (473-473)
  • RotateAboutTheAxis (616-628)
  • RotateAboutTheAxis (616-616)
src/Ext/Bullet/Trajectories/SampleTrajectory.cpp (4)
  • OnEarlyUpdate (80-83)
  • OnEarlyUpdate (80-80)
  • OnPreDetonate (118-131)
  • OnPreDetonate (118-118)
src/Ext/Bullet/Trajectories/PhobosTrajectory.h (1)
  • HorizontalRotate (297-300)
src/Ext/Bullet/Trajectories/StraightTrajectory.h (5)
src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp (38)
  • Load (114-127)
  • Load (114-114)
  • Load (142-181)
  • Load (142-142)
  • Load (924-928)
  • Load (924-924)
  • Load (998-1002)
  • Load (998-998)
  • Load (1047-1050)
  • Load (1047-1047)
  • Save (129-140)
  • Save (129-129)
  • Save (183-194)
  • Save (183-183)
  • Save (930-934)
  • Save (930-930)
  • Save (1004-1008)
  • Save (1004-1004)
  • Save (1052-1055)
  • Save (1052-1052)
  • Read (858-922)
  • Read (858-858)
  • Serialize (937-996)
  • Serialize (937-937)
  • Serialize (1011-1045)
  • Serialize (1011-1011)
  • Serialize (1058-1065)
  • Serialize (1058-1058)
  • OnUnlimbo (199-277)
  • OnUnlimbo (199-199)
  • OnVelocityCheck (317-450)
  • OnVelocityCheck (317-317)
  • OnDetonateUpdate (460-470)
  • OnDetonateUpdate (460-460)
  • OnPreDetonate (473-497)
  • OnPreDetonate (473-473)
  • OpenFire (500-531)
  • OpenFire (500-500)
src/Ext/Bullet/Trajectories/StraightTrajectory.cpp (34)
  • Load (20-25)
  • Load (20-20)
  • Load (60-65)
  • Load (60-60)
  • Save (27-32)
  • Save (27-27)
  • Save (67-72)
  • Save (67-67)
  • CreateInstance (6-9)
  • CreateInstance (6-6)
  • Read (34-49)
  • Read (34-34)
  • Serialize (12-18)
  • Serialize (12-12)
  • Serialize (52-58)
  • Serialize (52-52)
  • OnUnlimbo (74-97)
  • OnUnlimbo (74-74)
  • OnVelocityCheck (99-107)
  • OnVelocityCheck (99-99)
  • OnDetonateUpdate (109-129)
  • OnDetonateUpdate (109-109)
  • OnPreDetonate (131-143)
  • OnPreDetonate (131-131)
  • OpenFire (145-154)
  • OpenFire (145-145)
  • FireTrajectory (156-191)
  • FireTrajectory (156-156)
  • CalculateBulletLeadTime (193-251)
  • CalculateBulletLeadTime (193-193)
  • GetVelocityZ (253-298)
  • GetVelocityZ (253-253)
  • PassAndConfineAtHeight (300-342)
  • PassAndConfineAtHeight (300-300)
src/Utilities/Stream.h (2)
  • PhobosStreamReader (145-224)
  • PhobosStreamWriter (226-267)
src/Ext/Bullet/Trajectories/PhobosTrajectory.h (3)
  • TrajectoryFlag (258-267)
  • `` (477-477)
  • `` (492-492)
src/Ext/Bullet/Trajectories/PhobosActualTrajectory.h (2)
  • void (57-72)
  • ActualTrajectory (41-88)
src/Ext/Bullet/Trajectories/ParabolaTrajectory.h (7)
src/Utilities/Template.h (1)
  • Valueable (58-71)
src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp (40)
  • Load (114-127)
  • Load (114-114)
  • Load (142-181)
  • Load (142-142)
  • Load (924-928)
  • Load (924-924)
  • Load (998-1002)
  • Load (998-998)
  • Load (1047-1050)
  • Load (1047-1047)
  • Save (129-140)
  • Save (129-129)
  • Save (183-194)
  • Save (183-183)
  • Save (930-934)
  • Save (930-930)
  • Save (1004-1008)
  • Save (1004-1004)
  • Save (1052-1055)
  • Save (1052-1052)
  • Read (858-922)
  • Read (858-858)
  • Serialize (937-996)
  • Serialize (937-937)
  • Serialize (1011-1045)
  • Serialize (1011-1011)
  • Serialize (1058-1065)
  • Serialize (1058-1058)
  • OnUnlimbo (199-277)
  • OnUnlimbo (199-199)
  • OnVelocityCheck (317-450)
  • OnVelocityCheck (317-317)
  • OnDetonateUpdate (460-470)
  • OnDetonateUpdate (460-460)
  • OnPreDetonate (473-497)
  • OnPreDetonate (473-473)
  • OpenFire (500-531)
  • OpenFire (500-500)
  • MultiplyBulletVelocity (555-563)
  • MultiplyBulletVelocity (555-555)
src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp (40)
  • Load (61-66)
  • Load (61-61)
  • Load (114-119)
  • Load (114-114)
  • Save (68-73)
  • Save (68-68)
  • Save (121-126)
  • Save (121-121)
  • CreateInstance (39-42)
  • CreateInstance (39-39)
  • Read (75-100)
  • Read (75-75)
  • Serialize (45-59)
  • Serialize (45-45)
  • Serialize (103-112)
  • Serialize (103-103)
  • OnUnlimbo (128-151)
  • OnUnlimbo (128-128)
  • OnVelocityCheck (153-286)
  • OnVelocityCheck (153-153)
  • OnDetonateUpdate (288-334)
  • OnDetonateUpdate (288-288)
  • OnPreDetonate (336-346)
  • OnPreDetonate (336-336)
  • OpenFire (348-357)
  • OpenFire (348-348)
  • FireTrajectory (359-395)
  • FireTrajectory (359-359)
  • MultiplyBulletVelocity (397-409)
  • MultiplyBulletVelocity (397-397)
  • CalculateBulletVelocityRightNow (574-681)
  • CalculateBulletVelocityRightNow (574-574)
  • CalculateBulletVelocityLeadTime (411-572)
  • CalculateBulletVelocityLeadTime (411-411)
  • SearchVelocity (683-713)
  • SearchVelocity (683-683)
  • CalculateBulletVelocityAfterBounce (849-896)
  • CalculateBulletVelocityAfterBounce (849-849)
  • GetGroundNormalVector (898-998)
  • GetGroundNormalVector (898-898)
src/Utilities/Stream.h (2)
  • PhobosStreamReader (145-224)
  • PhobosStreamWriter (226-267)
src/Ext/Bullet/Trajectories/PhobosTrajectory.h (3)
  • std (495-495)
  • `` (477-477)
  • `` (492-492)
src/Ext/Bullet/Trajectories/PhobosActualTrajectory.h (2)
  • void (57-72)
  • ActualTrajectory (41-88)
src/Ext/Bullet/Trajectories/PhobosActualTrajectory.cpp (4)
  • OnUnlimbo (75-84)
  • OnUnlimbo (75-75)
  • OnPreDetonate (99-118)
  • OnPreDetonate (99-99)
src/Ext/Bullet/Trajectories/PhobosVirtualTrajectory.h (4)
src/Utilities/Template.h (1)
  • Valueable (58-71)
src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp (24)
  • Load (114-127)
  • Load (114-114)
  • Load (142-181)
  • Load (142-142)
  • Load (924-928)
  • Load (924-924)
  • Load (998-1002)
  • Load (998-998)
  • Load (1047-1050)
  • Load (1047-1047)
  • Save (129-140)
  • Save (129-129)
  • Save (183-194)
  • Save (183-183)
  • Save (930-934)
  • Save (930-930)
  • Save (1004-1008)
  • Save (1004-1004)
  • Save (1052-1055)
  • Save (1052-1052)
  • OnUnlimbo (199-277)
  • OnUnlimbo (199-199)
  • OnEarlyUpdate (280-314)
  • OnEarlyUpdate (280-280)
src/Utilities/Stream.h (2)
  • PhobosStreamReader (145-224)
  • PhobosStreamWriter (226-267)
src/Ext/Bullet/Trajectories/PhobosVirtualTrajectory.cpp (6)
  • OnUnlimbo (61-69)
  • OnUnlimbo (61-61)
  • OnEarlyUpdate (71-77)
  • OnEarlyUpdate (71-71)
  • InvalidFireCondition (79-97)
  • InvalidFireCondition (79-79)
src/Ext/Bullet/Trajectories/PhobosVirtualTrajectory.cpp (3)
src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp (4)
  • OnUnlimbo (199-277)
  • OnUnlimbo (199-199)
  • OnEarlyUpdate (280-314)
  • OnEarlyUpdate (280-280)
src/Ext/Techno/Body.cpp (2)
  • IsActive (66-72)
  • IsActive (66-66)
src/Ext/Bullet/Trajectories/PhobosTrajectory.h (1)
  • Get2DOpRadian (285-288)
src/Ext/Bullet/Trajectories/MissileTrajectory.h (7)
src/Utilities/Template.h (1)
  • Valueable (58-71)
src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp (42)
  • Load (114-127)
  • Load (114-114)
  • Load (142-181)
  • Load (142-142)
  • Load (924-928)
  • Load (924-924)
  • Load (998-1002)
  • Load (998-998)
  • Load (1047-1050)
  • Load (1047-1047)
  • Save (129-140)
  • Save (129-129)
  • Save (183-194)
  • Save (183-183)
  • Save (930-934)
  • Save (930-930)
  • Save (1004-1008)
  • Save (1004-1004)
  • Save (1052-1055)
  • Save (1052-1052)
  • Read (858-922)
  • Read (858-858)
  • Serialize (937-996)
  • Serialize (937-937)
  • Serialize (1011-1045)
  • Serialize (1011-1011)
  • Serialize (1058-1065)
  • Serialize (1058-1058)
  • OnUnlimbo (199-277)
  • OnUnlimbo (199-199)
  • OnEarlyUpdate (280-314)
  • OnEarlyUpdate (280-280)
  • OnVelocityCheck (317-450)
  • OnVelocityCheck (317-317)
  • OnDetonateUpdate (460-470)
  • OnDetonateUpdate (460-460)
  • OpenFire (500-531)
  • OpenFire (500-500)
  • SetBulletNewTarget (534-539)
  • SetBulletNewTarget (534-534)
  • CalculateBulletVelocity (542-552)
  • CalculateBulletVelocity (542-542)
src/Ext/Bullet/Trajectories/MissileTrajectory.cpp (42)
  • Load (31-36)
  • Load (31-31)
  • Load (107-112)
  • Load (107-107)
  • Save (38-43)
  • Save (38-38)
  • Save (114-119)
  • Save (114-114)
  • Read (45-90)
  • Read (45-45)
  • Serialize (11-29)
  • Serialize (11-11)
  • Serialize (93-105)
  • Serialize (93-93)
  • OnUnlimbo (121-131)
  • OnUnlimbo (121-121)
  • OnEarlyUpdate (133-143)
  • OnEarlyUpdate (133-133)
  • OnVelocityCheck (145-161)
  • OnVelocityCheck (145-145)
  • OnDetonateUpdate (163-177)
  • OnDetonateUpdate (163-163)
  • OpenFire (179-211)
  • OpenFire (179-179)
  • SetBulletNewTarget (224-237)
  • SetBulletNewTarget (224-224)
  • CalculateBulletVelocity (239-248)
  • CalculateBulletVelocity (239-239)
  • InitializeBulletNotCurve (250-298)
  • InitializeBulletNotCurve (250-250)
  • GetPreAimCoordsWithBurst (300-309)
  • GetPreAimCoordsWithBurst (300-300)
  • CalculateReducedVelocity (311-329)
  • CalculateReducedVelocity (311-311)
  • CurveVelocityChange (331-404)
  • CurveVelocityChange (331-331)
  • NotCurveVelocityChange (406-416)
  • NotCurveVelocityChange (406-406)
  • StandardVelocityChange (418-473)
  • StandardVelocityChange (418-418)
  • ChangeBulletVelocity (475-527)
  • ChangeBulletVelocity (475-475)
src/Utilities/Stream.h (2)
  • PhobosStreamReader (145-224)
  • PhobosStreamWriter (226-267)
src/Ext/Bullet/Trajectories/PhobosTrajectory.h (4)
  • std (495-495)
  • TrajectoryFlag (258-267)
  • `` (477-477)
  • `` (492-492)
src/Ext/Bullet/Trajectories/PhobosActualTrajectory.h (2)
  • void (57-72)
  • ActualTrajectory (41-88)
src/Ext/Bullet/Trajectories/PhobosActualTrajectory.cpp (4)
  • OnUnlimbo (75-84)
  • OnUnlimbo (75-75)
  • OnEarlyUpdate (86-97)
  • OnEarlyUpdate (86-86)
src/Ext/Bullet/Trajectories/PhobosTrajectory.h (1)
src/Ext/Bullet/Trajectories/AdditionalWarheads.cpp (4)
  • GetCellsInRectangle (77-299)
  • GetCellsInRectangle (77-77)
  • GetCellsInProximityRadius (8-62)
  • GetCellsInProximityRadius (8-8)
🔇 Additional comments (53)
CREDITS.md (1)

528-531: LGTM: Proper attribution for significant trajectory system contributions.

The additions appropriately acknowledge the extensive new trajectory system implementation including the new trajectory types (Missile, Engrave, Tracing) and the general trajectory framework, which represents a major feature addition to the project.

src/Utilities/EnumFunctions.cpp (1)

21-21: LGTM: Clean code simplification.

The direct assignment using abstract_cast<TechnoClass*> simplifies the logic while maintaining the same functionality. The subsequent null check ensures proper handling of the result.

src/Ext/Techno/Body.cpp (1)

761-762: LGTM: Proper serialization of new trajectory system members.

The addition of LastWeaponFLH and TrajectoryGroup to the serialization process correctly supports the new trajectory grouping and firing location tracking features. The placement in the serialization sequence is appropriate.

src/Ext/BulletType/Body.h (1)

74-76: LGTM: Well-integrated new bullet interaction flag.

The SubjectToSolid property follows the established pattern for boolean configuration flags in the class. The initialization to false and positioning among related flags is appropriate. This extends the bullet's environmental interaction capabilities as part of the trajectory system enhancements.

Also applies to: 105-105

src/Phobos.Ext.cpp (2)

283-283: LGTM: Global shared pointer registry for enhanced serialization.

The GlobalSharedRegistry properly supports the enhanced serialization framework by tracking shared pointers during save/load operations, preventing duplication and maintaining pointer integrity during deserialization.


285-289: LGTM: Proper registry cleanup on game load.

The LoadGame_ClearShared hook ensures the shared pointer registry is cleared during game load, which is essential for memory management and preventing stale references from previous sessions.

src/Ext/Bullet/Hooks.DetonateLogics.cpp (3)

19-19: Good variable naming improvement.

Renaming pBullet to pThis enhances consistency and follows common naming conventions for hook implementations.


23-27: Excellent integration with the new trajectory system.

This addition properly integrates trajectory-based detonation control with the existing bullet logic. The early return when ShouldDetonate is false allows trajectory implementations to have fine-grained control over when bullets should detonate, which is essential for the new trajectory types like Missile, Engrave, and Tracing.


31-31: Consistent variable usage update.

Properly updated to use the renamed pThis variable, maintaining consistency throughout the function.

src/Ext/Bullet/Body.h (3)

35-35: Good addition for dispersal weapon support.

The DispersedTrajectory flag appropriately supports the new trajectory system's dispersal weapon functionality. The placement after the Trajectory member and boolean type are both logical choices.


49-50: Proper constructor initialization.

The initializer list correctly orders members to match declaration order and appropriately initializes DispersedTrajectory to false as the default state.


53-53: Appropriate destructor change for trajectory cleanup.

The change from a defaulted destructor to an explicit virtual destructor is necessary to support proper cleanup of trajectory group membership and other trajectory-related resources when bullets are destroyed.

src/Ext/Bullet/Body.cpp (3)

16-32: LGTM! Proper cleanup for trajectory groups.

The destructor correctly removes the bullet from its trajectory group when destroyed, ensuring no dangling references remain in the group's bullet list.


171-176: Good adaptation to the new trajectory system.

The velocity angle calculation now correctly uses the trajectory's MovingVelocity when available, falling back to the bullet's velocity for compatibility. This ensures firing animations are properly oriented based on the trajectory's actual movement direction.


434-436: Correct serialization update for new member.

The DispersedTrajectory member is properly added to the serialization chain, maintaining save/load compatibility for the new trajectory system.

Phobos.vcxproj (1)

60-71: LGTM! Well-organized trajectory system files.

The project file changes properly integrate the new trajectory source and header files. The grouping of all trajectory-related files together in the src\Ext\Bullet\Trajectories\ directory follows good organizational practices.

Also applies to: 245-253

src/Ext/Techno/Body.h (1)

9-9: Appropriate additions for trajectory system integration.

The new include and member variables are properly declared and initialized. The LastWeaponFLH member for tracking firing locations and TrajectoryGroup for managing bullet capacity constraints align well with the trajectory system enhancements described in the PR.

Also applies to: 56-57, 124-125

src/Ext/Bullet/Hooks.cpp (1)

86-93: Good separation of concerns for laser trail handling.

The change properly delegates laser trail drawing to trajectories when present, avoiding duplicate rendering. The early return with Detonate when OnEarlyUpdate() returns true and no spawn animation exists correctly triggers detonation logic.

src/Ext/Techno/Hooks.Firing.cpp (4)

349-358: Well-implemented trajectory capacity management.

The capacity check properly enforces bullet creation limits per trajectory type. The lazy initialization of TrajectoryGroup and the differentiation between TemporarilyCannotFire vs CannotFire based on weapon damage provides appropriate feedback to the game's targeting system.


812-816: Correct implementation of burst fire FLH mirroring.

Moving the Y-coordinate mirroring logic inside the positive weapon index branch ensures it only applies to actual weapon firing, not alternate FLHs. The parity check on CurrentBurstIndex for mirroring is the standard approach for alternating fire positions.


828-835: Good passenger-aware FLH tracking.

The logic correctly identifies the passenger at the specified index and stores its FLH for later use. This enables proper projectile origin tracking for passengers firing from transports.


839-839: Important register correction.

Changing from R->EBX(pCoords) to R->EAX(pCoords) fixes the return value register, as the game expects the result in EAX at this hook location.

src/Ext/BulletType/Body.cpp (1)

125-141: Excellent trajectory flag management.

The validation logic properly:

  • Resets the Arm flag to prevent conflicts
  • Transfers Ranged behavior to trajectory while clearing it from bullet
  • Propagates SubjectToGround to appropriate trajectory types

The comment explaining the Ranged behavior is helpful for understanding the design decision.

src/Utilities/SavegameDef.h (1)

443-455: LGTM!

The pair serialization implementation is clean and correct, properly handling both members with short-circuit evaluation.

src/Ext/Bullet/Trajectories/PhobosVirtualTrajectory.cpp (1)

61-69: LGTM!

The OnUnlimbo initialization logic is clear and correct.

src/Ext/Bullet/Trajectories/TracingTrajectory.cpp (2)

6-35: LGTM!

The INI parser specialization for TraceTargetMode is well-implemented with proper error handling.


108-126: LGTM!

The lifecycle methods correctly handle trajectory initialization and early termination conditions.

src/Ext/Bullet/Trajectories/AdditionalWeapons.cpp (1)

552-594: LGTM!

The CreateDisperseBullets method is well-structured with clear responsibilities.

src/Ext/Bullet/Trajectories/AdditionalWarheads.cpp (3)

301-337: LGTM!

The obstacle checking logic is well-structured with appropriate handling for different techno types and special cases.


604-622: LGTM!

Clean implementation with clear separation of different damage application methods.


624-652: LGTM!

Well-implemented damage calculation with appropriate handling of attenuation and minimal damage retention.

src/Ext/Bullet/Trajectories/PhobosActualTrajectory.h (1)

62-72: LGTM!

The inline helper method CheckProjectileRange is appropriately placed in the header due to its small size and frequent usage.

src/Ext/Bullet/Trajectories/TracingTrajectory.h (3)

5-13: LGTM!

Well-designed enum class with clear mode names and type safety.


15-41: LGTM!

Well-structured trajectory type class with proper initialization and virtual method overrides.


43-75: LGTM!

Clean trajectory implementation with appropriate virtual method overrides and const correctness.

docs/New-or-Enhanced-Logics.md (1)

839-844: Rotation section mixes coordinate spaces

Trajectory.RotateCoord and Trajectory.AxisOfRotation are described in world space, but Trajectory.OffsetCoord is relative-target space. Readers have to infer this. Add one sentence clarifying coordinate space for each key.

src/Ext/Bullet/Trajectories/SampleTrajectory.h (1)

12-50: LGTM! Sample trajectory properly updated to new architecture.

The changes correctly adapt the sample trajectory to the new trajectory system architecture, including:

  • Proper base class initialization with bullet context
  • Consistent method signatures with other trajectory implementations
  • Appropriate visibility for configuration parameters
src/Ext/Bullet/Trajectories/ParabolaTrajectory.h (2)

5-13: Good use of enum for firing modes.

The ParabolaFireMode enum improves code clarity by providing named constants for the different trajectory calculation modes.


98-109: Cliff detection logic is well-implemented.

The static CheckBulletHitCliff method correctly:

  • Validates cell existence before accessing
  • Uses appropriate height threshold (384 = 4 * LevelHeight - 32)
  • Includes clear documentation of the magic number
src/Ext/Bullet/Trajectories/BombardTrajectory.h (2)

15-15: Good use of Nullable for optional configuration.

The change from Valueable<double> to Nullable<double> for FallSpeed allows proper handling of unset values, distinguishing them from a speed of 0.

Also applies to: 27-27


55-55: Clear naming improvement for angle units.

Renaming RotateAngle to RotateRadian clarifies that the value is in radians, reducing potential for unit conversion errors.

Also applies to: 64-64

src/Ext/Bullet/Trajectories/StraightTrajectory.cpp (2)

74-97: Range modifier calculation properly handles edge cases.

The implementation correctly:

  • Applies range modifiers only when enabled
  • Handles both positive and negative detonation distances
  • Prevents premature firing for dispersed trajectories

193-251: Lead time calculation is mathematically sound.

The quadratic equation solving for target interception is well-implemented with proper handling of:

  • Edge cases (vertical shots, no solution)
  • Moving targets (extra frame compensation)
  • Linear equation special case
src/Ext/Bullet/Trajectories/EngraveTrajectory.cpp (2)

52-52: Speed limitation needs documentation.

The speed is clamped to a maximum of 128.0 without explanation. Consider adding a comment explaining why this limitation exists for engrave trajectories.

 	// Limitation
+	// Engrave trajectories are limited to prevent visual artifacts at high speeds
 	this->Speed = Math::min(128.0, this->Speed);

284-284: Unexplained magic number in height check.

The value 256 is used as a threshold without explanation. This appears different from the cliff height of 384. Please document what this value represents.

-			// Less than 384 and greater than the maximum difference that can be achieved between two non cliffs
+			// Less than 384 and greater than the maximum difference that can be achieved between two non-cliffs
+			// 256 = maximum height difference between adjacent non-cliff cells
 			if (nowDifference >= 256)
src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp (1)

1122-1123: Verify hook return behavior

The hook returns 0 when trajectory doesn't exist, which continues to the next instruction. Ensure this is the intended behavior and doesn't skip necessary game code.

If the intention is to skip game code when no trajectory exists, consider returning the skip address instead:

-if (!BulletExt::ExtMap.Find(pThis)->Trajectory)
-    return 0;
+if (!BulletExt::ExtMap.Find(pThis)->Trajectory)
+    return SkipGameCode;
src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp (4)

8-37: LGTM!

The template specialization for reading ParabolaFireMode is well-implemented with proper error handling and clear string-to-enum mappings.


39-100: Proper refactoring to new base classes

The instance creation and serialization methods are correctly updated to work with the new ActualTrajectoryType base class. The comprehensive parameter reading in the Read method properly handles all trajectory configuration options.


849-896: Well-implemented bounce physics

The bounce calculation correctly handles:

  • Surface eligibility checks
  • Techno collision detection
  • Realistic bounce physics using surface normals
  • Damage attenuation on bounce
  • Additional warhead detonation

The implementation is thorough and physically accurate.


898-998: Comprehensive ground normal vector calculation

The GetGroundNormalVector method excellently handles:

  • Various slope indices with pre-calculated normal vectors
  • Cliff detection with directional analysis
  • Edge case handling for different terrain configurations

The magic numbers are well-documented with their mathematical derivations, making the code maintainable.

docs/Whats-New.md (1)

430-433: LGTM!

The new trajectory system entries are well-formatted and consistent with the existing changelog structure.

src/Ext/Bullet/Trajectories/EngraveTrajectory.h (1)

5-45: Add virtual destructor for polymorphic base class

Since EngraveTrajectoryType is a polymorphic class (has virtual functions and is used through base class pointers), it should have a virtual destructor to ensure proper cleanup.

 class EngraveTrajectoryType final : public VirtualTrajectoryType
 {
 public:
 	EngraveTrajectoryType() : VirtualTrajectoryType()
 		, IsLaser { true }
 		, IsIntense { false }
 		, IsHouseColor { false }
 		, IsSingleColor { false }
 		, LaserInnerColor { { 0, 0, 0 } }
 		, LaserOuterColor { { 0, 0, 0 } }
 		, LaserOuterSpread { { 0, 0, 0 } }
 		, LaserThickness { 3 }
 		, LaserDuration { 1 }
 		, LaserDelay { 1 }
 		, AttachToTarget { false }
 		, UpdateDirection { false }
 	{ }
+
+	virtual ~EngraveTrajectoryType() = default;

Likely an incorrect or invalid review comment.

src/Ext/Bullet/Trajectories/SampleTrajectory.cpp (1)

1-166: Excellent refactoring with helpful documentation!

The refactoring to use the new base classes is clean and well-executed. The added comments explaining each lifecycle method's purpose significantly improve code maintainability.

Comment on lines 389 to 602
continue;

const auto nextDistanceCrd = distanceCrd - velocityCrd;
// Should be behind the bullet's next frame position
if (nextDistanceCrd * velocityCrd > 0)
{
// Otherwise, at least within the spherical range of future position
if (nextDistanceCrd.Magnitude() > radius)
continue;
}
// Calculate the distance between the point and the line
const auto distance = (velocity > 1e-10) ? (distanceCrd.CrossProduct(nextDistanceCrd).Magnitude() / velocity) : distanceCrd.Magnitude();
// Should be in the center cylinder
if (distance > radius)
continue;
// Manual expansion
if (thisSize >= vectSize)
{
vectSize += cellSize;
validTechnos.reserve(vectSize);
}

validTechnos.push_back(pTechno);
thisSize += 1;
}
}
// Step 3: Record each target without repetition.
std::vector<int> casualtyChecked;
casualtyChecked.reserve(Math::max(validTechnos.size(), this->TheCasualty.size()));
// No impact on firer
if (pFirer)
this->TheCasualty[pFirer->UniqueID] = 5;
// Update Record
for (const auto& [ID, remainTime] : this->TheCasualty)
{
if (remainTime > 0)
this->TheCasualty[ID] = remainTime - 1;
else
casualtyChecked.push_back(ID);
}

for (const auto& ID : casualtyChecked)
this->TheCasualty.erase(ID);

std::vector<TechnoClass*> validTargets;
validTargets.reserve(validTechnos.size());
// checking for duplicate
for (const auto& pTechno : validTechnos)
{
if (!this->TheCasualty.contains(pTechno->UniqueID))
validTargets.push_back(pTechno);
// Record 5 frames
this->TheCasualty[pTechno->UniqueID] = 5;
}
// Step 4: Detonate warheads in sequence based on distance.
const auto targetsSize = validTargets.size();

if (this->ProximityImpact > 0 && static_cast<int>(targetsSize) > this->ProximityImpact)
{
std::sort(&validTargets[0], &validTargets[targetsSize],[pBullet](TechnoClass* pTechnoA, TechnoClass* pTechnoB)
{
const auto distanceA = pTechnoA->GetCoords().DistanceFromSquared(pBullet->SourceCoords);
const auto distanceB = pTechnoB->GetCoords().DistanceFromSquared(pBullet->SourceCoords);
// Distance priority
if (distanceA < distanceB)
return true;

if (distanceA > distanceB)
return false;

return pTechnoA->UniqueID < pTechnoB->UniqueID;
});
}

for (const auto& pTechno : validTargets)
{
// Not effective for the technos following it.
if (pTechno == this->ExtraCheck)
break;
// Last chance
if (this->ProximityImpact == 1)
{
this->ExtraCheck = pTechno;
break;
}
// Skip technos that are within range but will not obstruct and cannot be passed through
const auto absType = pTechno->WhatAmI();

if (!pType->ThroughVehicles && (absType == AbstractType::Unit || absType == AbstractType::Aircraft))
continue;

if (absType == AbstractType::Building && (static_cast<BuildingClass*>(pTechno)->IsStrange() ? !pType->ThroughVehicles : !pType->ThroughBuilding))
continue;

this->ProximityDetonateAt(pOwner, pTechno);
// Record the number of times
if (this->ProximityImpact > 0)
--this->ProximityImpact;
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Refactor this complex function for better maintainability.

This 200+ line function handles multiple responsibilities: target validation, distance calculations, deduplication, and detonation sequencing. The embedded lambda is also quite large.

Consider extracting:

  1. The checkCellContent lambda into a separate method
  2. Target validation logic into a dedicated function
  3. Distance calculation and filtering into helper methods

Additionally, the manual vector capacity management (lines 464-468, 519-523) seems redundant since reserve is already being used. The vector will automatically grow as needed.

🤖 Prompt for AI Agents
In src/Ext/Bullet/Trajectories/AdditionalWarheads.cpp from lines 389 to 602,
refactor the large PrepareForDetonateAt function by extracting the
checkCellContent lambda into its own method, isolating target validation logic
into a separate function, and moving distance calculation and filtering into
helper methods. Remove the manual vector capacity management code that attempts
to reserve additional capacity during push_back calls, as std::vector
automatically grows and the initial reserve calls are sufficient. This will
improve maintainability and readability by breaking down the complex function
into smaller, focused components.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and this bot comment

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and this bot comment

There are buildings on the ground, so the distance calculation is very different. So I don't think it's necessary to package it

@Metadorius
Copy link
Member

Why did the bot suddenly trigger at your code xD

@CrimRecya
Copy link
Contributor Author

Why did the bot suddenly trigger at your code xD

I don't know, it suddenly popped up after the last push

@CrimRecya
Copy link
Contributor Author

All completed

@CrimRecya CrimRecya requested a review from Metadorius July 25, 2025 07:22
Comment on lines 1251 to 1252
- The referenced images, weapons, and warheads should be supplemented by yourself.
- SOMEWEAPON[] - SOMEPROJECTILE[] - SOMEIMAGE[] / SOMEWARHEAD[]
Copy link
Member

@Metadorius Metadorius Jul 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does, for example, SOMEWEAPON[] mean? Did you mean to put a placeholder this way, (like SOMEWEAPONA1)? It's not very clear this way. Maybe we could omit that at all? Should be clear that those aren't public assets, it's the default case in Phobos docs anyways.

Also how about putting it also under {note} block?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Get it

![Trajectory-Demo-B](_static/images/Trajectory-demo-B.gif)
- The assets of the case come from mod *Light Cone*.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't you want to make those as links? :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I asked the person who provides GIFs for this, and he said that their mod currently does not have any particularly good web pages that can be used as links, So they haven't put the link for now.

Copy link
Member

@Metadorius Metadorius left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Went ahead and did at least a partial code review (hard to go over 7k+/3k- lines), mainly on clarity and code style. I think due to size this would need some more reviewing, especially on the higher level code organization

casualtyChecked.reserve(Math::max(validTechnos.size(), this->TheCasualty.size()));
// No impact on firer
if (pFirer)
this->TheCasualty[pFirer->UniqueID] = 5;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally no articles are used in code, could just remove them (a or the)

this->CalculateNewDamage();
}

int PhobosTrajectory::GetTheTrueDamage(int damage, bool self)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

Comment on lines 23 to 34
}
// It has not lost its target
if (!check)
return false;
// Check whether need to detonate directly after the target was lost
if (!pType->TolerantTime || pType->RetargetRadius < 0)
return true;
// Check the timer
if (this->RetargetTimer.HasTimeLeft())
return false;
// Next time wait for so long first
this->RetargetTimer.Start(pType->RetargetInterval);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally, there should be newlines here and in all other places to improve readability:

Suggested change
}
// It has not lost its target
if (!check)
return false;
// Check whether need to detonate directly after the target was lost
if (!pType->TolerantTime || pType->RetargetRadius < 0)
return true;
// Check the timer
if (this->RetargetTimer.HasTimeLeft())
return false;
// Next time wait for so long first
this->RetargetTimer.Start(pType->RetargetInterval);
}
// It has not lost its target
if (!check)
return false;
// Check whether need to detonate directly after the target was lost
if (!pType->TolerantTime || pType->RetargetRadius < 0)
return true;
// Check the timer
if (this->RetargetTimer.HasTimeLeft())
return false;
// Next time wait for so long first
this->RetargetTimer.Start(pType->RetargetInterval);

const auto pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon);
TechnoClass* pNewTechno = nullptr;
// Find the first target
if (!this->TargetInTheAir) // Only get same type (on ground / in air)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another The

Comment on lines 70 to 85
if (!pTechnoType->LegalTarget)
continue;
else if (pTechno->IsBeingWarpedOut())
continue;
else if (pTechno->WhatAmI() == AbstractType::Building && static_cast<BuildingClass*>(pTechno)->Type->InvisibleInGame)
continue;
else if (!PhobosTrajectory::CheckWeaponValidness(pOwner, pTechno, pCell, pType->RetargetHouses))
continue;
else if (PhobosTrajectory::GetDistanceFrom(retargetCoords, pTechno) > retargetRange)
continue;
else if (MapClass::GetTotalDamage(100, pBullet->WH, pTechnoType->Armor, 0) == 0)
continue;
else if (pWeapon && PhobosTrajectory::GetDistanceFrom(pFirer ? pFirer->GetCoords() : pBullet->SourceCoords, pTechno) > range)
continue;
else if (!PhobosTrajectory::CheckWeaponCanTarget(pWeaponExt, pFirer, pTechno))
continue;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

else are not needed here, also not sure it will be better but could just make it a long multiline var like

bool skip = ...
	|| ...
	|| ...

if (skip)
	continue;

if (pType->NoLaunch)
return extraOffsetCoord * this->GetLeadTime(std::round((this->Height - target.Z) / pType->FallSpeed.Get(pType->Speed)));

const auto theDistanceSquared = targetSourceCoord.MagnitudeSquared();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another the

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose and the difference between PhobosTrajectory, PhobosActualTrajectory and PhobosVirtualTrajectory? Those seem like new concepts and I think every one of them should have an explanation and relation documented.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's beneficial to make the specific implemeneted trajectories go into a subfolder.

@@ -52,6 +53,8 @@ class TechnoExt
bool CanCloakDuringRearm; // Current rearm timer was started by DecloakToFire=no weapon.
int WHAnimRemainingCreationInterval;
WeaponTypeClass* LastWeaponType;
CoordStruct LastWeaponFLH;
std::shared_ptr<PhobosMap<DWORD, PhobosTrajectory::GroupData>> TrajectoryGroup;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this for?

Comment on lines 224 to 238
struct GroupData
{
std::vector<DWORD> Bullets {};
double Angle { 0.0 };
bool ShouldUpdate { true };

GroupData() = default;

bool Load(PhobosStreamReader& stm, bool registerForChange);
bool Save(PhobosStreamWriter& stm) const;

private:
template <typename T>
bool Serialize(T& stm);
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could leave a comment on what this is for too

{
const auto pFirerExt = TechnoExt::ExtMap.Find(pThis);

if (!pFirerExt->TrajectoryGroup)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TrajectoryGroup code snippet should be wrapped into a helper function as it has been used in multiple places

void PhobosTrajectory::UpdateGroupIndex()
{
const auto pBullet = this->Bullet;
auto& groupData = (*this->TrajectoryGroup)[pBullet->Type->UniqueID];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the key for map could simply be a pointer instead of using its ID, as the Type itself is unique

Copy link
Contributor

@Thrifinesma Thrifinesma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is too long! I'll review this much today and look at the rest later.

this->InitialTargetCoord = pBullet->TargetCoords;

// Special case: Set the target to the ground
if (this->Type->DetonationDistance.Get() <= -1e-10)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this 1e-10 should be a constexpr int

// Target will update location after techno firing, which may result in inaccurate
// target position recorded by the LastTargetCoord in Unlimbo(). Therefore, it's
// necessary to record the position during the first Update(). - CrimRecya
if (this->WaitOneFrame == 2)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WaitOneFrame has only three avaliable values. They should be enum class to make their usage more clear to developer

if (horizonDistance > 0)
{
// Slowly step up
double horizonMult = std::abs(this->MovingVelocity.Z / 64.0) / horizonDistance;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there needs a comment to explain why 64.0 here and beneath

// Check for additional obstacles on the ground
if (this->CheckThroughAndSubjectInCell(MapClass::Instance.GetCellAt(pBullet->Location), pOwner))
{
if (32.0 < velocity)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

velocity > 32.0 is more readable?

return false;

// Detonates itself in the next frame
if (velocityCheck == 2)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, velocityCheck has limited fixed value of 0, 1, 2, I think it's better to have an enum for it

return BulletVelocity{ 0.4472135954999579392818347337463 * reverseSgnX, 0.8944271909999158785636694674925 * reverseSgnY, 0.0 };

// 0.7071067811865475244008443621049 -> 1 / sqrt(2)
return BulletVelocity{ 0.7071067811865475244008443621049 * reverseSgnX, 0.7071067811865475244008443621049 * reverseSgnY, 0.0 };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function has very many immediate numbers. I suggest to give they aliases by using constexpr and comments for reason. Otherwise, I'm afraid nobody would know why these numbers should be these values after months.

else if (cliffType == CliffType::Type_2_1)
return BulletVelocity{ shortRightAngledEdge * reverseSgnX, longRightAngledEdge * reverseSgnY, 0.0 };

constexpr double hypotenuse = 0.7071067811865475244008443621049; // 1 / sqrt(2)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you misunderstood a bit. If you write

constexpr double hypotenuse = 1 / sqrt(2)

-- it will be equal to what you did now, except more readable. constexpr guarantees you that the result of what's on the right side of equals sign will be available at compile time.

Copy link
Contributor Author

@CrimRecya CrimRecya Jul 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MSVC does not support constexpr sqrt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Major Big stuff to do ❓New feature ⚙️T2 T2 maintainer review is sufficient Tested
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants