/** ** 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; }
/** ** Set the value of the unit variable. ** ** @param l Lua state. ** ** @return The new value of the unit. */ static int CclSetUnitVariable(lua_State *l) { const int nargs = lua_gettop(l); Assert(nargs >= 3 && nargs <= 5); lua_pushvalue(l, 1); CUnit *unit = CclGetUnit(l); lua_pop(l, 1); const char *const name = LuaToString(l, 2); int value; if (!strcmp(name, "Player")) { unit->AssignToPlayer(Players[LuaToNumber(l, 3)]); } else if (!strcmp(name, "RegenerationRate")) { value = LuaToNumber(l, 3); unit->Variable[HP_INDEX].Increase = std::min(unit->Variable[HP_INDEX].Max, value); } else if (!strcmp(name, "IndividualUpgrade")) { LuaCheckArgs(l, 4); std::string upgrade_ident = LuaToString(l, 3); bool has_upgrade = LuaToBoolean(l, 4); if (CUpgrade::Get(upgrade_ident)) { if (has_upgrade && unit->IndividualUpgrades[CUpgrade::Get(upgrade_ident)->ID] == false) { IndividualUpgradeAcquire(*unit, CUpgrade::Get(upgrade_ident)); } else if (!has_upgrade && unit->IndividualUpgrades[CUpgrade::Get(upgrade_ident)->ID]) { IndividualUpgradeLost(*unit, CUpgrade::Get(upgrade_ident)); } } else { LuaError(l, "Individual upgrade \"%s\" doesn't exist." _C_ upgrade_ident.c_str()); } } else if (!strcmp(name, "Active")) { bool ai_active = LuaToBoolean(l, 3); if (ai_active != unit->Active) { if (ai_active) { unit->Player->UnitTypesAiActiveCount[unit->Type->Slot]++; } else { unit->Player->UnitTypesAiActiveCount[unit->Type->Slot]--; if (unit->Player->UnitTypesAiActiveCount[unit->Type->Slot] < 0) { // if unit AI active count is negative, something wrong happened fprintf(stderr, "Player %d has a negative %s AI active count of %d.\n", unit->Player->Index, unit->Type->Ident.c_str(), unit->Player->UnitTypesAiActiveCount[unit->Type->Slot]); } } } unit->Active = ai_active; } else { const int index = UnitTypeVar.VariableNameLookup[name];// User variables if (index == -1) { LuaError(l, "Bad variable name '%s'\n" _C_ name); } value = LuaToNumber(l, 3); bool stats = false; if (nargs == 5) { stats = LuaToBoolean(l, 5); } if (stats) { // stat variables const char *const type = LuaToString(l, 4); if (!strcmp(type, "Value")) { unit->Stats->Variables[index].Value = std::min(unit->Stats->Variables[index].Max, value); } else if (!strcmp(type, "Max")) { unit->Stats->Variables[index].Max = value; } else if (!strcmp(type, "Increase")) { unit->Stats->Variables[index].Increase = value; } else if (!strcmp(type, "Enable")) { unit->Stats->Variables[index].Enable = value; } else { LuaError(l, "Bad variable type '%s'\n" _C_ type); } } else if (nargs == 3) { unit->Variable[index].Value = std::min(unit->Variable[index].Max, value); } else { const char *const type = LuaToString(l, 4); if (!strcmp(type, "Value")) { unit->Variable[index].Value = std::min(unit->Variable[index].Max, value); } else if (!strcmp(type, "Max")) { unit->Variable[index].Max = value; } else if (!strcmp(type, "Increase")) { unit->Variable[index].Increase = value; } else if (!strcmp(type, "Enable")) { unit->Variable[index].Enable = value; } else { LuaError(l, "Bad variable type '%s'\n" _C_ type); } } } lua_pushnumber(l, value); return 1; }