• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Indexer, Unit Indexer, and Unit Roster

This bundle is marked as pending. It has not been reviewed by a staff member yet.
  • Like
Reactions: deepstrasz
See attached map for example use of code.

Several things here:
  • Indexer (which can be used in multiple instances) will create indexes from 0 to whatever you need. Can be used for unit indexing or object allocation.
  • Unit Indexer is an example using the Indexer to index units
  • Unit Roster is an example of a "class" implemented using the Indexer. Unit Roster is used to store counts of unit types. (In this map example its used to count units in your selection). It could be used to keep track of kills of certain units.
Advantages of using the Indexer:
  • No precomplier or third party software needed to use object oriented programming
  • Ease of use with existing sc2 galaxy editor
  • Looks and feels like the sc2 API you already know
Disadvantages of using the Indexer:
  • No polymorphism (if you honestly need it in a StarCraft II map)
  • Requires manual destruction of objects, cannot make use of sc2 garbage collection system
I wrote the Indexer and Unit Roster for a map I'm developing (Cutthroat Defense). Consider checking it on the sc2 arcade. Its really fun if you enjoy defense style maps like Zillion Zerglings or 20,000 Hydralisks but found them to be missing something...

The Unit Indexer I wrote just as an example of how the Indexer can be used. I also use the Indexer for a custom timer window, which is helpful when using campaign dependencies which mysteriously mess up the default timer window.


C:
// set this value to however many units you think you will need
// or if you have a class that you intend to have many objects
const int INDEXER_MAX_INDEX = 5000;
// set this value higher if you plan on having many classes
const int INDEXER_MAX_INSTANCE_COUNT = 100;
int [INDEXER_MAX_INSTANCE_COUNT][INDEXER_MAX_INDEX] indexerFreeIndexes;
int [INDEXER_MAX_INSTANCE_COUNT] indexerFreeIndexCount;
int [INDEXER_MAX_INSTANCE_COUNT] indexerNextIndex;

int [INDEXER_MAX_INSTANCE_COUNT] indexerFreeInstances;
int indexerFreeInstanceCount = 0;
int indexerNextInstance = 0;

int [INDEXER_MAX_INSTANCE_COUNT][INDEXER_MAX_INDEX] indexerAllocatedIndexes;
bool [INDEXER_MAX_INSTANCE_COUNT][INDEXER_MAX_INDEX] indexerIndexIsAllocated;
int [INDEXER_MAX_INSTANCE_COUNT] indexerAllocatedIndexesCount;
int [INDEXER_MAX_INSTANCE_COUNT][INDEXER_MAX_INDEX] indexerIndOfAllocatedIndex;

int indexerCurrentIndex;

int IndexerNextIndex(int inst) {
    int index;
    int indOfAllocatedIndex;
    if (indexerFreeIndexCount[inst] > 0) {
        indexerFreeIndexCount[inst] -= 1;
        index = indexerFreeIndexes[inst][indexerFreeIndexCount[inst]];
    }
    else {
        index = indexerNextIndex[inst];
        indexerNextIndex[inst] += 1;
    }
   
    indOfAllocatedIndex = indexerAllocatedIndexesCount[inst];
    indexerAllocatedIndexes[inst][indOfAllocatedIndex] = index;
    indexerIndOfAllocatedIndex[inst][index] = indOfAllocatedIndex;
    indexerAllocatedIndexesCount[inst] += 1;
    indexerIndexIsAllocated[inst][index] = true;

    return index;
}

void IndexerFreeIndex(int inst, int index) {
    int indOfAllocatedIndex = indexerIndOfAllocatedIndex[inst][index];
    int allocatedIndexCount = indexerAllocatedIndexesCount[inst];
    int lastAllocatedIndex = indexerAllocatedIndexes[inst][allocatedIndexCount - 1];
    indexerFreeIndexes[inst][indexerFreeIndexCount[inst]] = index;
    indexerFreeIndexCount[inst] += 1;
    // swap out the allocated index
    // move the last index in the allocated indexes array to the position of the index we're removing
    indexerAllocatedIndexes[inst][indOfAllocatedIndex] = lastAllocatedIndex;
    // update the ind of this index to be the ind of the index we're removing
    indexerIndOfAllocatedIndex[inst][lastAllocatedIndex] = indOfAllocatedIndex;
    // subtract the allocation count by 1
    indexerAllocatedIndexesCount[inst] -= 1;
    indexerIndexIsAllocated[inst][index] = false;
}

void IndexerForEachAllocatedIndex(int inst, trigger cb) {
    int ind;
    int allocatedIndexCount = indexerAllocatedIndexesCount[inst];
    for (ind = 0; ind < allocatedIndexCount; ind += 1) {
        indexerCurrentIndex = indexerAllocatedIndexes[inst][ind];
        TriggerExecute(cb, true, true);
    }
}

int IndexerCurrentIndex() {
    return indexerCurrentIndex;
}

bool IndexerIndexIsAllocated(int inst, int index) {
    return indexerIndexIsAllocated[inst][index];
}

int IndexerAllocatedIndicesCount(int inst) {
    return indexerAllocatedIndexesCount[inst];
}

int IndexerCreate() {
    int inst;
    int index;
    int i;
    if (indexerFreeInstanceCount > 0) {
        indexerFreeInstanceCount -= 1;
        inst = indexerFreeInstances[indexerFreeInstanceCount];
    }
    else {
        inst = indexerNextInstance;
        indexerNextInstance += 1;
    }

    for (i = 0; i < INDEXER_MAX_INDEX; i += 1) {
        indexerIndexIsAllocated[inst][i] = false;
    }

    indexerFreeIndexCount[inst] = 0;
    indexerNextIndex[inst] = 0;
    indexerAllocatedIndexesCount[inst] = 0;

    return inst;
}

void IndexerDestroy(int inst) {
    indexerFreeInstances[indexerFreeInstanceCount] = inst;
    indexerFreeInstanceCount += 1;
}

C:
// easilly assign indexes to units on creation and destruction
int unitIndexer;
const int UNIT_INDEXER_CUSTOM_VALUE_INDEX = 0; // set this to whatever you need to avoid collision with other systems
trigger unitIndexerOnUnitCreated;
trigger unitIndexerOnUnitDied;
trigger unitIndexerInitialUnitRegister;
// uncomment to start adding flags or other data associated with units
//bool [INDEXER_MAX_INDEX] unitIndexerSomeUnitFlag;

int UnitIndexerTotalRegisteredUnitCount() {
    return IndexerAllocatedIndicesCount(unitIndexer);
}

void UnitIndexerRegisterUnit(unit u) {
    UnitSetCustomValue(u, UNIT_INDEXER_CUSTOM_VALUE_INDEX, IndexerNextIndex(unitIndexer));
}

void UnitIndexerUnRegisterUnit(unit u) {
    IndexerFreeIndex(unitIndexer, FixedToInt(UnitGetCustomValue(u, UNIT_INDEXER_CUSTOM_VALUE_INDEX)));
}

bool unitIndexerOnUnitCreatedCB(bool a, bool b) {
    unit u = EventUnitCreatedUnit();
    string uType = UnitGetType(u);
    // do not register missiles...
    if (!UnitTypeTestFlag(uType, c_unitFlagMissile)) {
        UnitIndexerRegisterUnit(u);
    }
    return true;
}

bool unitIndexerOnUnitDiedCB(bool a, bool b) {
    UnitIndexerUnRegisterUnit(EventUnit());
    return true;
}

bool unitIndexerInitialUnitRegisterCB(bool a, bool b) {
    // currently the unit filter is told to ignore dead or missile units, you can change the filter to whatever you need
    // if you're confused, make a trigger. make a local variable of type unit filter
    // go to value and change the filters/flags to whatever you like
    // now do a custom script action and mash the keyboard.
    // when you press save, an error will appear. You can copy the text for the unit filter local variable by highlighting it
    // and right clicking the pressing copy from the context menu. cntrl c + cntrl v won't work here, you have to use the right click
    // context menu
    unitgroup un = UnitGroup(null, c_playerAny, RegionEntireMap(), UnitFilter(0, 0, (1 << c_targetFilterMissile), (1 << (c_targetFilterDead - 32))), c_noMaxCount);
   
    UnitGroupLoopBegin(un);
    while(!UnitGroupLoopDone()) {
        UnitIndexerRegisterUnit(UnitGroupLoopCurrent());
        UnitGroupLoopStep();
    }
    UnitGroupLoopEnd();
    return true;
}

// set this as the custom script's initialization function
void initUnitIndexer() {
    unitIndexer = IndexerCreate();
    unitIndexerOnUnitCreated = TriggerCreate("unitIndexerOnUnitCreatedCB");
    TriggerAddEventUnitCreated(unitIndexerOnUnitCreated, null, null, null);
    unitIndexerOnUnitDied = TriggerCreate("unitIndexerOnUnitDiedCB");
    TriggerAddEventUnitDied(unitIndexerOnUnitDied, null);
    TriggerAddEventUnitRemoved(unitIndexerOnUnitDied, null);
    unitIndexerInitialUnitRegister = TriggerCreate("unitIndexerInitialUnitRegisterCB");
    TriggerAddEventTimeElapsed(unitIndexerInitialUnitRegister, 0, c_timeGame);
}

C:
const int UNIT_ROSTER_MAX_INSTANCE = INDEXER_MAX_INDEX;
int unitRosterIndexer;
const string UNIT_ROSTER_TABLE_PREFIX = "UNIT_ROSTER";
string [UNIT_ROSTER_MAX_INSTANCE][100] unitRosterRegisteredUnitTypes;
string [100] unitRosterUnitTypesTmp;
int [UNIT_ROSTER_MAX_INSTANCE] unitRosterRegisteredUnitTypesCount;
int [UNIT_ROSTER_MAX_INSTANCE] unitRosterRegisteredUnitCount;
int unitRosterCurrentUnitTypeCount;
string unitRosterCurrentUnitType;
int tmpUnitRoster;
int unitRosterCompareOne;
int unitRosterCompareTwo;

int UnitRosterCreate() {
    int inst = IndexerNextIndex(unitRosterIndexer);
   
    unitRosterRegisteredUnitTypesCount[inst] = 0;
    unitRosterRegisteredUnitCount[inst] = 0;

    return inst;
}

void UnitRosterClear(int inst) {
    int i;
    string uType;

    for (i = 0; i < unitRosterRegisteredUnitTypesCount[inst]; i += 1) {
        uType = unitRosterRegisteredUnitTypes[inst][i];
        DataTableValueRemove(true, UNIT_ROSTER_TABLE_PREFIX + IntToString(inst) + uType + "_COUNT");
    }

    unitRosterRegisteredUnitTypesCount[inst] = 0;
    unitRosterRegisteredUnitCount[inst] = 0;
}

void UnitRosterDestroy(int inst) {
    UnitRosterClear(inst);
    IndexerFreeIndex(unitRosterIndexer, inst);
}

bool UnitRosterHasUnitType(int inst, string uType) {
    return DataTableValueExists(true, UNIT_ROSTER_TABLE_PREFIX + IntToString(inst) + uType + "_COUNT");
}

int UnitRosterGetUnitTypeCount(int inst, string uType) {
    return DataTableGetInt(true, UNIT_ROSTER_TABLE_PREFIX + IntToString(inst) + uType + "_COUNT");
}

void UnitRosterAddUnitType(int inst, string uType, int amount) {
    int count;

    if (UnitRosterHasUnitType(inst, uType)) {
        count = UnitRosterGetUnitTypeCount(inst, uType);
        DataTableSetInt(true, UNIT_ROSTER_TABLE_PREFIX + IntToString(inst) + uType + "_COUNT", count + amount);
    }
    else {
        unitRosterRegisteredUnitTypes[inst][unitRosterRegisteredUnitTypesCount[inst]] = uType;
        unitRosterRegisteredUnitTypesCount[inst] += 1;
        DataTableSetInt(true, UNIT_ROSTER_TABLE_PREFIX + IntToString(inst) + uType + "_COUNT", amount);
    }

    unitRosterRegisteredUnitCount[inst] += amount;
}

void UnitRosterSubtractUnitType(int inst, string uType, int amount) {
    UnitRosterAddUnitType(inst, uType, -1 * amount);
}

void UnitRosterAddUnit(int inst, unit u) {
    UnitRosterAddUnitType(inst, UnitGetType(u), 1);
}

void UnitRosterSubtractUnit(int inst, unit u) {
    UnitRosterSubtractUnitType(inst, UnitGetType(u), 1);
}

int UnitRosterGetTotalUnitCount(int inst) {
    return unitRosterRegisteredUnitCount[inst];
}

int UnitRosterGetRegisteredUnitTypesCount(int inst) {
    return unitRosterRegisteredUnitTypesCount[inst];
}

string UnitRosterGetUnitTypeIndex(int inst, int ind) {
    return unitRosterRegisteredUnitTypes[inst][ind];
}

void UnitRosterSortBySupply(int inst) {
    int i;
    int j;
    int n = unitRosterRegisteredUnitTypesCount[inst];
    string tmp0;
    string tmp1;

    // copy the array
    for (i = 0; i < n; i += 1) {
        unitRosterUnitTypesTmp[i] = unitRosterRegisteredUnitTypes[inst][i];
    }

    // sort the array
    for (i = 0; i < n - 1; i += 1) {
        for (j = 0; j < n - i - 1; j += 1) {
            if (UnitTypeGetProperty(unitRosterUnitTypesTmp[j], c_unitPropSuppliesUsed) > UnitTypeGetProperty(unitRosterUnitTypesTmp[j + 1], c_unitPropSuppliesUsed)) {
                tmp0 = unitRosterUnitTypesTmp[j];
                tmp1 = unitRosterUnitTypesTmp[j + 1];
                unitRosterUnitTypesTmp[j] = tmp1;
                unitRosterUnitTypesTmp[j + 1] = tmp0;
            }
        }
    }
   
    // copy the array back
    for (i = 0; i < n; i += 1) {
        unitRosterRegisteredUnitTypes[inst][i] = unitRosterUnitTypesTmp[i];
    }
}

void UnitRosterAddUnitGroup(int inst, unitgroup un) {
    UnitGroupLoopBegin(un);
    while(!UnitGroupLoopDone()) {
        UnitRosterAddUnit(inst, UnitGroupLoopCurrent());
        UnitGroupLoopStep();
    }
    UnitGroupLoopEnd();
}

int UnitRosterCurrentUnitTypeCount() {
    return unitRosterCurrentUnitTypeCount;
}

string UnitRosterCurrentUnitType() {
    return unitRosterCurrentUnitType;
}

void UnitRosterForEachUnitCount(int inst, trigger cb) {
    int i;
    for (i = 0; i < unitRosterRegisteredUnitTypesCount[inst]; i += 1) {
        unitRosterCurrentUnitType = unitRosterRegisteredUnitTypes[inst][i];
        unitRosterCurrentUnitTypeCount = UnitRosterGetUnitTypeCount(inst, unitRosterCurrentUnitType);
        TriggerExecute(cb, true, true);
    }
}

// set this as the custom script's initialization function
void initUnitRoster() {
    unitRosterIndexer = IndexerCreate();
}
Contents

Indexer, Unit Indexer, and Unit Roster (Map)

Top