Brood War API – The Comprehensive Guide: Movement modifiers, and Terran units

Index for the Comprehensive Guide posts

Foreword

First, an addition to movement. This will be needed to understand some of the explanations below. (The actual finished book will be better structured, but I’m writing this articles in chronological order as I research various areas). I will reference sections by their intended name in the book, so if you don’t find something, don’t be too surprised (The Processing frames and updating units section is not published yet, for example). I do try to keep it consistent though.

Let’s get into it!

(Chapter: An inside look into StarCraft)

Movement modifiers

Some upgrades, and effects modify the unit’s speed, acceleration, and turn rate. One would expect these to stack additively, but it’s simpler than that.

Unit speed: if the unit is under the effect of stim packs, or has a speed upgrade, it has +1 modifier and it has a -1 modifier if it’s ensnared. There is no movement speed upgrade for Firebats and Marines, so while the code permits them to stack, effectively they never do. If the sum of these modifiers is less than 0, the unit’s speed is halved. If it’s greater than 0, two things can happen. If the unit is a Protoss Scout (or a hero version of it), the speed is set to a constant value (ufp8::integer(6) + (ufp8::integer(1) - ufp8::integer(1) / 3u)) – this is roughly 6.66, which is a 33% increase. Otherwise the speed of the unit is increased by half of the base speed, and the minimum speed is increased by a constant (ufp8::integer(3) + ufp8::integer(1) / 3u) or 3.33. This causes the zerg overlord speed upgrade to result in a 300% increase in speed. If the speed is lower than the minimum, it is modified to be the minimum. If the modifier total is 0, then the unit’s base speed is returned (so ensnare and stim packs can cancel each other out, for example).

Unit acceleration: Like with speed, there is a modifier value calculated, for the same effects. If the modifier is negative, the acceleration rate is divided by four, if it’s negative, it is doubled.

Unit turn rate: The calculation is the same as with the the acceleration. If the modifier is negative, the turn rate is divided by four, if it’s positive, it’s doubled.

Here is the code segment dictating these:

	fp8 get_modified_unit_speed(const unit_t* u, fp8 base_speed) const {
		ufp8 speed = base_speed.as_unsigned();
		int mod = 0;
		if (u->stim_timer) ++mod;
		if (u_speed_upgrade(u)) ++mod;
		if (u->ensnare_timer) --mod;
		if (mod < 0) speed /= 2u;
		if (mod > 0) {
			if (unit_is_scout(u)) {
				speed = ufp8::integer(6) + (ufp8::integer(1) - ufp8::integer(1) / 3u);
			} else {
				speed += speed / 2u;
				ufp8 min_speed = ufp8::integer(3) + ufp8::integer(1) / 3u;
				if (speed < min_speed) speed = min_speed;
			}
		}
		return speed.as_signed();
	}

	fp8 get_modified_unit_acceleration(const unit_t* u, fp8 base_acceleration) const {
		ufp8 acceleration = base_acceleration.as_unsigned();
		int mod = 0;
		if (u->stim_timer) ++mod;
		if (u_speed_upgrade(u)) ++mod;
		if (u->ensnare_timer) --mod;
		if (mod < 0) acceleration -= acceleration / 4u;
		if (mod > 0) acceleration *= 2u;
		return acceleration.as_signed();
	}

	fp8 get_modified_unit_turn_rate(const unit_t* u, fp8 base_turn_rate) const {
		ufp8 turn_rate = base_turn_rate.as_unsigned();
		int mod = 0;
		if (u->stim_timer) ++mod;
		if (u_speed_upgrade(u)) ++mod;
		if (u->ensnare_timer) --mod;
		if (mod < 0) turn_rate -= turn_rate / 4u;
		if (mod > 0) turn_rate *= 2u;
		return turn_rate.as_signed();
	}

Second, an addition to the Energy segment of unit abilities:

The starting energy of a unit is the quarter of the maximum energy of the unit type (rounded up), so upgrades affect it. The exceptions are the Protoss Shield Battery, with the starting energy of 100, and Dark Archons, with 50 energy (even with energy upgrade).

bool initialize_unit_type(unit_t* u, const unit_type_t* unit_type, xy pos, int owner) {
(...)
        if (unit_is(u, UnitTypes::Protoss_Shield_Battery)) u->energy = fp8::integer(100);
        else u->energy = unit_max_energy(u) / 4;

(Continuing the section about Terran buildings)

Refinery: Gas extractor building for the Terran race.

Academy: Enables building Comsat station addons, and training Medics, Firebats, and Ghosts at the Barracks. Also enables some research options for Terran infantry units. These are the following

U-238 Shells: Increases maximum range for the Terran Marine by 32 pixels. Also adds +1 tile (32 pixels) to the target acquisition range.

Stim Packs: Allows Marines and Firebats to use the Stim Packs upgrade. This costs 10 life to use, and increases movement and attack speed while in effect – to be more precise, adds a positive modifier for the unit acceleration, turn rate, speed, and weapon cooldown (see those sections for details). The duration of the Stim Pack tech is not constant, although the actual difference is 7 frames at most. When a use stim pack action is issued, a stim pack timer is issued to the unit, set at 37. This is decreased in every 8 frames, but that 8 frame cycle timer does not start at 0. This in turn results in the stim pack effect lasting between 295 and 288 frames. (See the Processing frames and updating units section)

Restoration: Removes all harmful effect from from target unit. (Lockdown, Optical Flare, Irradiate, Plague, Ensnare, Acid Spores and Parasite, but not Stasis Field). Strangely, it also kills Hallucinations.

Optical Flare: Sets the unit’s sight range to 2* 32 pixels, and removes the ability to detect invisible units. Kills hallucinations.

int unit_sight_range(const unit_t* u, bool ignore_blindness = false) const {
		(...)
		if (!ignore_blindness && u->blinded_by) return 32 * 2;

Also sets the update_tiles countdown to 1, so the effect appears instantaneous.

	void blind_unit(unit_t* u, int source_owner) {
		if (u_hallucination(u)) {
			kill_unit(u);
			return;
		}
		u->blinded_by |= 1 << source_owner;
		play_sound(1019, u);
		create_sized_image(u, ImageTypes::IMAGEID_Optical_Flare_Hit_Small);
		st.update_tiles_countdown = 1;
	}

Caduceus Reactor: Adds +50 to the Medic’s maximum energy (And indirectly sets the starting energy to 63, see the section about unit energy).

Engineering Bay: Allows infantry upgrades (Terran Infantry Armor, and Terran Infantry Weapons), can lift off. Enables building missile turrets.

Missile Turret: Static anti-air defense unit that has detection (Since it’s a building, the detection range is 7 tiles, or 7*32 pixels, which is not the same as the unit’s sight range).

Factory: Allows building Terran mechanical units (Vulture, Siege Tank, Goliath). Can lift off. Can build the Machine Shop addon. Allows building of the Armory.

Machine Shop (Addon for the Factory): Allows building Siege Tanks at the Factory it’s attached to, and can research the following techs:

Ion thrusters: Speed upgrade for Vultures. (See the Speed section how those work)

Spider Mines: Allows Vultures to use the Spider Mines ability. After researching, each Vulture will have 3 spider mines available to place (existing and newly created both). These can be placed on passable terrain only. If they are attempted to place into an invalid position, an error message “Must target passable terrain” is displayed.

Siege Tech: Allows Siege Tanks to use the Siege Mode ability.

Starport: Allows training of Terran flying units (Wraith, Dropship, Science Vessel, Battlecruiser, Valkyrie). Can lift off. Allows building of the Science Facility.

Control Tower: Addon to the Starport. Allows building Dropships, Science Vessels, Battlecruisers, and Valkyries at the Starport it is attached to. Allows researching of the following technologies:

Cloaking Field: Allows Wraiths to use the Cloaking ability.

Apollo Reactor: Increases the Wraiths’ maximum energy by +50, and indirectly, the starting energy to 63.

Science Facility: Allows the building of Science Vessels at Starports. Has two types of addons, the Covert Ops, and the Physics lab. Allows the researching of three upgrades:

EMP Shockwave: The science Vessel fires a missile that sets all the energy and shields of the units in the area (Including buildings) to zero. This affects allied units as well. This also kills hallucinations. Although the animation of the abiliy indicates a circular area of effect, it is actually a square. It doesn’t affect units under Stasis.


	void emp_shockwave(xy position, const unit_t* source_unit) {
		int range = get_weapon_type(WeaponTypes::EMP_Shockwave)->inner_splash_radius;
		for (unit_t* target : find_units_noexpand(square_at(position, range))) {
			if (target == source_unit) continue;
			if (source_unit && target == source_unit->subunit) continue;
			if (u_hallucination(target)) {
				kill_unit(target);
				continue;
			}
			if (target->stasis_timer) continue;
			target->energy = 0_fp8;
			target->shield_points = 0_fp8;
		}
	}

The size of the EMP effect is a 129×129 pixel box.

Irradiate: Adds the irradiate effect to the target unit. Can only target units (not buildings), but only damages organic units. Does not damage Larvae, Eggs and Lurker Eggs, but the units spawning from the latter two retain the effect, and can still get damaged. If an irradiated unit gets into a transport or a Bunker, it damages organic units inside, but not the transport itself, even if that’s organic. If the transport is irradiated, the units are not affected inside.

When on the map, It damages units in an area, which resembles a rounded square. The search box for affected units is 321×321 pixels, and the effect has a 32-pixel range, measured from the edge of the irradiated unit’s bounding box – in short, bigger units can damage more units around them.

	void deal_irradiate_damage(unit_t* source_unit) {
		auto damage = [&](unit_t* target) {
			if (!ut_organic(target)) return;
			if (ut_building(target)) return;
			if (unit_is(target, UnitTypes::Zerg_Larva)) return;
			if (unit_is(target, UnitTypes::Zerg_Egg)) return;
			if (unit_is(target, UnitTypes::Zerg_Lurker_Egg)) return;
			if (u_burrowed(target) && target != source_unit) return;
			if (!u_loaded(target)) {
				if (!unit_target_in_range(source_unit, target, 32)) return;
			}
			auto* w = get_weapon_type(WeaponTypes::Irradiate);
			weapon_deal_damage(w, fp8::integer(w->damage_amount) / w->cooldown, 1, target, 0_dir, source_unit->irradiated_by, source_unit->irradiate_owner);
		};
		if (u_burrowed(source_unit)) {
			damage(source_unit);
		} else if (u_loaded(source_unit)) {
			if (source_unit->connected_unit) damage(source_unit->connected_unit);
		} else {
			for (unit_t* n : find_units_noexpand(square_at(source_unit->sprite->position, 160))) {
				damage(n);
			}
		}
	}

Titan Reactor: Adds +50 to the Science Vessel’s maximum energy, and indirectly sets the starting energy to 63.

Armory: Provides armor and damage upgrades for terran mechanical units (Terran Vehicle Plating, Terran Ship Plating, Terran Vehicle Weapons, Terran Ship Weapons).

Covert Ops: Allows building Ghosts at the Barracks. Allows research of the following technologies:

Personnel Cloaking: Allows Ghosts to use the Cloaking ability. (See the Energy and Detection sections for more info)

Lockdown: Allows Ghosts to use the Lockdown Ability. Can only target mechanical units. Kills Hallucinations. Disables the target unit. When the ability is cast, the lockdown_timer variable is set to 131. This is decreased in every 8 frames (See the Processing frames and updating units section) , resulting in the Lockdown effect lasting between 1040-1047 frames.

	void lockdown_unit(unit_t* u) {
		if (u->lockdown_timer == 0) {
			create_sized_image(u, ImageTypes::IMAGEID_Lockdown_Field_Small);
		}
		if (u->lockdown_timer < 131) u->lockdown_timer = 131;
		set_unit_disabled(u);
	}

Ocular Implants: Sets the Ghost’s sight range to 11 tiles (11*32 pixels). This also affects the maximum casting range of Nuclear Strike. (See the Orders section)

Moebius Reactor: Increases Ghosts’ maximum energy by +50, and indirectly, the starting energy to 63.

Physics Lab: Allows building Battlecruisers at the Starport. Allows research of the following upgrades:

Yamato Gun: Allows the Battlecruisers to use the Yamato Gun ability. The Yamato Gun costs 150 Energy, and deals 260 Explosive damage to the target.

Terran ground units

SCV: Terran worker unit. Can go inside bunkers. Both organic and mechanical.

Marine: Infantry unit, organic. Can use the Stim Pack ability. Can go inside, and fire from bunkers.

Firebat: Infantry unit, organic. Can use the Stim Pack ability. Can go inside, and fire from bunkers. The Firebat’s weapon deals 8 (unupgraded) damage, and is a series of radial splash explosion. (See Combat and Damage section) The attack sprite of the Flamethrower is a bit different than the actual damaging areas created by the attack. Firebats create 3 circular splash bullets in the direction the unit is facing at increasing distances on consecutive frames, similar to a line of bombs dropped from a plane. An excerpt from the attack script (iscript.bin):

FirebatGndAttkRpt:
    wait               1
    nobrkcodestart     
    imgol              421 0 0    # FlameThrower (thingy\flamer.grp)
    playfram           0x11    # frame set 1
    attkshiftproj      24
    wait               1
    attkshiftproj      52
    wait               1
    attkshiftproj      80
    wait               5
    playfram           0x00    # frame set 0
    wait               2
    ignorerest  

This means that after 1, 2, and 3 frames, invisible bullets are spawned with distances of 24, 52, and 80 pixels respectively (from the unit, in the attack’s direction). The splash radii is 15, 20, and 25 pixels for the inner, medium, and outer splash – all three attacks have the same pattern. Usually this arrangement means that a unit will get affacted by 2 bullets, but with big units, or units moving away from the origin of the attack, three bullets can affect them. This also means that it’s beneficial to attack from max range diagonally with firebats.

A visual explanation – attacking from minimum range. The Siege Tanks is only hit by two of the splashes.

While attacking from a little further away, all three splashes can affect the Tank.

And finally, all attacks overlaid, with all the splash radii, color coded (Red = inner, Orange = medium, Yellow = outer)

Medic: Infantry unit, organic. Has the Heal ability by default, Optical Flare and Restoration if researched. Can enter Bunkers. Automatically casts Heal to patch up damaged organic units nearby. The Medic has a 321×321 pixel square centered around it, where it searches for units to heal, and will attempt to heal them.

	unit_t* find_medic_target(const unit_t* u) const {
		if (!unit_can_use_tech(u, get_tech_type(TechTypes::Healing))) return nullptr;
		return find_nearest_unit(u, square_at(u->sprite->position, 160), [&](unit_t* target) {
			if (target->is_being_healed) return false;
			return medic_can_heal_target(u, target);
		});
	}

Heal: Boosts the health regeneration of the target unit. Can only heal organic units, and a unit can only be healed by one Medic at a time. Cannot heal flying units, hallucinations, enemy/neutral units, and disabled units.

	bool medic_can_heal_target(const unit_t* u, const unit_t* target) const {
		if (!target || target == u) return false;
		if (!ut_organic(target) || ut_building(target)) return false;
		if (u_flying(target) || u_hallucination(target)) return false;
		if (target->hp >= target->unit_type->hitpoints) return false;
		if (unit_target_is_enemy(u, target)) return false;
		if (u->owner >= 8 || target->owner >= 8) return false;
		if (unit_is_disabled(target)) return false;
		return true;
	}

If this method evaluates to true, then the Medic will try to heal the target. It can still fail if the unit is being healed already, or the Medic has no energy, or for some reason, the Healing tech is disabled.

	int medic_try_heal(unit_t* u) {
		if (!unit_can_use_tech(u, get_tech_type(TechTypes::Healing))) return 0;
		unit_t* target = u->order_target.unit;
		if (!target || !medic_can_heal_target(u, target)) return 0;
		if (target->is_being_healed) {
			target = find_medic_target(u);
			if (!target) return 0;
			u->order_target.unit = target;
			u->order_target.pos = target->sprite->position;
			move_to_target(u, target);
		}
		if (!unit_target_in_range(u, target, 30)) return 3;
		fp8 heal_amount = target->unit_type->hitpoints - target->hp;
		if (heal_amount > 200_fp8) heal_amount = 200_fp8;
		fp8 energy_required = heal_amount / 2;
		if (u->energy < energy_required) return 2;
		u->energy -= energy_required;
		set_unit_hp(target, target->hp + heal_amount);
		target->is_being_healed = true;
		return 1;
	}

The amount being healed is 200/256 HP per frame (or the amount required to reach maximum HP), and it costs 100/256 energy (half of it) to do so.

Ghost: Infantry unit, organic. Can use the Cloak, Lockdown, and Nuclear Strike abilities, if they are researched. Can enter, and fire from Bunkers. When cloaked, it doesn’t acquire targets automatically, so it does not fire at opponents, except when a Hold Position order is issued. When a Nuclear Strike command is issued, the Ghost isn’t able to do anything for a period of time, except canceling the action. If it’s canceled, or the Ghost is killed/disabled during that period, the nuke is lost. (See Orders section for further information about nuclear strikes)

And that’s where I finish this piece, thanks for reading! If you liked the article, consider subscribe to the mailing list for updates, or following me on my social media channels, which are Facebook and Twitter – or if you are an extra good boy, consider supporting me on Patreon!

Leave a Reply