Built-in Extended Object Types
Object types related to game content.
Reference:
https://cafe.naver.com/edac/120138
Extended Object Types
-
CUnit
EPDCUnitMap is another way to write CUnit.
A unit instance operation object that allows operating on a specific unit on the map. In the editor, "Unit" refers to a unit type, not a unit instance.
CUnit is a reference type, and the unit instance it operates on isdata that needs to be synchronized.object CUnit {function constructor(epd) {}static function from_read(epd) {}static function from_ptr(ptr) {}function set_color(Player : TrgPlayer){}function reset_buildq(){}function setloc(Location : TrgLocation){}function is_burrowed(){}function is_in_transport(){}function is_in_building(){}function is_air(){}function is_hallucination(){}function is_completed(){}function is_dying(){}function die(){}function set_speed_upgrade(){}function clear_speed_upgrade(){}function set_air(){}function set_ground(){}function set_invincible(){}function clear_invincible(){}function remove_collision(){}function set_hallucination(){}function clear_hallucination(){}function power(){}function unpower(){}function set_noclip(){}function clear_noclip(){}function set_gathering(){}function clear_gathering(){}function check_status_flag(Value){}function check_status_flag(Value, Mask){}function set_status_flag(Value){}function set_status_flag(Value, Mask){}function clear_status_flag(Value){}var prev;// CUnitMember(0x000)var next;// CUnitMember(0x004) //linkvar hp;// Member(0x008, MemberKind.DWORD) //displayed value is ceil(healthPoints/256)var sprite;// CSpriteMember(0x00C)var moveTargetPos;// Member(0x010, MemberKind.POSITION)var moveTargetX;// Member(0x010, MemberKind.POSITION_X)var moveTargetY;// Member(0x012, MemberKind.POSITION_Y)var moveTarget;// CUnitMember(0x014)var moveTargetUnit;// CUnitMember(0x014)var nextMovementWaypoint;// Member(0x018, MemberKind.POSITION) //The next way point in the path the unit is following to get to its destination. Equal to moveToPos for air units since they don't need to navigate around buildings.var nextTargetWaypoint;// Member(0x01C, MemberKind.POSITION) //The desired positionvar movementFlags;// MovementFlags(0x020, MemberKind.BYTE)var currentDirection1;// Member(0x021, MemberKind.BYTE) //current direction the unit is facingvar turnRadius;// Member(0x022, MemberKind.BYTE) //flingyvar velocityDirection1;// Member(0x023, MemberKind.BYTE) //usually only differs from the currentDirection field for units that can accelerate and travel in a different direction than they are facing. For example Mutalisks can change the direction they are facing faster than then can change the direction they are moving.var flingyID;// Member(0x024, MemberKind.FLINGY)var unknown0x26;// Member(0x026, MemberKind.BYTE)var flingyMovementType;// Member(0x027, MemberKind.BYTE)var pos;// Member(0x028, MemberKind.POSITION) //Current position of the unitvar posX;// Member(0x028, MemberKind.POSITION_X)var posY;// Member(0x02A, MemberKind.POSITION_Y)var haltX;// Member(0x02C, MemberKind.DWORD)var haltY;// Member(0x030, MemberKind.DWORD)var topSpeed;// Member(0x034, MemberKind.DWORD)var currentSpeed1;// Member(0x038, MemberKind.DWORD)var currentSpeed2;// Member(0x03C, MemberKind.DWORD)var currentVelocityX;// Member(0x040, MemberKind.DWORD)var currentVelocityY;// Member(0x044, MemberKind.DWORD)var acceleration;// Member(0x048, MemberKind.WORD)var currentDirection2;// Member(0x04A, MemberKind.BYTE)var velocityDirection2;// Member(0x04B, MemberKind.BYTE) //pathing relatedvar playerID;// Member(0x04C, MemberKind.TRG_PLAYER)var owner;// Member(0x04C, MemberKind.TRG_PLAYER)var orderID;// Member(0x04D, MemberKind.UNIT_ORDER)var order;// Member(0x04D, MemberKind.UNIT_ORDER)var orderState;// Member(0x04E, MemberKind.BYTE)var orderSignal;// Member(0x04F, MemberKind.BYTE)var orderUnitType;// Member(0x050, MemberKind.TRG_UNIT)var unknown0x52;// Member(0x052, MemberKind.WORD) //2-byte paddingvar cooldown;// Member(0x054, MemberKind.DWORD)var orderTimer;// Member(0x054, MemberKind.BYTE)var gCooldown;// Member(0x055, MemberKind.BYTE)var aCooldown;// Member(0x056, MemberKind.BYTE)var spellCooldown;// Member(0x057, MemberKind.BYTE)var groundWeaponCooldown;// Member(0x055, MemberKind.BYTE)var airWeaponCooldown;// Member(0x056, MemberKind.BYTE)var orderTargetPos;// Member(0x058, MemberKind.POSITION) //ActionFocusvar orderTargetXY;// Member(0x058, MemberKind.POSITION)var orderTargetX;// Member(0x058, MemberKind.POSITION_X)var orderTargetY;// Member(0x05A, MemberKind.POSITION_Y)var orderTarget;// CUnitMember(0x05C)var orderTargetUnit;// CUnitMember(0x05C)var shield;// Member(0x060, MemberKind.DWORD)var unitID;// Member(0x064, MemberKind.TRG_UNIT)var unitType;// Member(0x064, MemberKind.TRG_UNIT)var unknown0x66;// Member(0x066, MemberKind.WORD) //2-byte paddingvar prevPlayerUnit;// CUnitMember(0x068)var nextPlayerUnit;// CUnitMember(0x06C)var subUnit;// CUnitMember(0x070)var orderQueueHead;// UnsupportedMember(0x074, MemberKind.DWORD) //COrdervar orderQueueTail;// UnsupportedMember(0x078, MemberKind.DWORD)var autoTargetUnit;// CUnitMember(0x07C)var connectedUnit;// CUnitMember(0x080) //larva, in-transit, addonsvar orderQueueCount;// Member(0x084, MemberKind.BYTE) //may be count in addition to first since can be 2 when 3 orders are queuedvar orderQueueTimer;// Member(0x085, MemberKind.BYTE) //Cycles down from from 8 to 0 (inclusive). See also 0x122.var unknown0x86;// Member(0x086, MemberKind.BYTE)var attackNotifyTimer;// Member(0x087, MemberKind.BYTE) //Prevent "Your forces are under attack." on every attackvar prevUnitType;// UnsupportedMember(0x088, MemberKind.TRG_UNIT) //zerg buildings while morphingvar lastEventTimer;// UnsupportedMember(0x08A, MemberKind.BYTE)var lastEventColor;// UnsupportedMember(0x08B, MemberKind.BYTE) //17 : was completed (train, morph), 174 : was attackedvar unknown0x8C;// Member(0x08C, MemberKind.WORD) // might have originally been RGB from lastEventColorvar rankIncrease;// Member(0x08E, MemberKind.BYTE)var killCount;// Member(0x08F, MemberKind.BYTE)var lastAttackingPlayer;// Member(0x090, MemberKind.TRG_PLAYER)var secondaryOrderTimer;// Member(0x091, MemberKind.BYTE)var AIActionFlag;// Member(0x092, MemberKind.BYTE)var userActionFlags;// Member(0x093, MemberKind.BYTE) //2 : issued an order, 3 : interrupted an order, 4 : hide self before death (self-destruct?)var currentButtonSet;// Member(0x094, MemberKind.WORD)var isCloaked;// Member(0x096, MemberKind.BOOL)var movementState;// Member(0x097, MemberKind.BYTE)};const unit = CUnit.cast(v) // Convert function argument or return value to CUnit objectconst unit = CUnit(EPD) // Create CUnit object from structure offset EPD valueconst unit = CUnit(EPD, ptr=ptr) // Create CUnit object from structure offset EPD value and ptr valueconst unit = CUnit.from_read(EPD) // Read from the memory address storing EPD value and create CUnit object. If the address is empty, unit is 0const unit = CUnit.from_ptr(ptr) // Calculate EPD from ptr value and create CUnit type. Cache ptr value at call location to avoid recalculating EPDconst unit = CUnit(EPD).subUnit // CUnit type member of CUnit instance// Example: Per-trip resource collection exceeding 256const bonusMineral = PVariable(list(492, 0, 0, 0, 0, 0, 0, 0)); // P1 workers: 492 + 8 = collect up to 500 mineralsconst bonusGas = PVariable(list(992, 0, 0, 0, 0, 0, 0, 0)); // P1 workers: 992 + 8 = collect up to 1000 gasfunction loopUnit() {foreach(unit : EUDLoopCUnit()) {epdswitch(unit + 0x64/4, 255) { // Unit typecase $U("Mineral Field (Type 1)"), $U("Mineral Field (Type 2)"), $U("Mineral Field (Type 3)"):// Several workers collect a mineral patch at the same timeunit.gatherQueueCount = 0;unit.nextGatherer = 0;break;case $U("Terran SCV"), $U("Zerg Drone"), $U("Protoss Probe"):const worker = unit;// If the worker is not carrying anything and has extra resources, provide extra resources// worker.unknown0x66 = Extra collection amount (mineral or gas)// worker.resourceType = Resource type (1 = mineral, 2 = gas)// worker.connectedUnit = Resource (mineral patch/gas building) addressif(worker.resourceCarryAmount == 0 && worker.unknown0x66 >= 1) {if(worker.resourceType == 1) {SetResources(worker.owner, Add, worker.unknown0x66, Ore);} else if(worker.resourceType == 2) {SetResources(worker.owner, Add, worker.unknown0x66, Gas);}worker.resourceType = 0;worker.unknown0x66 = 0;}epdswitch(worker + 0x4D/4, 0xFF00) { // Ordercase EncodeUnitOrder("Harvesting Minerals") * 256, EncodeUnitOrder("Enter/Exit Gas Mine") * 256: {worker.connectedUnit = worker.orderTarget; // Store the mineral address in unused space// Indicates mineral/gasworker.resourceType = 1 + l2v(worker.order == EncodeUnitOrder("Enter/Exit Gas Mine"));break;}case EncodeUnitOrder("Reset Collision (Harvester&Mine)") * 256: {// Operates after mining minerals or gasif(worker.connectedUnit >= 1 && worker.resourceType >= 1 && worker.resourceType <= 2) {const player = worker.owner;const bonusAmount = (worker.resourceType == 1) ? bonusMineral[player] : bonusGas[player];const targetResource = worker.connectedUnit; // Mineral patch/gas building CUnit!const resourceAmount = targetResource.resourceAmount;// If the remaining amount of mineral/gas building is less than the extra collection amountif (resourceAmount < bonusAmount) {// Collect all remaining amountsworker.unknown0x66 = resourceAmount;targetResource.resourceAmount = 0;} else {// If the mineral patch/gas building has enough extra collection amounts, collect moreworker.unknown0x66 = bonusAmount;targetResource.resourceAmount -= bonusAmount;}}}case EncodeUnitOrder("Can Harvest Minerals") * 256:if(worker.orderState == 2) {worker.order = py_str("Move to Harvest Minerals");worker.orderState = 1;}break;}break;}}} -
UnitGroup
UnitGroup is a unit instance container optimized with CPTricks.
object GUnit {function remove(){} // Remove itself from the UnitGroup it belongs toconst dying : EUDGUnitIter; // It is not actually an iterator. If the unit is alive, the foreach code block will not execute. If the unit dies, after executing the code block in foreach, the dead unit will remove itself from the UnitGroup it belongs to.}object UnitGroup {function add(epd) {}const cp_loop : EUDGUnitIter; // cp_loop returns an iterator that iterates over all unit instances in the container. For now, let's call this unit instance type GUnit. GUnit has a remove method to remove itself from the UnitGroup it belongs to};// epScript example// UnitGroup Declarationconst zerglings = UnitGroup(1000);// max capacity = 1000, will use CPTrick// Register Unitzerglings.add(epd);// Loop UnitGroupforeach(unit : zerglings.cploop) {// Run Triggers on **any** zerglings (alive or dead)foreach(dead : unit.dying) {// Run Triggers on dead zerglings} // <- dead zergling will be removed at end of *dying* block// Run Triggers on alive zerglings}// example usagefunction afterTriggerExec() {const zerglings = UnitGroup(1000);foreach(cunit : EUDLoopNewCUnit()) {epdswitch(cunit + 0x64/4, 255) {case $U("Zerg Zergling"):zerglings.add(epd);break;}}foreach(unit : zerglings.cploop) {foreach(dead : unit.dying) {// spawn Infested Terran when zergling diesdead.move_cp(0x4C / 4); // Ownerconst owner = bread_cp(0, 0);dead.move_cp(0x28 / 4); // Unit Positionconst x, y = posread_cp(0);setloc("loc", x, y);CreateUnit(1, "Infested Terran", "loc", owner);}}} -
CSprite
Sprite instance operation object type
class CSpriteFlags(EnumMember):DrawSelCircle = Flag(0x01) # Draw selection circleAllySel1 = Flag(0x02)AllySel2 = Flag(0x04)Selected = Flag(0x08) # Draw HP bar, SelectedFlag4 = Flag(0x10)Hidden = Flag(0x20) # HiddenBurrowed = Flag(0x40) # BurrowedIscriptCode = Flag(0x80) # Iscript unbreakable code sectionclass CSprite(EPDOffsetMap):__slots__ = "_ptr"prev = CSpriteMember(0x00)next = CSpriteMember(0x04)sprite = Member(0x08, MemberKind.SPRITE)playerID = Member(0x0A, MemberKind.TRG_PLAYER) # officially "creator"# 0 <= selectionIndex <= 11. Index in the selection area at bottom of screen.selectionIndex = Member(0x0B, MemberKind.BYTE)# Player bits indicating the visibility for a player (not hidden by the fog-of-war)visibilityFlags = Member(0x0C, MemberKind.BYTE)elevationLevel = Member(0x0D, MemberKind.BYTE)flags = CSpriteFlags(0x0E, MemberKind.BYTE)selectionTimer = Member(0x0F, MemberKind.BYTE)index = Member(0x10, MemberKind.WORD)unknown0x12 = Member(0x12, MemberKind.BYTE)unknown0x13 = Member(0x13, MemberKind.BYTE)pos = Member(0x14, MemberKind.POSITION)posX = Member(0x14, MemberKind.POSITION_X)posY = Member(0x16, MemberKind.POSITION_Y)mainGraphic = Member(0x18, MemberKind.DWORD) # officially "pImagePrimary"imageHead = Member(0x1C, MemberKind.DWORD)imageTail = Member(0x20, MemberKind.DWORD)def __init__(self, epd: int_or_var, *, ptr: int_or_var | None = None) -> None:@classmethoddef cast(cls: type[T], _from: int_or_var) -> T:@classmethoddef from_ptr(cls: type[T], ptr: int_or_var) -> T:@classmethoddef from_read(cls: type[T], epd) -> T:@propertydef ptr(self) -> int | c.EUDVariable: