Site  •  Wiki  •  FAQ  •  Login

[EXAMPLE] Custom Image Overlay for Stim Packs



Posts: 10

Joined: December 8th, 2010, 1:57 pm

Post October 1st, 2012, 12:10 am

[EXAMPLE] Custom Image Overlay for Stim Packs

In this example, we are going to add some fancy graphical effects for Stimpacked units. This particular example uses the unused healing effect graphics in StarCraft.

(1) Run PyTBL and open the images.tbl file, available in DatEdit's Default/ folder or in BrooDat.mpq's arr/ folder.
Add a new string with: "thingy\tmeHealS.grp<0>"
Now save the images.tbl to some other place.

(2) Run DatEdit. From the top menu, select File > Options.... Switch to the Custom Files tab and set the path to the images.tbl file to the one we just saved in step 1. Press OK to close the Options window.

(3) In DatEdit, jump to the Images tab. Select the 970th entry (Unused Heal (small)). You will see that it is (wrongly) pointing to the thingy\tmeHealS.grp. However, there is no string pointing to tmeHealS.grp in the default images.tbl; this is why we added this string in step 1 and 2. Change the GRP file to "thingy\tmeHealS.grp<0>". It should be at the bottom of the list.

Save the images.dat to some other place.

(4) Finally, some plugin coding.

In game.cpp (top level code):
//-------------------------------- Unit Stats Update --------------------------------//
const DWORD UpdateUnitStatsInject   = 0x004EC352;
const DWORD UpdateUnitStatsTarget   = 0x00492F70;
const DWORD UpdateUnitStatsReturn   = UpdateUnitStatsInject + 5;

void __declspec(naked) OnUnitStatUpdate() {
   UNIT* unit;
   __asm {
      CALL [UpdateUnitStatsTarget]
      MOV EBP, ESP
      MOV unit, esi


   __asm {
      JMP [UpdateUnitStatsReturn]

Here, we've just added a new function, UpdateUnitStats, that is called whenever a unit's effect state (for Maelstrom, Plague, Ensnare, Stim Packs, etc.) is updated--which is every 8 frames.

In asm.cpp (top level code):
const DWORD RemoveOverlays_Func = 0x004E5CF0;

void RemoveOverlays(UNIT *unit, DWORD imageIdStart, DWORD imageIdEnd)
   if (!unit) return;
   if (imageIdStart > imageIdEnd) {
      DWORD tmp = imageIdStart;
      imageIdStart = imageIdEnd;
      imageIdEnd = tmp;

   __asm {
      MOV eax, unit
      MOV edx, imageIdStart
      MOV edi, imageIdEnd
      CALL [RemoveOverlays_Func]

void RemoveOverlays(UNIT *unit, DWORD imageId) {
   RemoveOverlays(unit, imageId, imageId);

const DWORD CreateOverlay_Func = 0x00498EA0;

void CreateOverlay(SPRITE* sprite, DWORD imageId, BYTE x = 0, BYTE y = 0, DWORD direction = 0)
   if (!sprite) return;
   DWORD _x = x, _y = y;

   __asm {
      PUSH direction
      PUSH _y
      PUSH _x
      MOV esi, imageId
      MOV eax, sprite
      CALL [CreateOverlay_Func]

Here, we're using two of StarCraft's internal functions. RemoveOverlays() removes any overlay with the specified image ID (or a range of image IDs) from the given unit. CreateOverlay() creates an image overlay on the given sprite, similar to how the IScript opcode imgol works.

In game.h:
// The following code is based on BWAPI's BW/Offsets.h
struct DatLoad {
   u32 address;
   u32 length;
   u32 entries;
static const DatLoad* const unitsDat   = (const DatLoad*)0x00513C30;
static const DWORD* const   UnitsDat_PrototypeFlags   = (DWORD*)   unitsDat[22].address;

//Called every 8 frames, right after a unit's effect/spell timers have been updated
void UpdateUnitStats(UNIT* unit) {
   if (unit->stimTimer) {
      DWORD protoFlags = UnitsDat_PrototypeFlags[unit->unitId];
      DWORD imgId = 970;
      if (protoFlags & UPF_LargeUnit) imgId += 1;
      if (protoFlags & UPF_HugeUnit) imgId += 2;

      CreateOverlay(unit->sprite, imgId);
      RemoveOverlays(unit, 970, 972);

Now we make use of the changes we introduced. The UnitsDat_PrototypeFlags array is used to determine which overlay (970, 971, or 972) should be used for each unit. To use UPF_LargeUnit (0x02000000) and UPF_HugeUnit (0x04000000), you should add the following to enumerations.h:

//Based on BWAPI
enum UnitPrototypeFlags {
   UPF_Building         = 0x00000001,
   UPF_Addon            = 0x00000002,
   UPF_Flyer            = 0x00000004,
   UPF_Worker            = 0x00000008,
   UPF_Subunit            = 0x00000010,
   UPF_FlyingBuilding      = 0x00000020,
   UPF_Hero            = 0x00000040,
   UPF_RegeneratesHP      = 0x00000080,
   UPF_AnimatedIdle      = 0x00000100,
   UPF_Cloakable         = 0x00000200,
   UPF_TwoUnitsIn1Egg      = 0x00000400,
   UPF_NeutralAccessories   = 0x00000800,
   UPF_ResourceDepot      = 0x00001000,
   UPF_ResourceContainer   = 0x00002000,
   UPF_RoboticUnit         = 0x00004000,
   UPF_Detector         = 0x00008000,
   UPF_Organic            = 0x00010000,
   UPF_CreepBuilding      = 0x00020000,
   UPF_Unused            = 0x00040000,
   UPF_RequiredPsi         = 0x00080000,
   UPF_Burrowable         = 0x00100000,
   UPF_Spellcaster         = 0x00200000,
   UPF_PermanentCloak      = 0x00400000,
   UPF_NPCOrAccessories   = 0x00800000,
   UPF_MorphFromOtherUnit   = 0x01000000,
   UPF_LargeUnit         = 0x02000000, //Used to determine overlay for various spells and effects
   UPF_HugeUnit         = 0x04000000, //Used to determine overlay for various spells and effects
   UPF_AutoAttackAndMove   = 0x08000000,
   UPF_Attack            = 0x10000000, /**< Can attack */
   UPF_Invincible         = 0x20000000,
   UPF_Mechanical         = 0x40000000,
   UPF_ProducesUnits      = 0x80000000 /**< It can produce units directly (making buildings doesn't count) */

Build qdp.dll and save it to some other place.

(5) Merge images.tbl, images.dat, and qdp.dll into a mod. Voilà!

I'm attaching a screenshot of Stimmed Marines for reference:


User avatar

Posts: 288

Joined: June 7th, 2009, 1:00 am

Location: Behind myself

Post October 2nd, 2012, 12:49 pm

Re: [EXAMPLE] Custom Image Overlay for Stim Packs

This is... Pretty darn awesome. :O


User avatar

Site Admin

Posts: 478

Joined: June 6th, 2009, 12:25 am

Location: Korea

Post October 2nd, 2012, 10:40 pm

Re: [EXAMPLE] Custom Image Overlay for Stim Packs

Very awesome indeed! You know, I always wondered why something like this wasn't default for StimPack. I remember finding the duration of Stim harder to gauge as a result of a lack of visual reinforcement.

This opens a lot of new possibilities for people using Stim as a trigger spell for a new self-cast ability. Thank you for this, RadiganP!

Return to Plugins

Who is online

Users browsing this forum: Majestic-12 [Bot] and 1 guest

phpBBST Software