Jump to content

Classic bug: NPC zipper uses another ship's scanner range

From Galactic Empire wiki
Bug ID GE-BUG-052
Affected versions Classic 3.2e and published classic source
Status Verified in source
Fixed in ge-next 0.3
Primary source zip() and cmd_zipper() in GECMDS.C; cyb_attack() in GECYBS.C; NPC tick dispatch in GEMAIN.C

Summary

Classic GE's zip() measures mines from the ship that launched the zipper but takes the trigger range from the global warsptr instead of that launching ship. The two pointers match for a player-issued ZIPPER command, but need not match when a Cybertron launches a zipper during its own update. An NPC can therefore trigger mines beyond its class's scanner range, or fail to trigger mines that should be in range.

Symptoms

An equipped Cybertron may use a zipper when mines are nearby. Which mines it actually trips can depend on the class of whichever ship warsptr currently references, rather than on the Cybertron's own class. This affects the ensuing mine explosions and can alter whether ships in the area take damage. Player-issued zippers do not have this particular mismatch because the command passes warsptr itself as the launching ship.

The source establishes the wrong-range path; the frequency of visible incidents in a live classic game has not been measured. The effect is easiest to notice when the two ship classes have substantially different scanner ranges and a mine lies between those limits.

Cause

The classic helper receives ptr, the launching ship, and computes each mine's distance from ptr->coord. It then compares that distance with the scanner range of warsptr->shpclass:

ddist = cdistance(&ptr->coord, &mptr->coord);
ddist *= 10000;
if (ddist < (double)shipclass[warsptr->shpclass].scanrange)
    mptr->timer = 1;

The player command calls zip(warsptr, usrnum), making ptr == warsptr. Cybertron combat calls zip(ptr, usrn) with the Cybertron's ship pointer. The NPC tick dispatcher supplies that pointer to the Cybertron routine without setting global warsptr to it. Other code can leave warsptr pointing at a player or another NPC. This is a shared-context error, not an intentional class-dependent bonus or penalty.

The unrelated usrn argument identifies the launching ship for messages; it does not correct the range comparison.

Example and verification

Suppose a Cybertron's configured scanner range is 100,000 distance units, the ship referenced by warsptr has a 50,000-unit range, and a live mine is 75,000 units from the Cybertron. The mine is within the intended range, but the classic helper leaves its timer unchanged because it compares 75,000 with 50,000. If the two ranges are reversed, the Cybertron can trip a mine beyond its own scanner range. These numbers illustrate the pointer mismatch; they are not a claim that a particular stock Cybertron has those exact values.

To verify in a test build, arrange two different class ranges, place a live mine between their limits, and invoke a Cybertron zipper launch while warsptr refers to the other ship. Compare the mine's timer before and after zip(). Ordinary live play may not offer a reliable way to force the Cybertron's random zipper decision at a chosen instant.

Correction in ge-next

ge-next 0.3 changed the range comparison to use the launching ship pointer, ptr->shpclass, rather than global warsptr->shpclass. Both position and range now belong to the same ship.

See also