Staredit Network > Forums > Modding Assistance > Topic: Plugin request help
Plugin request help
Nov 6 2018, 12:11 pm
By: Lagi  

Nov 6 2018, 12:11 pm Lagi Post #1



Can someone help how to write following plugins:

1. grant Ghost detector, but only if Oculus Implant upgrade is researched.
2. reduce attack range of Blind units to 3 (in game range, in editor it appear to be 6).
3. change Restoration spell from point target into Area Of Effect
4. SCV auto repair command toggle.

I would appreciate some code examples or pointing me to the ready code (this would save me my time on learning).
I think they are quite simple or already done. For sure someone know how to do it (i saw it implement in SC:Revolution).

thanks.



None.

Nov 6 2018, 7:35 pm Lagi Post #2



SOLVED Optic Flare reduce attack range

blinded units has max 3 attack range

open "weapon_range.cpp"

add new variable:
u32 finalRange = 0;


input at the code end:
Quote
....
bonusAmount += 96;
break;
}

if (unit->isBlind == 1 && (weapons_dat::MaxRange[weaponId] + bonusAmount) < 96)
finalRange = 96;
else
finalRange= weapons_dat::MaxRange[weaponId] + bonusAmount;

return finalRange;

dont forget to un-commnet hook inject in initialize.cpp (if you using Framework from here)

Quote
hooks::injectSightRangeHook();
hooks::injectUnitMaxEnergyHook();*/
hooks::injectWeaponRangeHooks();
/*
hooks::injectUnitTooltipHook();
hooks::injectSpellcasterAI();


Post has been edited 1 time(s), last time on Nov 9 2018, 9:21 am by Lagi.



None.

Nov 6 2018, 9:18 pm Lagi Post #3



SOLVED grant Detector with upgrade

at first give Ghost detector ability in .dat editor.

below plugin is removing Detecting ability, its checking if detector unit can see invisible units (Ghost and Overlord can not without Vision Upgrade in my case).

in Detector.cpp

Quote
unitCanDetectHook(CUnit* unit) {

return (
units_dat::BaseProperty[unit->id] & UnitProperty::Detector &&
unit->status & UnitStatus::Completed &&
!(unit->status & UnitStatus::DoodadStatesThing) &&
unit->lockdownTimer == 0 &&
unit->stasisTimer == 0 &&
unit->maelstromTimer == 0 &&
unit->isBlind == 0 &&
(!(unit->id == UnitId::TerranGhost &&
UpgradesSc->currentLevel[unit->playerId][UpgradeId::OcularImplants] == 0)) &&
(!(unit->id == UnitId::ZergOverlord &&
UpgradesSc->currentLevel[unit->playerId][UpgradeId::Antennae] == 0))
);

}

as always dont forget to un-comment hook injection in initialize.cpp

Post has been edited 1 time(s), last time on Nov 6 2018, 10:15 pm by Lagi.



None.

Nov 8 2018, 7:20 pm Voyager7456 Post #4

Responsible for my own happiness? I can't even be responsible for my own breakfast

Quote from Lagi
Can someone help how to write following plugins:

1. grant Ghost detector, but only if Oculus Implant upgrade is researched.
2. reduce attack range of Blind units to 3 (in game range, in editor it appear to be 6).
3. change Restoration spell from point target into Area Of Effect
4. SCV auto repair command toggle.

I would appreciate some code examples or pointing me to the ready code (this would save me my time on learning).
I think they are quite simple or already done. For sure someone know how to do it (i saw it implement in SC:Revolution).

thanks.

I've implemented these before, but I don't have access to my code examples at the moment. Hopefully pointing you to some relevant spots in GPTP will be useful:

For #3, I would suggest looking in wpnspellhit.cpp. Code there controls the on-hit effect of Restoration. Take a look at the code in the same file for EMP Shockwave, that's a fairly clear example of how to make an AOE on-hit effect.

For #4, there are a couple things to think about:
How to toggle the auto-repair state: Using a button with the Carrier Stop/Reaver Stop action works great for this, because they're orders that trigger as soon as the button is pressed.
How to locate repair target: (The UnitFinder class is a good tool for this - checkout the isUnderDarkSwarm() function for an example of how this works)
When an SCV is ready to repair: (Idle, not dead, has a valid target, the player has sufficient resources)



all i am is a contrary canary
but i'm crazy for you
i watched you cradling a tissue box
sneezing and sniffling, you were still a fox


Modding Resources: The Necromodicon [WIP] | Mod Night
My Projects: SCFC | ARAI | Excision [WIP] | SCFC2 [BETA] | Robots vs. Humans | Leviathan Wakes [BETA]


Nov 11 2018, 1:58 pm Lagi Post #5



Quote from Voyager7456
For #3, I would suggest looking in wpnspellhit.cpp. Code there controls the on-hit effect of Restoration. Take a look at the code in the same file for EMP Shockwave, that's a fairly clear example of how to make an AOE on-hit effect.

my efforts so far (ok,ok I know " i didnt take enough of it":

wpnspellhit.cpp
after initial " I can do it", I just start copy paste EMP code :hurr: .

Quote
void RestoreHit(CUnit* attacker, int x, int y, CBullet* bullet) {

static u16* const maxBoxRightValue = (u16*) 0x00628450; //should usually be mapTileSize->width * 32
static u16* const maxBoxBottomValue = (u16*) 0x006284B4; //should usually be mapTileSize->height * 32

Box16 area_of_effect;

CUnit** unitsInAreaOfEffect;
CUnit* current_unit;

//define the base area of effect
area_of_effect.left = (u16)x - weapons_dat::InnerSplashRadius[WeaponId::EMP_Shockwave];
area_of_effect.right = (u16)x + weapons_dat::InnerSplashRadius[WeaponId::EMP_Shockwave];
area_of_effect.top = (u16)y - weapons_dat::InnerSplashRadius[WeaponId::EMP_Shockwave];
area_of_effect.bottom = (u16)y + weapons_dat::InnerSplashRadius[WeaponId::EMP_Shockwave];

//check and fix effect if beyond width of map
if(area_of_effect.left < 0)
area_of_effect.left = 0;
else
if(area_of_effect.right > *maxBoxRightValue)
area_of_effect.right = *maxBoxRightValue;

//check and fix effect if beyond height of map
if(area_of_effect.top < 0)
area_of_effect.top = 0;
else
if(area_of_effect.bottom > *maxBoxBottomValue)
area_of_effect.bottom = *maxBoxBottomValue;

//find all units in area of effect and pick the first
unitsInAreaOfEffect = getAllUnitsInBounds(&area_of_effect);
current_unit = *unitsInAreaOfEffect;

while(current_unit != NULL) {

if(
current_unit != attacker && //EMP doesn't affect the attacker
(
attacker == NULL || //EMP doesn't affect the attacker
current_unit != attacker->subunit //subunit
)
)
{

if(current_unit->status & UnitStatus::IsHallucination)
current_unit->remove();
else {

//create impact overlay

u32 overlayImageId;
CUnit* overlayTargetUnit;

if(units_dat::BaseProperty[current_unit->id] & UnitProperty::MediumOverlay)
overlayImageId = ImageId::RestorationHit_Medium;
else
if(units_dat::BaseProperty[current_unit->id] & UnitProperty::LargeOverlay)
overlayImageId = ImageId::RestorationHit_Large;
else
overlayImageId = ImageId::RestorationHit_Small;

if(current_unit->subunit != NULL)
overlayTargetUnit = current_unit->subunit;
else
overlayTargetUnit = current_unit;

(overlayTargetUnit->sprite)->createTopOverlay(overlayImageId,0,0,0);

//remove status effects and corresponding overlays
//timers set to 0 twice reflect the original code

current_unit->parasiteFlags = 0;
current_unit->isBlind = 0;

if(current_unit->ensnareTimer != 0) {

current_unit->ensnareTimer = 0;
current_unit->removeOverlay(ImageId::EnsnareOverlay_Small,ImageId::EnsnareOverlay_Large);
current_unit->ensnareTimer = 0;

//specific update following ensnare removal
current_unit->updateSpeed();

}

if(current_unit->plagueTimer != 0) {
current_unit->plagueTimer = 0;
current_unit->removeOverlay(ImageId::PlagueOverlay_Small,ImageId::PlagueOverlay_Large);
current_unit->plagueTimer = 0;
}

if(current_unit->irradiateTimer != 0) {
current_unit->irradiateTimer = 0;
current_unit->removeOverlay(ImageId::Irradiate_Small,ImageId::Irradiate_Large);
current_unit->irradiateTimer = 0;

//specific updates following irradiate removal
current_unit->irradiatedBy = NULL;
current_unit->irradiatePlayerId = 8;

}

if(current_unit->lockdownTimer != 0)
current_unit->removeLockdown();

if(current_unit->maelstromTimer != 0)
current_unit->removeMaelstrom();

if(current_unit->acidSporeCount != 0)
current_unit->removeAcidSpores();

//was hardcoded in original code
scbw::refreshConsole();

}

} //void RestoreHit(CUnit* target)



unitsInAreaOfEffect++; //go on next unit of the list (or null)
current_unit = *unitsInAreaOfEffect;

} //while(current_unit != NULL)

//reload the previous temporary unit list from before the call to getAllUnitsInBounds
*tempUnitsListArraysCountsListLastIndex = *tempUnitsListArraysCountsListLastIndex - 1;
*tempUnitsListCurrentArrayCount = tempUnitsListArraysCountsList[*tempUnitsListArraysCountsListLastIndex];

} //void EMPShockwaveHit(CUnit* attacker, int x, int y)

;

next butchered file WpnSpellH.h


Quote
//The header file for the Weapon/Spell hit hook module.
#pragma once
#include <SCBW/structures/CUnit.h>
#include <SCBW/structures/CBullet.h>

namespace hooks {

void IrradiateHit(CUnit* attacker, CUnit* target, u8 attackingPlayerId); //00454E00
void OpticalFlareHit(CUnit* target, u32 attackingPlayerId, CBullet* bullet); //00455170
void RestoreHit(CUnit* attacker, int x, int y, CBullet* bullet); //00455230
void LockdownHit(CUnit* target, u8 previousLockdownTimer, CBullet* bullet); //00455380

and

wpnspellhit_inject.cpp
last but not least (because also in initialize.cpp i uncomment the inject)

Quote
void __declspec(naked) RestoreHitWrapper() {

static CUnit* attacker;
static CBullet* bullet;
static int x;
static int y;

__asm {

PUSH EBP
MOV EBP, ESP

MOV y, EAX
MOV x, ECX

MOV EAX, [EBP+0x08]
MOV attacker, EAX

MOV bullet, EBX

PUSHAD

}

hooks::RestoreHit(attacker,x,y,bullet);

__asm {
POPAD
MOV ESP, EBP
POP EBP
RETN 4
}

}

;

Its compile into successfully .gpt, that will crash Starcraft, as soon as you cast Restoration with medic, and its not Area of Effect, because required to take target.

I stop enjoying starcraft :flamer:



None.

Nov 11 2018, 2:39 pm KYSXD Post #6



Quote from Lagi
Quote from Voyager7456
For #3, I would suggest looking in wpnspellhit.cpp. Code there controls the on-hit effect of Restoration. Take a look at the code in the same file for EMP Shockwave, that's a fairly clear example of how to make an AOE on-hit effect.

Its compile into successfully .gpt, that will crash Starcraft, as soon as you cast Restoration with medic, and its not Area of Effect, because required to take target.

I stop enjoying starcraft :flamer:

I think you want it to be castable on terrain rather than AoE, those are different things.

If you want it to be castable on terrain, check `orders.dat` (IIRC) and change the targeting options. You'll also want to take a look on the `spells/cast_order` hooks.




Nov 11 2018, 7:04 pm Voyager7456 Post #7

Responsible for my own happiness? I can't even be responsible for my own breakfast

Quote from KYSXD
Quote from Lagi
Quote from Voyager7456
For #3, I would suggest looking in wpnspellhit.cpp. Code there controls the on-hit effect of Restoration. Take a look at the code in the same file for EMP Shockwave, that's a fairly clear example of how to make an AOE on-hit effect.

Its compile into successfully .gpt, that will crash Starcraft, as soon as you cast Restoration with medic, and its not Area of Effect, because required to take target.

I stop enjoying starcraft :flamer:

I think you want it to be castable on terrain rather than AoE, those are different things.

If you want it to be castable on terrain, check `orders.dat` (IIRC) and change the targeting options. You'll also want to take a look on the `spells/cast_order` hooks.

You want to do both, don't you?

This is what I had in mind for the RestoreHit function:

Code
void RestoreHit(CUnit* target, CBullet* bullet) {

        static u16* const maxBoxRightValue =             (u16*) 0x00628450;     //should usually be mapTileSize->width * 32
        static u16* const maxBoxBottomValue =             (u16*) 0x006284B4;     //should usually be mapTileSize->height * 32

        u32 x = bullet->position.x;
        u32 y = bullet->position.y;
        Box16 area_of_effect;

        CUnit** unitsInAreaOfEffect;
        CUnit* current_unit;

        //define the base area of effect
        area_of_effect.left = (u16)x -  weapons_dat::InnerSplashRadius[WeaponId::EMP_Shockwave];
        area_of_effect.right = (u16)x +  weapons_dat::InnerSplashRadius[WeaponId::EMP_Shockwave];
        area_of_effect.top = (u16)y -  weapons_dat::InnerSplashRadius[WeaponId::EMP_Shockwave];
        area_of_effect.bottom = (u16)y +  weapons_dat::InnerSplashRadius[WeaponId::EMP_Shockwave];

        //check and fix effect if beyond width of map
        if(area_of_effect.left < 0)
            area_of_effect.left = 0;
        else
        if(area_of_effect.right > *maxBoxRightValue)
            area_of_effect.right = *maxBoxRightValue;

        //check and fix effect if beyond height of map
        if(area_of_effect.top < 0)
            area_of_effect.top = 0;
        else
        if(area_of_effect.bottom > *maxBoxBottomValue)
            area_of_effect.bottom = *maxBoxBottomValue;

        //find all units in area of effect and pick the first
        unitsInAreaOfEffect = getAllUnitsInBounds(&area_of_effect);
        current_unit = *unitsInAreaOfEffect;

        while(current_unit != NULL) {

            if(current_unit->status & UnitStatus::IsHallucination)
            current_unit->remove();

            if(current_unit->playerId == bullet->sourceUnit->playerId) {
                u32 overlayImageId;
            CUnit* overlayTargetUnit;

            if(units_dat::BaseProperty[current_unit->id] & UnitProperty::MediumOverlay)
                overlayImageId = ImageId::RestorationHit_Medium;
            else
            if(units_dat::BaseProperty[current_unit->id] & UnitProperty::LargeOverlay)
                overlayImageId = ImageId::RestorationHit_Large;
            else
                overlayImageId = ImageId::RestorationHit_Small;

            if(current_unit->subunit != NULL)
                overlayTargetUnit = current_unit->subunit;
            else
                overlayTargetUnit = current_unit;

            (overlayTargetUnit->sprite)->createTopOverlay(overlayImageId,0,0,0);

            //remove status effects and corresponding overlays
            //timers set to 0 twice reflect the original code

            current_unit->parasiteFlags = 0;
            current_unit->isBlind = 0;

            if(current_unit->ensnareTimer != 0) {

                current_unit->ensnareTimer = 0;
                current_unit->removeOverlay(ImageId::EnsnareOverlay_Small,ImageId::EnsnareOverlay_Large);
                current_unit->ensnareTimer = 0;

                //specific update following ensnare removal
                current_unit->updateSpeed();

            }

            if(current_unit->plagueTimer != 0) {
                current_unit->plagueTimer = 0;
                current_unit->removeOverlay(ImageId::PlagueOverlay_Small,ImageId::PlagueOverlay_Large);
                current_unit->plagueTimer = 0;
            }

            if(current_unit->irradiateTimer != 0) {
                current_unit->irradiateTimer = 0;
                current_unit->removeOverlay(ImageId::Irradiate_Small,ImageId::Irradiate_Large);
                current_unit->irradiateTimer = 0;

                //specific updates following irradiate removal
                current_unit->irradiatedBy = NULL;
                current_unit->irradiatePlayerId = 8;

            }

            if(current_unit->lockdownTimer != 0)
                current_unit->removeLockdown();

            if(current_unit->maelstromTimer != 0)
                current_unit->removeMaelstrom();

            if(current_unit->acidSporeCount != 0)
                current_unit->removeAcidSpores();

            }

            unitsInAreaOfEffect++;                     //go on next unit of the list (or null)
            current_unit = *unitsInAreaOfEffect;

        } //while(current_unit != NULL)

        //reload the previous temporary unit list from before the call to getAllUnitsInBounds
        *tempUnitsListArraysCountsListLastIndex = *tempUnitsListArraysCountsListLastIndex - 1;
        *tempUnitsListCurrentArrayCount = tempUnitsListArraysCountsList[*tempUnitsListArraysCountsListLastIndex];
        scbw::refreshConsole();


    } //void RestoreHit(CUnit* target)


Just changing weapons/orders.dat is not enough to make Restoration target terrain, apparently. I didn't see anything super-obvious in cast_order or tech_target_check, so I'm guessing it's one of these cases where the BW content has special logic somewhere. I'll look into it deeper when I have time, unless you know what I'm missing, KYSXD.



all i am is a contrary canary
but i'm crazy for you
i watched you cradling a tissue box
sneezing and sniffling, you were still a fox


Modding Resources: The Necromodicon [WIP] | Mod Night
My Projects: SCFC | ARAI | Excision [WIP] | SCFC2 [BETA] | Robots vs. Humans | Leviathan Wakes [BETA]


Options
  Back to forum
Please log in to reply to this topic or to report it.
Members in this topic: None.
[10:50 pm]
Vrael -- Ultraviolet
Ultraviolet shouted: How about you all send me your minerals instead of washing them into the gambling void? I'm saving up for a new name color and/or glow
hey cut it out I'm getting all the minerals
[10:11 pm]
Ultraviolet -- :P
[10:11 pm]
Ultraviolet -- How about you all send me your minerals instead of washing them into the gambling void? I'm saving up for a new name color and/or glow
[2024-4-17. : 11:50 pm]
O)FaRTy1billion[MM] -- nice, now i have more than enough
[2024-4-17. : 11:49 pm]
O)FaRTy1billion[MM] -- if i don't gamble them away first
[2024-4-17. : 11:49 pm]
O)FaRTy1billion[MM] -- o, due to a donation i now have enough minerals to send you minerals
[2024-4-17. : 3:26 am]
O)FaRTy1billion[MM] -- i have to ask for minerals first tho cuz i don't have enough to send
[2024-4-17. : 1:53 am]
Vrael -- bet u'll ask for my minerals first and then just send me some lousy vespene gas instead
[2024-4-17. : 1:52 am]
Vrael -- hah do you think I was born yesterday?
[2024-4-17. : 1:08 am]
O)FaRTy1billion[MM] -- i'll trade you mineral counts
Please log in to shout.


Members Online: Ultraviolet, jun3hong