/** ** Parse unit ** ** @param l Lua state. ** ** @todo Verify that vision table is always correct (transporter) ** @todo (PlaceUnit() and host-info). */ static int CclUnit(lua_State *l) { const int slot = LuaToNumber(l, 1); if (!lua_istable(l, 2)) { LuaError(l, "incorrect argument"); } CUnit *unit = NULL; CUnitType *type = NULL; CUnitType *seentype = NULL; CPlayer *player = NULL; // Parse the list: const int args = lua_rawlen(l, 2); for (int j = 0; j < args; ++j) { const char *value = LuaToString(l, 2, j + 1); ++j; if (!strcmp(value, "type")) { type = UnitTypeByIdent(LuaToString(l, 2, j + 1)); } else if (!strcmp(value, "seen-type")) { seentype = UnitTypeByIdent(LuaToString(l, 2, j + 1)); } else if (!strcmp(value, "player")) { player = &Players[LuaToNumber(l, 2, j + 1)]; // During a unit's death animation (when action is "die" but the // unit still has its original type, i.e. it's still not a corpse) // the unit is already removed from map and from player's // unit list (=the unit went through LetUnitDie() which // calls RemoveUnit() and UnitLost()). Such a unit should not // be put on player's unit list! However, this state is not // easily detected from this place. It seems that it is // characterized by // unit->CurrentAction()==UnitActionDie so we have to wait // until we parsed at least Unit::Orders[]. Assert(type); unit = &UnitManager.GetSlotUnit(slot); unit->Init(*type); unit->Seen.Type = seentype; unit->Active = 0; unit->Removed = 0; Assert(UnitNumber(*unit) == slot); } else if (!strcmp(value, "current-sight-range")) { unit->CurrentSightRange = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "refs")) { unit->Refs = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "host-info")) { lua_rawgeti(l, 2, j + 1); if (!lua_istable(l, -1) || lua_rawlen(l, -1) != 4) { LuaError(l, "incorrect argument"); } Vec2i pos; int w; int h; pos.x = LuaToNumber(l, -1, 1); pos.y = LuaToNumber(l, -1, 2); w = LuaToNumber(l, -1, 3); h = LuaToNumber(l, -1, 4); MapSight(*player, pos, w, h, unit->CurrentSightRange, MapMarkTileSight); // Detectcloak works in container if (unit->Type->DetectCloak) { MapSight(*player, pos, w, h, unit->CurrentSightRange, MapMarkTileDetectCloak); } // Radar(Jammer) not. lua_pop(l, 1); } else if (!strcmp(value, "tile")) { lua_rawgeti(l, 2, j + 1); CclGetPos(l, &unit->tilePos.x , &unit->tilePos.y, -1); lua_pop(l, 1); unit->Offset = Map.getIndex(unit->tilePos); } else if (!strcmp(value, "seen-tile")) { lua_rawgeti(l, 2, j + 1); CclGetPos(l, &unit->Seen.tilePos.x , &unit->Seen.tilePos.y, -1); lua_pop(l, 1); } else if (!strcmp(value, "stats")) { unit->Stats = &type->Stats[LuaToNumber(l, 2, j + 1)]; } else if (!strcmp(value, "pixel")) { lua_rawgeti(l, 2, j + 1); CclGetPos(l, &unit->IX , &unit->IY, -1); lua_pop(l, 1); } else if (!strcmp(value, "seen-pixel")) { lua_rawgeti(l, 2, j + 1); CclGetPos(l, &unit->Seen.IX , &unit->Seen.IY, -1); lua_pop(l, 1); } else if (!strcmp(value, "frame")) { unit->Frame = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "seen")) { unit->Seen.Frame = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "not-seen")) { unit->Seen.Frame = UnitNotSeen; --j; } else if (!strcmp(value, "direction")) { unit->Direction = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "damage-type")) { unit->DamagedType = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "attacked")) { // FIXME : unsigned long should be better handled unit->Attacked = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "auto-repair")) { unit->AutoRepair = 1; --j; } else if (!strcmp(value, "burning")) { unit->Burning = 1; --j; } else if (!strcmp(value, "destroyed")) { unit->Destroyed = 1; --j; } else if (!strcmp(value, "removed")) { unit->Removed = 1; --j; } else if (!strcmp(value, "selected")) { unit->Selected = 1; --j; } else if (!strcmp(value, "summoned")) { unit->Summoned = 1; --j; } else if (!strcmp(value, "waiting")) { unit->Waiting = 1; --j; } else if (!strcmp(value, "mine-low")) { unit->MineLow = 1; --j; } else if (!strcmp(value, "rescued-from")) { unit->RescuedFrom = &Players[LuaToNumber(l, 2, j + 1)]; } else if (!strcmp(value, "seen-by-player")) { const char *s = LuaToString(l, 2, j + 1); unit->Seen.ByPlayer = 0; for (int i = 0; i < PlayerMax && *s; ++i, ++s) { if (*s == '-' || *s == '_' || *s == ' ') { unit->Seen.ByPlayer &= ~(1 << i); } else { unit->Seen.ByPlayer |= (1 << i); } } } else if (!strcmp(value, "seen-destroyed")) { const char *s = LuaToString(l, 2, j + 1); unit->Seen.Destroyed = 0; for (int i = 0; i < PlayerMax && *s; ++i, ++s) { if (*s == '-' || *s == '_' || *s == ' ') { unit->Seen.Destroyed &= ~(1 << i); } else { unit->Seen.Destroyed |= (1 << i); } } } else if (!strcmp(value, "constructed")) { unit->Constructed = 1; --j; } else if (!strcmp(value, "seen-constructed")) { unit->Seen.Constructed = 1; --j; } else if (!strcmp(value, "seen-state")) { unit->Seen.State = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "active")) { unit->Active = 1; --j; } else if (!strcmp(value, "ttl")) { // FIXME : unsigned long should be better handled unit->TTL = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "threshold")) { // FIXME : unsigned long should be better handled unit->Threshold = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "group-id")) { unit->GroupId = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "last-group")) { unit->LastGroup = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "resources-held")) { unit->ResourcesHeld = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "current-resource")) { lua_rawgeti(l, 2, j + 1); lua_pushvalue(l, -1); unit->CurrentResource = CclGetResourceByName(l); lua_pop(l, 1); } else if (!strcmp(value, "pathfinder-input")) { lua_rawgeti(l, 2, j + 1); lua_pushvalue(l, -1); unit->pathFinderData->input.Load(l); lua_pop(l, 1); } else if (!strcmp(value, "pathfinder-output")) { lua_rawgeti(l, 2, j + 1); lua_pushvalue(l, -1); unit->pathFinderData->output.Load(l); lua_pop(l, 1); } else if (!strcmp(value, "wait")) { unit->Wait = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "anim-data")) { lua_rawgeti(l, 2, j + 1); CAnimations::LoadUnitAnim(l, *unit, -1); lua_pop(l, 1); } else if (!strcmp(value, "wait-anim-data")) { lua_rawgeti(l, 2, j + 1); CAnimations::LoadWaitUnitAnim(l, *unit, -1); lua_pop(l, 1); } else if (!strcmp(value, "blink")) { unit->Blink = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "moving")) { unit->Moving = 1; --j; } else if (!strcmp(value, "re-cast")) { unit->ReCast = 1; --j; } else if (!strcmp(value, "boarded")) { unit->Boarded = 1; --j; } else if (!strcmp(value, "next-worker")) { lua_rawgeti(l, 2, j + 1); lua_pushvalue(l, -1); unit->NextWorker = CclGetUnitFromRef(l); lua_pop(l, 1); } else if (!strcmp(value, "resource-workers")) { lua_rawgeti(l, 2, j + 1); lua_pushvalue(l, -1); unit->Resource.Workers = CclGetUnitFromRef(l); lua_pop(l, 1); } else if (!strcmp(value, "resource-assigned")) { lua_rawgeti(l, 2, j + 1); lua_pushvalue(l, -1); unit->Resource.Assigned = LuaToNumber(l, -1); lua_pop(l, 1); } else if (!strcmp(value, "resource-active")) { lua_rawgeti(l, 2, j + 1); lua_pushvalue(l, -1); unit->Resource.Active = LuaToNumber(l, -1); lua_pop(l, 1); } else if (!strcmp(value, "units-boarded-count")) { unit->BoardCount = LuaToNumber(l, 2, j + 1); } else if (!strcmp(value, "units-contained")) { int subargs; int k; lua_rawgeti(l, 2, j + 1); if (!lua_istable(l, -1)) { LuaError(l, "incorrect argument"); } subargs = lua_rawlen(l, -1); for (k = 0; k < subargs; ++k) { lua_rawgeti(l, -1, k + 1); CUnit *u = CclGetUnitFromRef(l); lua_pop(l, 1); u->AddInContainer(*unit); } lua_pop(l, 1); } else if (!strcmp(value, "orders")) { lua_rawgeti(l, 2, j + 1); lua_pushvalue(l, -1); CclParseOrders(l, *unit); lua_pop(l, 1); // now we know unit's action so we can assign it to a player Assert(player != NULL); unit->AssignToPlayer(*player); if (unit->CurrentAction() == UnitActionBuilt) { DebugPrint("HACK: the building is not ready yet\n"); // HACK: the building is not ready yet unit->Player->UnitTypesCount[type->Slot]--; if (unit->Active) { unit->Player->UnitTypesAiActiveCount[type->Slot]--; } } } else if (!strcmp(value, "critical-order")) { lua_rawgeti(l, 2, j + 1); lua_pushvalue(l, -1); CclParseOrder(l, *unit , &unit->CriticalOrder); lua_pop(l, 1); } else if (!strcmp(value, "saved-order")) { lua_rawgeti(l, 2, j + 1); lua_pushvalue(l, -1); CclParseOrder(l, *unit, &unit->SavedOrder); lua_pop(l, 1); } else if (!strcmp(value, "new-order")) { lua_rawgeti(l, 2, j + 1); lua_pushvalue(l, -1); CclParseOrder(l, *unit, &unit->NewOrder); lua_pop(l, 1); } else if (!strcmp(value, "goal")) { unit->Goal = &UnitManager.GetSlotUnit(LuaToNumber(l, 2, j + 1)); } else if (!strcmp(value, "auto-cast")) { const char *s = LuaToString(l, 2, j + 1); Assert(SpellTypeByIdent(s)); if (!unit->AutoCastSpell) { unit->AutoCastSpell = new char[SpellTypeTable.size()]; memset(unit->AutoCastSpell, 0, SpellTypeTable.size()); } unit->AutoCastSpell[SpellTypeByIdent(s)->Slot] = 1; } else if (!strcmp(value, "spell-cooldown")) { lua_rawgeti(l, 2, j + 1); if (!lua_istable(l, -1) || lua_rawlen(l, -1) != SpellTypeTable.size()) { LuaError(l, "incorrect argument"); } if (!unit->SpellCoolDownTimers) { unit->SpellCoolDownTimers = new int[SpellTypeTable.size()]; memset(unit->SpellCoolDownTimers, 0, SpellTypeTable.size() * sizeof(int)); } for (size_t k = 0; k < SpellTypeTable.size(); ++k) { unit->SpellCoolDownTimers[k] = LuaToNumber(l, -1, k + 1); } lua_pop(l, 1); } else { const int index = UnitTypeVar.VariableNameLookup[value];// User variables if (index != -1) { // Valid index lua_rawgeti(l, 2, j + 1); DefineVariableField(l, unit->Variable + index, -1); lua_pop(l, 1); continue; } LuaError(l, "Unit: Unsupported tag: %s" _C_ value); } } // Unit may not have been assigned to a player before now. If not, // do so now. It is only assigned earlier if we have orders. // for loading of units from a MAP, and not a savegame, we won't // have orders for those units. They should appear here as if // they were just created. if (!unit->Player) { Assert(player); unit->AssignToPlayer(*player); UpdateForNewUnit(*unit, 0); } // Revealers are units that can see while removed if (unit->Removed && unit->Type->Revealer) { MapMarkUnitSight(*unit); } // Fix Colors for rescued units. if (unit->RescuedFrom) { unit->Colors = &unit->RescuedFrom->UnitColors; } return 0; }
/** ** Define a new upgrade modifier. ** ** @param l List of modifiers. */ static int CclDefineModifier(lua_State *l) { const char *key; const char *value; CUpgradeModifier *um; int args; int j; args = lua_gettop(l); um = new CUpgradeModifier; memset(um->ChangeUpgrades, '?', sizeof(um->ChangeUpgrades)); memset(um->ApplyTo, '?', sizeof(um->ApplyTo)); um->Modifier.Variables = new CVariable[UnitTypeVar.NumberVariable]; um->UpgradeId = UpgradeIdByIdent(LuaToString(l, 1)); for (j = 1; j < args; ++j) { if (!lua_istable(l, j + 1)) { LuaError(l, "incorrect argument"); } lua_rawgeti(l, j + 1, 1); key = LuaToString(l, -1); lua_pop(l, 1); #if 1 // To be removed. must modify lua file. if (!strcmp(key, "attack-range")) { key = "AttackRange"; } else if (!strcmp(key, "sight-range")) { key = "SightRange"; } else if (!strcmp(key, "basic-damage")) { key = "BasicDamage"; } else if (!strcmp(key, "piercing-damage")) { key = "PiercingDamage"; } else if (!strcmp(key, "armor")) { key = "Armor"; } else if (!strcmp(key, "hit-points")) { key = "HitPoints"; } #endif if (!strcmp(key, "regeneration-rate")) { lua_rawgeti(l, j + 1, 2); um->Modifier.Variables[HP_INDEX].Increase = LuaToNumber(l, -1); lua_pop(l, 1); } else if (!strcmp(key, "cost")) { int i; if (!lua_istable(l, j + 1) || luaL_getn(l, j + 1) != 2) { LuaError(l, "incorrect argument"); } lua_rawgeti(l, j + 1, 1); value = LuaToString(l, -1); lua_pop(l, 1); for (i = 0; i < MaxCosts; ++i) { if (!strcmp(value, DefaultResourceNames[i])) { break; } } if (i == MaxCosts) { LuaError(l, "Resource not found: %s" _C_ value); } lua_rawgeti(l, j + 1, 2); um->Modifier.Costs[i] = LuaToNumber(l, -1); lua_pop(l, 1); } else if (!strcmp(key, "allow-unit")) { lua_rawgeti(l, j + 1, 2); value = LuaToString(l, -1); lua_pop(l, 1); if (!strncmp(value, "unit-", 5)) { lua_rawgeti(l, j + 1, 3); um->ChangeUnits[UnitTypeIdByIdent(value)] = LuaToNumber(l, -1); lua_pop(l, 1); } else { LuaError(l, "unit expected"); } } else if (!strcmp(key, "allow")) { lua_rawgeti(l, j + 1, 2); value = LuaToString(l, -1); lua_pop(l, 1); if (!strncmp(value, "upgrade-", 8)) { lua_rawgeti(l, j + 1, 3); um->ChangeUpgrades[UpgradeIdByIdent(value)] = LuaToNumber(l, -1); lua_pop(l, 1); } else { LuaError(l, "upgrade expected"); } } else if (!strcmp(key, "apply-to")) { lua_rawgeti(l, j + 1, 2); value = LuaToString(l, -1); lua_pop(l, 1); um->ApplyTo[UnitTypeIdByIdent(value)] = 'X'; } else if (!strcmp(key, "convert-to")) { lua_rawgeti(l, j + 1, 2); value = LuaToString(l, -1); lua_pop(l, 1); um->ConvertTo = UnitTypeByIdent(value); } else { int index; // variable index; index = GetVariableIndex(key); if (index != -1) { lua_rawgeti(l, j + 1, 2); if (lua_istable(l, -1)) { DefineVariableField(l, um->Modifier.Variables + index, -1); } else if (lua_isnumber(l, -1)) { um->Modifier.Variables[index].Enable = 1; um->Modifier.Variables[index].Value = LuaToNumber(l, -1); um->Modifier.Variables[index].Max = LuaToNumber(l, -1); } else { LuaError(l, "bad argument type for '%s'\n" _C_ key); } lua_pop(l, 1); } else { LuaError(l, "wrong tag: %s" _C_ key); } } } UpgradeModifiers[NumUpgradeModifiers++] = um; return 0; }
/** ** Define a new upgrade modifier. ** ** @param l List of modifiers. */ static int CclDefineModifier(lua_State *l) { const int args = lua_gettop(l); CUpgradeModifier *um = new CUpgradeModifier; memset(um->ChangeUpgrades, '?', sizeof(um->ChangeUpgrades)); memset(um->ApplyTo, '?', sizeof(um->ApplyTo)); um->Modifier.Variables = new CVariable[UnitTypeVar.GetNumberVariable()]; um->ModifyPercent = new int[UnitTypeVar.GetNumberVariable()]; memset(um->ModifyPercent, 0, UnitTypeVar.GetNumberVariable() * sizeof(int)); std::string upgrade_ident = LuaToString(l, 1); um->UpgradeId = UpgradeIdByIdent(upgrade_ident); if (um->UpgradeId == -1) { LuaError(l, "Error when defining upgrade modifier: upgrade \"%s\" doesn't exist." _C_ upgrade_ident.c_str()); } for (int j = 1; j < args; ++j) { if (!lua_istable(l, j + 1)) { LuaError(l, "incorrect argument"); } const char *key = LuaToString(l, j + 1, 1); #if 0 // To be removed. must modify lua file. if (!strcmp(key, "attack-range")) { key = "AttackRange"; } else if (!strcmp(key, "sight-range")) { key = "SightRange"; } else if (!strcmp(key, "basic-damage")) { key = "BasicDamage"; } else if (!strcmp(key, "piercing-damage")) { key = "PiercingDamage"; } else if (!strcmp(key, "armor")) { key = "Armor"; } else if (!strcmp(key, "hit-points")) { key = "HitPoints"; } #endif if (!strcmp(key, "regeneration-rate")) { um->Modifier.Variables[HP_INDEX].Increase = LuaToNumber(l, j + 1, 2); } else if (!strcmp(key, "cost")) { if (!lua_istable(l, j + 1) || lua_rawlen(l, j + 1) != 2) { LuaError(l, "incorrect argument"); } const char *value = LuaToString(l, j + 1, 1); const int resId = GetResourceIdByName(l, value); um->Modifier.Costs[resId] = LuaToNumber(l, j + 1, 2); } else if (!strcmp(key, "storing")) { if (!lua_istable(l, j + 1) || lua_rawlen(l, j + 1) != 2) { LuaError(l, "incorrect argument"); } const char *value = LuaToString(l, j + 1, 1); const int resId = GetResourceIdByName(l, value); um->Modifier.Storing[resId] = LuaToNumber(l, j + 1, 2); } else if (!strcmp(key, "allow-unit")) { const char *value = LuaToString(l, j + 1, 2); if (!strncmp(value, "unit-", 5)) { um->ChangeUnits[UnitTypeIdByIdent(value)] = LuaToNumber(l, j + 1, 3); } else { LuaError(l, "unit expected"); } } else if (!strcmp(key, "allow")) { const char *value = LuaToString(l, j + 1, 2); if (!strncmp(value, "upgrade-", 8)) { um->ChangeUpgrades[UpgradeIdByIdent(value)] = LuaToNumber(l, j + 1, 3); } else { LuaError(l, "upgrade expected"); } } else if (!strcmp(key, "apply-to")) { const char *value = LuaToString(l, j + 1, 2); um->ApplyTo[UnitTypeIdByIdent(value)] = 'X'; } else if (!strcmp(key, "convert-to")) { const char *value = LuaToString(l, j + 1, 2); um->ConvertTo = UnitTypeByIdent(value); } else if (!strcmp(key, "research-speed")) { um->SpeedResearch = LuaToNumber(l, j + 1, 2); } else { int index = UnitTypeVar.VariableNameLookup[key]; // variable index; if (index != -1) { if (lua_rawlen(l, j + 1) == 3) { const char *value = LuaToString(l, j + 1, 3); if (!strcmp(value, "Percent")) { um->ModifyPercent[index] = LuaToNumber(l, j + 1, 2); } } else { lua_rawgeti(l, j + 1, 2); if (lua_istable(l, -1)) { DefineVariableField(l, um->Modifier.Variables + index, -1); } else if (lua_isnumber(l, -1)) { um->Modifier.Variables[index].Enable = 1; um->Modifier.Variables[index].Value = LuaToNumber(l, -1); um->Modifier.Variables[index].Max = LuaToNumber(l, -1); } else { LuaError(l, "bad argument type for '%s'\n" _C_ key); } lua_pop(l, 1); } } else { LuaError(l, "wrong tag: %s" _C_ key); } } } UpgradeModifiers[NumUpgradeModifiers++] = um; return 0; }