Site  •  Wiki  •  FAQ  •  Login

[EXAMPLE] Plasma Shields Overcharge

<<

RadiganP

Posts: 10

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

Post October 25th, 2012, 9:28 pm

[EXAMPLE] Plasma Shields Overcharge

In this example, we are going to make Shield Batteries a lot better, by allowing them to recharge up to 200% shields. This is similar to overhealing in Team Fortress 2.

Step 1: Allow Shield Batteries to go way up.

This part is actually quite easy. There are several places where StarCraft checks for a unit's maximum shields, which we are going to modify.

Code in game.h (but you can put it anywhere!)
  Code:
//Offsets to some internal constants
BYTE* const shieldShiftVal1 = (BYTE*) 0x00493578;   //unitCanRechargeShields();
BYTE* const shieldShiftVal2 = (BYTE*) 0x00493EE8;   //orders_RechargeShields1();
BYTE* const shieldShiftVal3 = (BYTE*) 0x004934C3;   //ApplyRechargeShields();
BYTE* const shieldShiftVal4 = (BYTE*) 0x00493501;   //ApplyRechargeShields();

All of these constants have a value of 8. This number is used in an arithmetic shift left operation that effectively multiplies the default plasma shields value by 256 so that it can be compared to internal shield points. Now, what if we change this number to 9?

Code in game.cpp (A helper function)
  Code:
void MemoryPatch(BYTE* pSrc, BYTE value) {
   DWORD oldProt = 0;
   VirtualProtect((LPVOID)pSrc, sizeof(BYTE), PAGE_EXECUTE_READWRITE, &oldProt);
   *pSrc = value;
   VirtualProtect((LPVOID)pSrc, sizeof(BYTE), oldProt, &oldProt);
}

Also...

Code in qdp.cpp (inside the InitializePlugin() function)
  Code:
   MemoryPatch(shieldShiftVal1, 9);
   MemoryPatch(shieldShiftVal2, 9);
   MemoryPatch(shieldShiftVal3, 9);
   MemoryPatch(shieldShiftVal4, 9);

This effectively makes StarCraft multiply max shield points by 512 for checking while recharging shields.

Step 2. Fix Plasma Shield Regeneration
The changes above will not work by themselves because SC (1) checks a unit's shields for regeneration and (2) sets it to max shield points if it's too big. We are going to prevent this behavior.

Code in game.h:
  Code:
const BYTE* const SHIELD_REGEN = (BYTE*) 0x004EC2E5;
DWORD RegenerateShieldsInject  = 0x004EC2DF;
DWORD RegenerateShieldsReturn  = 0x004EC2F0;
DWORD RegenerateShieldsSkip    = 0x004EC2FE;

//Don't fix Plasma Shields overflow
void __declspec(naked) RegenerateShields() {
   __asm {
      CMP ecx, eax               ; ecx = current shields, eax = full shields
      JAE SkipShieldRegen        ; Skip if current shields >= max shields
      SUB eax, ecx               ; eax -= ecx
      PUSH ebx
      MOV ebx, [SHIELD_REGEN]
      MOVZX ebx, BYTE PTR [ebx]  ; ebx = shield regen amount per cycle
      CMP eax, ebx
      JAE RegenShields           ; If remaining gap is bigger than default regen amount, use the default
      MOV ebx, eax               ; Otherwise, fill up to max hp
RegenShields:
      ADD ecx, ebx               ; ecx = current shields, ebx = modified shield regen amount
      POP ebx
      MOV [esi + 0x60], ecx      ; update UNIT struct with the new shield amount
      JMP [RegenerateShieldsReturn]
SkipShieldRegen:
      JMP [RegenerateShieldsSkip]
   }
}

Yeah, assembly code is a PITA but if you are into serious plug-in editing, it's a must. Wikibooks has a decent guide to x86 assembly here.

Code in qdp.cpp (inside the InitializePlugin() function)
  Code:
JmpCallPatch(RegenerateShields, RegenerateShieldsInject);


Aaaand...done! I'm also uploading the compiled plugin (probably does not interfere with other plugins).
Note: The weird-colored wireframe is an entirely unintended side-effect. But hey, it's cool.
Attachments
SB_Overcharge.zip
(17.79 KiB) Downloaded 679 times
overcharge.png
<<

Hercanic

User avatar

Site Admin

Posts: 478

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

Location: Korea

Post November 19th, 2012, 10:11 pm

Re: [EXAMPLE] Plasma Shields Overcharge

That's pretty cool. It's like a Medic and Science Vessel made sweet, sweet love. The wireframe color change is definitely interesting, ironically taking care of visual notification for players.
<<

RadiganP

Posts: 10

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

Post February 8th, 2013, 3:59 am

Re: [EXAMPLE] Plasma Shields Overcharge

After tinkering for a while, I found that I wanted to limit the 200% recharge to ground units only (600HP Carriers seemed OP). I also wanted overcharged shields to slowly return to 100% (default maximum), just like how overheal wears off in Team Fortress 2.

Part 2-1. Using a New Maximum Shields Array

The method used in post #1 was acceptable, but it wasn't flexible enough for this task. So I thought of a new plan: instead of overwriting an obscure "rshift factor", why not replace the Maximum Shield Points array with a custom-built one? That is, make StarCraft reference a separate, custom-built array for recharging/regenerating shield values.

The following code goes in game.h:
  Code:
//Use custom array for maximum shield points
DWORD* const maxShieldArrayRef1 = (DWORD*) 0x0049356F;    //unitCanRechargeShields();
DWORD* const maxShieldArrayRef2 = (DWORD*) 0x00493EDF;    //orders_RechargeShields1();
DWORD* const maxShieldArrayRef3 = (DWORD*) 0x004934BD;    //ApplyRechargeShields();
DWORD* const maxShieldArrayRef4 = (DWORD*) 0x004934FB;    //ApplyRechargeShields();

WORD MaxRechargeShields[228];

//An initializer function that must be called inside nextFrame() at the beginning of the game
void SetRechargeShieldsAmount() {
    for (int i = 0; i < 228; i++) {
        if (!(UnitsDat_PrototypeFlags[i] & UPF_Flyer))
            MaxRechargeShields[i] = 2 * UnitsDat_MaxShields[i];
        else
            MaxRechargeShields
[i] = UnitsDat_MaxShields[i];
    }
}
 


The following code goes in qdp.cpp, inside the InitializePlugin() function:
  Code:

    MemoryPatch
(maxShieldArrayRef1, (DWORD)&MaxRechargeShields);
    MemoryPatch(maxShieldArrayRef2, (DWORD)&MaxRechargeShields);
    MemoryPatch(maxShieldArrayRef3, (DWORD)&MaxRechargeShields);
    MemoryPatch(maxShieldArrayRef4, (DWORD)&MaxRechargeShields);
 




Part 2-2. An Improved Shield Regeneration Function

I wanted overcharged Plasma Shields to slowly decrease back to 100%, at an identical rate to normal shield regeneration. Writing assembly for all of this would be tiresome, though, and I found a new way to do it:

Code in game.h:
  Code:

DWORD RegenerateShieldsInject    
= 0x004EC2E6;
DWORD RegenerateShieldsReturn    = 0x004EC2F0;

void __declspec(naked) RegenerateShields() {
    UNIT *unit;
    DWORD maxShields;
    __asm {
        PUSHAD
        MOV ebp
, esp
        MOV unit
, esi
    
}

    maxShields = UnitsDat_MaxShields[unit->unitId] << 8;
    if (unit->shields < maxShields) {
        DWORD regen = maxShields - unit->shields;
        if (regen > 7) regen = 7;
        unit->shields += regen;
    }
    else if (unit->shields > maxShields) {
        DWORD loss = unit->shields - maxShields;
        if (loss > 7) loss = 7;
        unit->shields -= loss;
    }

    __asm {
        POPAD
        JMP 
[RegenerateShieldsReturn]
    }
}
 


The RegenerateShields() function allows writing the core logic in C++.

Return to Plugins

Who is online

Users browsing this forum: No registered users and 1 guest

cron
phpBBST Software