-
Notifications
You must be signed in to change notification settings - Fork 5
SCData Explained: An Easy Way to Modify SC Data With Code
As the initial contributor of the feature https://github.com/armoha/eudplib/pull/26, I am glad to see it finally integrated into the main branch.
The TrgUnit, Weapon, Flingy, Sprite, Image, Upgrade, Tech, and UnitOrder classes have been changed and now it is possible to change StarCraft data through their members.
The TrgPlayer class has also been changed to support modifying some of the player-related data.
// Make Marine max health 60
TrgUnit("Terran Marine").maxHp = 60 * 256;
// Give Ghost Shield
TrgUnit("Terran Ghost").hasShield = true;
// Increase Vulture Attack by 3
TrgUnit("Terran Vulture").groundWeapon.damage += 3;
// Make Zergling Draw Functionality into Hallucination
Image("Zergling").drawingFunction = 16;
// Show Player 7 Gas
printAll("\x04P7 가스: {}", P7.gas);
P1.terranSupplyMax = 800; // Make max Terran supply to 400These classes support cast and can be used conveniently in typed EUD functions or EUD structs.
function make_unit_stronger(unitID: TrgUnit) {
unitID.maxHp += 20;
unitID.maxShield += 20;
}
…
make_unit_stronger("Protoss High Templar");Additionally, CUnit and CSprite have been changed so that the members that previously gave unit numbers, player numbers, etc. now return the corresponding classes.
// hero is a CUnit
hero.unitType.maxHp += 20 * 256;
hero.owner.ore += 100;(Most of this copied from https://github.com/armoha/eudplib/pull/26)
Previously it's surprisingly difficult to manipulate SC data via code(eps/plib) without the help of EUD Editor. For example, suppose we want to increase Fragmentation Grenade's damage by 3.
We have to find the address (0x656EB0) and size (2) of the weapon damage amount offset, and need to write in the offset. (We need a reference such as EUD Book)
const offset = 0x656EB0 + EncodeWeapon("Fragmentation Grenade") * 2;
wwrite(offset, wread(offset) + 3);In this code, the only thing that makes sense (in terms of understanding the code by reading) is the EncodeWeapon part. What is 0x656EB0 supposed to do? Why * 2? Is this code increasing fragmentation grenade's range, or splash inner radius, or damage? While eps-server provides some information for offsets, this is far from being a comfortable coding experience.
Some mapmakers have built their own libraries to solve this issue - for example, one example could be Westreed's DataEdit.
import DataEdit as de;
…
const frag_grenade = de.DWeapon(EncodeWeapon("Fragmentation Grenade"));
frag_grenade.SetDamageAmount(frag_grenade.GetDamageAmount() + 3);Now we know what the code is doing, but it's still a bit verbose. What if the Vulture weapon is not fixed to Fragmentation Grenade, but can be changed? What if we need to increase the damage of the current weapon by 3?
import DataEdit as de;
…
const vulture_weapon = de.DUnit($U("Terran Vulture")).GetGroundWeapon();
const frag_grenade = de.DWeapon(vulture_weapon);
frag_grenade.SetDamageAmount(frag_grenade.GetDamageAmount() + 3);Does it need to be this complex?
There is also the issue of the efficiency of the implementation. In the case of DataEdit, it's written entirely in eps, making it extremely inefficient. However, a Python library would also result in a lot of unnecessary triggers without a significant amount of optimization. Therefore, I thought it's important to embed this functionality within eudplib to solve the problems.
TrgUnit("Terran Vulture").groundWeapon.damage += 3;The same thing neatly reduced into a single line.
The TrgUnit, Weapon, Flingy, Sprite, Image, Upgrade, Tech, UnitOrder, and TrgPlayer classes take a number, variable, or string as a constructor argument (the same form as the EncodeXXX).
const vulture_data = TrgUnit("Terran Vulture");Each class has a set of members. One can access/change SC data by reading/writing to those members. The byte size will be automatically managed. Remember that HP needs to be multiplied by 256; (unlike in the case of CUnit, shields should NOT be multiplied by 256).
vulture_data.maxHp = 100 * 256;If the corresponding data is of a specific type, it returns a class of that type. However, reading/writing a number also works.
const weapon_data = vulture_data.groundWeapon; // weapon_data is of Weapon type
weapon_data.damage += 3;
vulture_data.groundWeapon = 0; // changes to weapon ID 0
printAll("{}", vulture_data.groundWeapon + 10); // 10 will be printedFor players, all of the constants like P1, P2, ... have been updated to TrgPlayer. However, use only P1 to P12, as using things like Force1 and Foes in the same way will not work correctly.
P1.ore += 70;As mentioned above, all these classes support casting and can be used in eps functions or objects easily.
function make_unit_stronger(unitID: TrgUnit) {
unitID.maxHp += 20 * 256;
unitID.maxShield += 20;
}
…
make_unit_stronger("Protoss High Templar");object HeroData {
var unitID: TrgUnit;
function make_me_stronger() {
this.unitID.maxHp += 20 * 256;
this.unitID.maxShield += 20;
}
};However, it is strongly advised to not directly use .cast for normal use. Due to the additional optimizations for automatic casting as shown above, casting directly can lead to unexpected results. I recommend calling the constructor directly instead of casting.
var a = 0;
const u = TrgUnit.cast(a);
a++;
printAll("{}", u); // This will print 1, not 0
var b = 0;
const u2 = TrgUnit(b);
b++;
printAll("{}", u2); // Prints 0 as expectedThe = means that they are referring to the same thing. (Two names in one data)
Comma doesn't mean anything; it's just grouping similar things together.
graphic = flingy (Flingy Type)
subUnit (TrgUnit Type)
constructionGraphic
startDirection
hasShield
maxShield
maxHp
elevation
movementFlags
rank
computerIdleOrder, humanIdleOrder, returnToIdleOrder, attackUnitOrder, attackMoveOrder (UnitOrder Type)
groundWeapon, airWeapon (TrgUnit Type)
maxGroundHits, maxAirHits
AIFlags
baseProperty
seekRange, sightRange
armorUpgrade
sizeType
armor
rightClickAction
readySound, whatSoundStart, whatSoundEnd, pissedSoundStart, pissedSoundEnd, yesSoundStart, yesSoundEnd
buildingDimensions (Position Type)
portrait
mineralCost, gasCost
timeCost
requirementOffset
groupFlags
supplyProvided, supplyUsed
transportSpaceProvided, transportSpaceRequired
buildScore, killScore
nameString
broodWarFlag, stareditAvailabilityFlags
label
graphic = flingy (Flingy Type)
targetFlags
minRange
maxRange
upgrade = upgradeID
damageType
behavior
removeAfter
explosionType
splashInnerRadius, splashMiddleRadius, splashOuterRadius
damage
damageBonus
cooldown
damageFactor
image = imageID (Image Type)
isVisible
isTurnable = graphicTurn
isClickable
useFullIscript
drawIfCloaked
drawingFunction
iscript = iscriptID
mineralCostBase
mineralCostFactor
gasCostBase
gasCostFactor
timeCostBase
timeCostFactor
requirementOffset
icon
label
race
maxLevel
broodWarFlag
mineralCost
gasCost
timeCost
energyCost
requirementOffset
icon
label
race
researched
broodWarFlag
UnitOrder
label
useWeaponTargeting
canBeInterrupted
canBeQueued
canBeObstructed
weapon = weaponID
techUsed
animation
buttonIcon
requirementOffset
obscuredOrder
mineral = ore
gas
cumulativeGas
cumulativeMineral = cumulativeOre
zergControlAvailable, zergControlUsed, zergControlMax
terranSupplyAvailable, terranSupplyUsed, terranSupplyMax
protossPsiAvailable, protossPsiUsed, protossPsiMax