Parse Spell.
@param l Lua state.
static int CclDefineSpell(lua_State *l)
{
const int args = lua_gettop(l);
const std::string identname = LuaToString(l, 1);
SpellType *spell = SpellTypeByIdent(identname);
if (spell != NULL) {
DebugPrint("Redefining spell-type '%s'\n" _C_ identname.c_str());
} else {
spell = new SpellType(SpellTypeTable.size(), identname);
for (std::vector<CUnitType *>::size_type i = 0; i < UnitTypes.size(); ++i) { // adjust array for caster already defined
if (UnitTypes[i]->CanCastSpell) {
char *newc = new char[(SpellTypeTable.size() + 1) * sizeof(char)];
memcpy(newc, UnitTypes[i]->CanCastSpell, SpellTypeTable.size() * sizeof(char));
delete[] UnitTypes[i]->CanCastSpell;
UnitTypes[i]->CanCastSpell = newc;
UnitTypes[i]->CanCastSpell[SpellTypeTable.size()] = 0;
}
if (UnitTypes[i]->AutoCastActive) {
char *newc = new char[(SpellTypeTable.size() + 1) * sizeof(char)];
memcpy(newc, UnitTypes[i]->AutoCastActive, SpellTypeTable.size() * sizeof(char));
delete[] UnitTypes[i]->AutoCastActive;
UnitTypes[i]->AutoCastActive = newc;
UnitTypes[i]->AutoCastActive[SpellTypeTable.size()] = 0;
}
}
SpellTypeTable.push_back(spell);
}
for (int i = 1; i < args; ++i) {
const char *value = LuaToString(l, i + 1);
++i;
if (!strcmp(value, "showname")) {
spell->Name = LuaToString(l, i + 1);
} else if (!strcmp(value, "manacost")) {
spell->ManaCost = LuaToNumber(l, i + 1);
} else if (!strcmp(value, "cooldown")) {
spell->CoolDown = LuaToNumber(l, i + 1);
} else if (!strcmp(value, "res-cost")) {
lua_pushvalue(l, i + 1);
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
const int len = lua_rawlen(l, -1);
if (len != MaxCosts) {
LuaError(l, "resource table size isn't correct");
}
for (int j = 1; j < len; ++j) { // exclude the time
spell->Costs[j] = LuaToNumber(l, -1, j + 1);
}
lua_pop(l, 1);
} else if (!strcmp(value, "range")) {
if (!lua_isstring(l, i + 1) && !lua_isnumber(l, i + 1)) {
LuaError(l, "incorrect argument");
}
if (lua_isstring(l, i + 1) && !strcmp(lua_tostring(l, i + 1), "infinite")) {
spell->Range = INFINITE_RANGE;
} else if (lua_isnumber(l, i + 1)) {
spell->Range = static_cast<int>(lua_tonumber(l, i + 1));
} else {
LuaError(l, "Invalid range");
}
} else if (!strcmp(value, "repeat-cast")) {
spell->RepeatCast = 1;
--i;
} else if (!strcmp(value, "force-use-animation")) {
spell->ForceUseAnimation = true;
--i;
} else if (!strcmp(value, "target")) {
value = LuaToString(l, i + 1);
if (!strcmp(value, "self")) {
spell->Target = TargetSelf;
} else if (!strcmp(value, "unit")) {
spell->Target = TargetUnit;
} else if (!strcmp(value, "position")) {
spell->Target = TargetPosition;
} else {
LuaError(l, "Unsupported spell target type tag: %s" _C_ value);
}
} else if (!strcmp(value, "action")) {
if (!lua_istable(l, i + 1)) {
LuaError(l, "incorrect argument");
}
const int subargs = lua_rawlen(l, i + 1);
for (int k = 0; k < subargs; ++k) {
lua_rawgeti(l, i + 1, k + 1);
spell->Action.push_back(CclSpellAction(l));
lua_pop(l, 1);
}
} else if (!strcmp(value, "condition")) {
if (!spell->Condition) {
spell->Condition = new ConditionInfo;
}
lua_pushvalue(l, i + 1);
CclSpellCondition(l, spell->Condition);
lua_pop(l, 1);
} else if (!strcmp(value, "autocast")) {
if (!spell->AutoCast) {
spell->AutoCast = new AutoCastInfo();
}
lua_pushvalue(l, i + 1);
CclSpellAutocast(l, spell->AutoCast);
lua_pop(l, 1);
} else if (!strcmp(value, "ai-cast")) {
if (!spell->AICast) {
spell->AICast = new AutoCastInfo();
}
lua_pushvalue(l, i + 1);
CclSpellAutocast(l, spell->AICast);
lua_pop(l, 1);
} else if (!strcmp(value, "sound-when-cast")) {
// Free the old name, get the new one
spell->SoundWhenCast.Name = LuaToString(l, i + 1);
spell->SoundWhenCast.MapSound();
// Check for sound.
if (!spell->SoundWhenCast.Sound) {
spell->SoundWhenCast.Name.clear();
}
} else if (!strcmp(value, "depend-upgrade")) {
value = LuaToString(l, i + 1);
spell->DependencyId = UpgradeIdByIdent(value);
if (spell->DependencyId == -1) {
lua_pushfstring(l, "Bad upgrade name: %s", value);
}
} else {
LuaError(l, "Unsupported tag: %s" _C_ value);
}
}
return 0;
}
Description
Add a new message.
Example:
AddMessage("Hello World!")
static int CclAddMessage(lua_State *l)
{
LuaCheckArgs(l, 1);
SetMessage("%s", LuaToString(l, 1));
return 0;
}
Description
Set speed of key scroll
@param l Lua state.
Example:
SetKeyScrollSpeed(4)
static int CclSetKeyScrollSpeed(lua_State *l)
{
LuaCheckArgs(l, 1);
UI.KeyScrollSpeed = LuaToNumber(l, 1);
return 0;
}
Description
Get speed of key scroll
@param l Lua state.
Example:
scroll_speed = GetKeyScrollSpeed()
print(scroll_speed)
static int CclGetKeyScrollSpeed(lua_State *l)
{
LuaCheckArgs(l, 0);
lua_pushnumber(l, UI.KeyScrollSpeed);
return 1;
}
Description
Set speed of mouse scroll
@param l Lua state.
Example:
SetMouseScrollSpeed(2)
static int CclSetMouseScrollSpeed(lua_State *l)
{
LuaCheckArgs(l, 1);
UI.MouseScrollSpeed = LuaToNumber(l, 1);
return 0;
}
Description
Get speed of mouse scroll
@param l Lua state.
Example:
scroll_speed = GetMouseScrollSpeed()
print(scroll_speed)
static int CclGetMouseScrollSpeed(lua_State *l)
{
LuaCheckArgs(l, 0);
lua_pushnumber(l, UI.MouseScrollSpeed);
return 1;
}
Set speed of middle-mouse scroll
@param l Lua state.
static int CclSetMouseScrollSpeedDefault(lua_State *l)
{
LuaCheckArgs(l, 1);
UI.MouseScrollSpeedDefault = LuaToNumber(l, 1);
return 0;
}
Get speed of middle-mouse scroll
@param l Lua state.
static int CclGetMouseScrollSpeedDefault(lua_State *l)
{
LuaCheckArgs(l, 0);
lua_pushnumber(l, UI.MouseScrollSpeedDefault);
return 0;
}
Set speed of ctrl-middle-mouse scroll
@param l Lua state.
static int CclSetMouseScrollSpeedControl(lua_State *l)
{
LuaCheckArgs(l, 1);
UI.MouseScrollSpeedControl = LuaToNumber(l, 1);
return 0;
}
Get speed of ctrl-middle-mouse scroll
@param l Lua state.
static int CclGetMouseScrollSpeedControl(lua_State *l)
{
LuaCheckArgs(l, 0);
lua_pushnumber(l, UI.MouseScrollSpeedControl);
return 0;
}
Set which missile is used for right click
@param l Lua state.
static int CclSetClickMissile(lua_State *l)
{
const int args = lua_gettop(l);
if (args > 1 || (args == 1 && (!lua_isnil(l, 1) && !lua_isstring(l, 1)))) {
LuaError(l, "incorrect argument");
}
ClickMissile.clear();
if (args == 1 && !lua_isnil(l, 1)) {
ClickMissile = lua_tostring(l, 1);
}
return 0;
}
Set which missile shows Damage
@param l Lua state.
static int CclSetDamageMissile(lua_State *l)
{
const int args = lua_gettop(l);
if (args > 1 || (args == 1 && (!lua_isnil(l, 1) && !lua_isstring(l, 1)))) {
LuaError(l, "incorrect argument");
}
DamageMissile.clear();
if (args == 1 && !lua_isnil(l, 1)) {
DamageMissile = lua_tostring(l, 1);
}
return 0;
}
Description
Set the video resolution.
@param l Lua state.
Example:
SetVideoResolution(640,480)
static int CclSetVideoResolution(lua_State *l)
{
LuaCheckArgs(l, 2);
if (CclInConfigFile) {
// May have been set from the command line
if (!Video.Width || !Video.Height) {
Video.Width = LuaToNumber(l, 1);
Video.Height = LuaToNumber(l, 2);
}
}
return 0;
}
Description
Get the video resolution.
@param l Lua state.
Example:
width,height = GetVideoResolution()
print("Resolution is " .. width .. "x" .. height)
static int CclGetVideoResolution(lua_State *l)
{
LuaCheckArgs(l, 0);
lua_pushnumber(l, Video.Width);
lua_pushnumber(l, Video.Height);
return 2;
}
Description
Set the video fullscreen mode.
@param l Lua state.
Example:
-- Full Screen mode enabled
SetVideoFullScreen(true)
-- Full Screen mode disabled
SetVideoFullScreen(false)
static int CclSetVideoFullScreen(lua_State *l)
{
LuaCheckArgs(l, 1);
if (CclInConfigFile) {
// May have been set from the command line
if (!VideoForceFullScreen) {
Video.FullScreen = LuaToBoolean(l, 1);
}
}
return 0;
}
Description
Get the video fullscreen mode.
@param l Lua state.
Example:
fullscreenmode = GetVideoFullScreen()
print(fullscreenmode)
static int CclGetVideoFullScreen(lua_State *l)
{
LuaCheckArgs(l, 0);
lua_pushboolean(l, Video.FullScreen);
return 1;
}
Request a specific initial window size
static int CclSetWindowSize(lua_State *l)
{
LuaCheckArgs(l, 2);
if (CclInConfigFile) {
// May have been set from the command line
if (!Video.WindowWidth || !Video.WindowHeight) {
Video.WindowWidth = LuaToNumber(l, 1);
Video.WindowHeight = LuaToNumber(l, 2);
}
}
return 0;
}
For games with non-square pixels, this sets the scale of vertical pixels versus horizontal pixels.
e.g., if your assets are 320x200, but you render at 320x240, this is 1.2.
static int CclSetVerticalPixelSize(lua_State *l)
{
LuaCheckArgs(l, 1);
if (CclInConfigFile) {
luaL_checktype(l, 1, LUA_TNUMBER);
Video.VerticalPixelSize = static_cast<double>(lua_tonumber(l, 1));
}
return 0;
}
Description
Declare which codepage the font files are in. Text is handled internally
as UTF-8 everywhere, but the font rendering system uses graphics with 256
symbols. Commonly, DOS and early Windows games used codepage 437 or 1252 for
western European languages, or 866 for Russian and some other cyrillic
writing systems. These are the only ones that are currently supported, but
more can easily be added. All text is mapped into the codepage that is set
for the font files. If the codepage is not one of the supported ones, or if
something doesn't map (for example, some accented characters with codepage
866, or cyrillic letters with codepage 437), a simple "visual" mapping to
7-bit ASCII is used to at least print something that may be recognizable.
static int CclSetFontCodePage(lua_State *l)
{
LuaCheckArgs(l, 1);
FontCodePage = LuaToNumber(l, 1);
return 0;
}
Default title screens.
@param l Lua state.
static int CclSetTitleScreens(lua_State *l)
{
if (TitleScreens) {
for (int i = 0; TitleScreens[i]; ++i) {
delete TitleScreens[i];
}
delete[] TitleScreens;
TitleScreens = NULL;
}
const int args = lua_gettop(l);
TitleScreens = new TitleScreen *[args + 1];
memset(TitleScreens, 0, (args + 1) * sizeof(TitleScreen *));
for (int j = 0; j < args; ++j) {
if (!lua_istable(l, j + 1)) {
LuaError(l, "incorrect argument");
}
TitleScreens[j] = new TitleScreen;
TitleScreens[j]->Iterations = 1;
lua_pushnil(l);
while (lua_next(l, j + 1)) {
const char *value = LuaToString(l, -2);
if (!strcmp(value, "Image")) {
TitleScreens[j]->File = LuaToString(l, -1);
} else if (!strcmp(value, "Music")) {
TitleScreens[j]->Music = LuaToString(l, -1);
} else if (!strcmp(value, "Timeout")) {
TitleScreens[j]->Timeout = LuaToNumber(l, -1);
} else if (!strcmp(value, "Iterations")) {
TitleScreens[j]->Iterations = LuaToNumber(l, -1);
} else if (!strcmp(value, "Editor")) {
TitleScreens[j]->Editor = LuaToNumber(l, -1);
} else if (!strcmp(value, "Labels")) {
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
const int subargs = lua_rawlen(l, -1);
TitleScreens[j]->Labels = new TitleScreenLabel *[subargs + 1];
memset(TitleScreens[j]->Labels, 0, (subargs + 1) * sizeof(TitleScreenLabel *));
for (int k = 0; k < subargs; ++k) {
lua_rawgeti(l, -1, k + 1);
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
TitleScreens[j]->Labels[k] = new TitleScreenLabel;
lua_pushnil(l);
while (lua_next(l, -2)) {
const char *value = LuaToString(l, -2);
if (!strcmp(value, "Text")) {
TitleScreens[j]->Labels[k]->Text = LuaToString(l, -1);
} else if (!strcmp(value, "Font")) {
TitleScreens[j]->Labels[k]->Font = CFont::Get(LuaToString(l, -1));
} else if (!strcmp(value, "Pos")) {
CclGetPos(l, &TitleScreens[j]->Labels[k]->Xofs, &TitleScreens[j]->Labels[k]->Yofs);
} else if (!strcmp(value, "Flags")) {
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
const int subsubargs = lua_rawlen(l, -1);
for (int subk = 0; subk < subsubargs; ++subk) {
const char *value = LuaToString(l, -1, subk + 1);
if (!strcmp(value, "center")) {
TitleScreens[j]->Labels[k]->Flags |= TitleFlagCenter;
} else {
LuaError(l, "incorrect flag");
}
}
} else {
LuaError(l, "Unsupported key: %s" _C_ value);
}
lua_pop(l, 1);
}
lua_pop(l, 1);
}
} else {
LuaError(l, "Unsupported key: %s" _C_ value);
}
lua_pop(l, 1);
}
}
return 0;
}
/ui/script_ui.cpp:355 CclShowTitleScreens
static int CclShowTitleScreens(lua_State *l)
{
LuaCheckArgs(l, 0);
ShowTitleScreens();
lua_pushboolean(l, 1);
return 1;
}
Define the Panels.
Define what is shown in the panel(text, icon, variables)
@param l Lua state.
@return 0.
static int CclDefinePanelContents(lua_State *l)
{
const int nargs = lua_gettop(l);
for (int i = 0; i < nargs; i++) {
Assert(lua_istable(l, i + 1));
CUnitInfoPanel *infopanel = new CUnitInfoPanel;
for (lua_pushnil(l); lua_next(l, i + 1); lua_pop(l, 1)) {
const char *key = LuaToString(l, -2);
if (!strcmp(key, "Ident")) {
infopanel->Name = LuaToString(l, -1);
} else if (!strcmp(key, "Pos")) {
CclGetPos(l, &infopanel->PosX, &infopanel->PosY);
} else if (!strcmp(key, "DefaultFont")) {
infopanel->DefaultFont = CFont::Get(LuaToString(l, -1));
} else if (!strcmp(key, "Condition")) {
infopanel->Condition = ParseConditionPanel(l);
} else if (!strcmp(key, "Contents")) {
Assert(lua_istable(l, -1));
for (size_t j = 0; j < lua_rawlen(l, -1); j++, lua_pop(l, 1)) {
lua_rawgeti(l, -1, j + 1);
infopanel->Contents.push_back(CclParseContent(l));
}
} else {
LuaError(l, "'%s' invalid for DefinePanelContents" _C_ key);
}
}
for (std::vector<CContentType *>::iterator content = infopanel->Contents.begin();
content != infopanel->Contents.end(); ++content) { // Default value for invalid value.
(*content)->Pos.x += infopanel->PosX;
(*content)->Pos.y += infopanel->PosY;
}
size_t j;
for (j = 0; j < UI.InfoPanelContents.size(); ++j) {
if (infopanel->Name == UI.InfoPanelContents[j]->Name) {
DebugPrint("Redefinition of Panel '%s'\n" _C_ infopanel->Name.c_str());
delete UI.InfoPanelContents[j];
UI.InfoPanelContents[j] = infopanel;
break;
}
}
if (j == UI.InfoPanelContents.size()) {
UI.InfoPanelContents.push_back(infopanel);
}
}
return 0;
}
Define the Panels.
Define what is shown in the panel(text, icon, variables)
@param l Lua state.
@return 0.
static int CclDefinePopup(lua_State *l)
{
Assert(lua_istable(l, 1));
CPopup *popup = new CPopup;
for (lua_pushnil(l); lua_next(l, 1); lua_pop(l, 1)) {
const char *key = LuaToString(l, -2);
if (!strcmp(key, "Ident")) {
popup->Ident = LuaToString(l, -1);
} else if (!strcmp(key, "DefaultFont")) {
popup->DefaultFont = CFont::Get(LuaToString(l, -1));
} else if (!strcmp(key, "BackgroundColor")) {
popup->BackgroundColor = LuaToUnsignedNumber(l, -1);
} else if (!strcmp(key, "BorderColor")) {
popup->BorderColor = LuaToUnsignedNumber(l, -1);
} else if (!strcmp(key, "Margin")) {
CclGetPos(l, &popup->MarginX, &popup->MarginY);
} else if (!strcmp(key, "MinWidth")) {
popup->MinWidth = LuaToNumber(l, -1);
} else if (!strcmp(key, "MinHeight")) {
popup->MinHeight = LuaToNumber(l, -1);
} else if (!strcmp(key, "Contents")) {
Assert(lua_istable(l, -1));
for (size_t j = 0; j < lua_rawlen(l, -1); j++, lua_pop(l, 1)) {
lua_rawgeti(l, -1, j + 1);
popup->Contents.push_back(CPopupContentType::ParsePopupContent(l));
}
} else {
LuaError(l, "'%s' invalid for DefinePopups" _C_ key);
}
}
for (size_t j = 0; j < UI.ButtonPopups.size(); ++j) {
if (popup->Ident == UI.ButtonPopups[j]->Ident) {
DebugPrint("Redefinition of Popup '%s'\n" _C_ popup->Ident.c_str());
delete UI.ButtonPopups[j];
UI.ButtonPopups[j] = popup;
return 0;
}
}
UI.ButtonPopups.push_back(popup);
return 0;
}
Define the viewports.
@param l Lua state.
static int CclDefineViewports(lua_State *l)
{
int i = 0;
const int args = lua_gettop(l);
for (int j = 0; j < args; ++j) {
const char *value = LuaToString(l, j + 1);
++j;
if (!strcmp(value, "mode")) {
UI.ViewportMode = (ViewportModeType)LuaToNumber(l, j + 1);
} else if (!strcmp(value, "viewport")) {
if (!lua_istable(l, j + 1) && lua_rawlen(l, j + 1) != 3) {
LuaError(l, "incorrect argument");
}
UI.Viewports[i].MapPos.x = LuaToNumber(l, j + 1, 1);
UI.Viewports[i].MapPos.y = LuaToNumber(l, j + 1, 2);
const int slot = LuaToNumber(l, j + 1, 3);
if (slot != -1) {
UI.Viewports[i].Unit = &UnitManager->GetSlotUnit(slot);
}
++i;
} else {
LuaError(l, "Unsupported tag: %s" _C_ value);
}
}
UI.NumViewports = i;
return 0;
}
Fighter right button attacks as default.
@param l Lua state.
static int CclRightButtonAttacks(lua_State *l)
{
LuaCheckArgs(l, 0);
RightButtonAttacks = true;
return 0;
}
Fighter right button moves as default.
@param l Lua state.
static int CclRightButtonMoves(lua_State *l)
{
LuaCheckArgs(l, 0);
RightButtonAttacks = false;
return 0;
}
Description
Enable/disable the fancy buildings.
@param l Lua state.
Example:
-- Enable fancy buildings
SetFancyBuildings(true)
-- Disable fancy buildings
SetFancyBuildings(false)
static int CclSetFancyBuildings(lua_State *l)
{
LuaCheckArgs(l, 1);
FancyBuildings = LuaToBoolean(l, 1);
return 0;
}
Define a button.
@param l Lua state.
static int CclDefineButton(lua_State *l)
{
LuaCheckArgs(l, 1);
if (!lua_istable(l, 1)) {
LuaError(l, "incorrect argument");
}
ButtonAction ba;
//
// Parse the arguments
//
lua_pushnil(l);
while (lua_next(l, 1)) {
const char *value = LuaToString(l, -2);
if (!strcmp(value, "Pos")) {
ba.Pos = LuaToNumber(l, -1);
} else if (!strcmp(value, "Level")) {
ba.Level = LuaToNumber(l, -1);
} else if (!strcmp(value, "AlwaysShow")) {
ba.AlwaysShow = LuaToBoolean(l, -1);
} else if (!strcmp(value, "Icon")) {
ba.Icon.Name = LuaToString(l, -1);
} else if (!strcmp(value, "Action")) {
value = LuaToString(l, -1);
if (!strcmp(value, "move")) {
ba.Action = ButtonMove;
} else if (!strcmp(value, "stop")) {
ba.Action = ButtonStop;
} else if (!strcmp(value, "attack")) {
ba.Action = ButtonAttack;
} else if (!strcmp(value, "repair")) {
ba.Action = ButtonRepair;
} else if (!strcmp(value, "harvest")) {
ba.Action = ButtonHarvest;
} else if (!strcmp(value, "button")) {
ba.Action = ButtonButton;
} else if (!strcmp(value, "build")) {
ba.Action = ButtonBuild;
} else if (!strcmp(value, "train-unit")) {
ba.Action = ButtonTrain;
} else if (!strcmp(value, "patrol")) {
ba.Action = ButtonPatrol;
} else if (!strcmp(value, "explore")) {
ba.Action = ButtonExplore;
} else if (!strcmp(value, "stand-ground")) {
ba.Action = ButtonStandGround;
} else if (!strcmp(value, "attack-ground")) {
ba.Action = ButtonAttackGround;
} else if (!strcmp(value, "return-goods")) {
ba.Action = ButtonReturn;
} else if (!strcmp(value, "cast-spell")) {
ba.Action = ButtonSpellCast;
} else if (!strcmp(value, "research")) {
ba.Action = ButtonResearch;
} else if (!strcmp(value, "upgrade-to")) {
ba.Action = ButtonUpgradeTo;
} else if (!strcmp(value, "unload")) {
ba.Action = ButtonUnload;
} else if (!strcmp(value, "cancel")) {
ba.Action = ButtonCancel;
} else if (!strcmp(value, "cancel-upgrade")) {
ba.Action = ButtonCancelUpgrade;
} else if (!strcmp(value, "cancel-train-unit")) {
ba.Action = ButtonCancelTrain;
} else if (!strcmp(value, "cancel-build")) {
ba.Action = ButtonCancelBuild;
} else if (!strcmp(value, "callback")) {
ba.Action = ButtonCallbackAction;
} else {
LuaError(l, "Unsupported button action: %s" _C_ value);
}
} else if (!strcmp(value, "Value")) {
if (!lua_isnumber(l, -1) && !lua_isstring(l, -1) && !lua_isfunction(l, -1)) {
LuaError(l, "incorrect argument");
}
if (lua_isfunction(l, -1)) {
ba.Payload = new LuaCallback(l, -1);
} else {
char buf[64];
const char *s2;
if (lua_isnumber(l, -1)) {
snprintf(buf, sizeof(buf), "%ld", (long int)lua_tonumber(l, -1));
s2 = buf;
} else {
s2 = lua_tostring(l, -1);
}
ba.ValueStr = s2;
}
} else if (!strcmp(value, "Allowed")) {
value = LuaToString(l, -1);
if (!strcmp(value, "check-true")) {
ba.Allowed = ButtonCheckTrue;
} else if (!strcmp(value, "check-false")) {
ba.Allowed = ButtonCheckFalse;
} else if (!strcmp(value, "check-upgrade")) {
ba.Allowed = ButtonCheckUpgrade;
} else if (!strcmp(value, "check-individual-upgrade")) {
ba.Allowed = ButtonCheckIndividualUpgrade;
} else if (!strcmp(value, "check-unit-variable")) {
ba.Allowed = ButtonCheckUnitVariable;
} else if (!strcmp(value, "check-units-or")) {
ba.Allowed = ButtonCheckUnitsOr;
} else if (!strcmp(value, "check-units-and")) {
ba.Allowed = ButtonCheckUnitsAnd;
} else if (!strcmp(value, "check-units-not")) {
ba.Allowed = ButtonCheckUnitsNot;
} else if (!strcmp(value, "check-units-nor")) {
ba.Allowed = ButtonCheckUnitsNor;
} else if (!strcmp(value, "check-network")) {
ba.Allowed = ButtonCheckNetwork;
} else if (!strcmp(value, "check-no-network")) {
ba.Allowed = ButtonCheckNoNetwork;
} else if (!strcmp(value, "check-no-work")) {
ba.Allowed = ButtonCheckNoWork;
} else if (!strcmp(value, "check-no-research")) {
ba.Allowed = ButtonCheckNoResearch;
} else if (!strcmp(value, "check-attack")) {
ba.Allowed = ButtonCheckAttack;
} else if (!strcmp(value, "check-upgrade-to")) {
ba.Allowed = ButtonCheckUpgradeTo;
} else if (!strcmp(value, "check-research")) {
ba.Allowed = ButtonCheckResearch;
} else if (!strcmp(value, "check-single-research")) {
ba.Allowed = ButtonCheckSingleResearch;
} else if (!strcmp(value, "check-debug")) {
ba.Allowed = ButtonCheckDebug;
} else {
LuaError(l, "Unsupported action: %s" _C_ value);
}
} else if (!strcmp(value, "AllowArg")) {
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
std::string allowstr;
const unsigned int subargs = lua_rawlen(l, -1);
for (unsigned int k = 0; k < subargs; ++k) {
const char *s2 = LuaToString(l, -1, k + 1);
allowstr += s2;
if (k != subargs - 1) {
allowstr += ",";
}
}
ba.AllowStr = allowstr;
} else if (!strcmp(value, "Key")) {
std::string key(LuaToString(l, -1));
ba.Key = GetHotKey(key);
} else if (!strcmp(value, "Hint")) {
ba.Hint = LuaToString(l, -1);
} else if (!strcmp(value, "Description")) {
ba.Description = LuaToString(l, -1);
} else if (!strcmp(value, "CommentSound")) {
ba.CommentSound.Name = LuaToString(l, -1);
} else if (!strcmp(value, "ButtonCursor")) {
ba.ButtonCursor = LuaToString(l, -1);
} else if (!strcmp(value, "Popup")) {
ba.Popup = LuaToString(l, -1);
} else if (!strcmp(value, "ForUnit")) {
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
// FIXME: ba.UnitMask shouldn't be a string
std::string umask = ",";
const unsigned subargs = lua_rawlen(l, -1);
for (unsigned int k = 0; k < subargs; ++k) {
const char *s2 = LuaToString(l, -1, k + 1);
umask += s2;
umask += ",";
}
ba.UnitMask = umask;
if (!strncmp(ba.UnitMask.c_str(), ",*,", 3)) {
ba.UnitMask = "*";
}
} else {
LuaError(l, "Unsupported tag: %s" _C_ value);
}
lua_pop(l, 1);
}
AddButton(ba.Pos, ba.Level, ba.Icon.Name, ba.Action, ba.ValueStr, ba.Payload,
ba.Allowed, ba.AllowStr, ba.Key, ba.Hint, ba.Description, ba.CommentSound.Name,
ba.ButtonCursor, ba.UnitMask, ba.Popup, ba.AlwaysShow);
return 0;
}
Clear all buttons
@param l Lua state.
static int CclClearButtons(lua_State *l)
{
LuaCheckArgs(l, 0);
CleanButtons();
return 0;
}
/ui/script_ui.cpp:1186 CclCopyButtonsForUnitType
static int CclCopyButtonsForUnitType(lua_State *l)
{
LuaCheckArgs(l, 2);
// Slot identifier
const char* fromName = LuaToString(l, 1);
CUnitType *from = UnitTypeByIdent(fromName);
const char* toName = LuaToString(l, 2);
CUnitType *to = UnitTypeByIdent(toName);
if (!to) {
LuaError(l, "Unknown unit-type '%s'\n" _C_ toName);
}
if (!from) {
LuaError(l, "Unknown unit-type '%s'\n" _C_ fromName);
}
for (auto btn : UnitButtonTable) {
if (btn->UnitMask.find(fromName) != std::string::npos) {
btn->UnitMask += toName;
btn->UnitMask += ",";
}
}
return 0;
}
Define a button style
@param l Lua state.
static int CclDefineButtonStyle(lua_State *l)
{
LuaCheckArgs(l, 2);
if (!lua_istable(l, 2)) {
LuaError(l, "incorrect argument");
}
const char *style = LuaToString(l, 1);
ButtonStyle *&b = ButtonStyleHash[style];
if (!b) {
b = new ButtonStyle;
// Set to bogus value to see if it was set later
b->Default.TextPos.x = b->Hover.TextPos.x = b->Clicked.TextPos.x = 0xFFFFFF;
}
lua_pushnil(l);
while (lua_next(l, 2)) {
const char *value = LuaToString(l, -2);
if (!strcmp(value, "Size")) {
CclGetPos(l, &b->Width, &b->Height);
} else if (!strcmp(value, "Font")) {
b->Font = CFont::Get(LuaToString(l, -1));
} else if (!strcmp(value, "TextNormalColor")) {
b->TextNormalColor = LuaToString(l, -1);
} else if (!strcmp(value, "TextReverseColor")) {
b->TextReverseColor = LuaToString(l, -1);
} else if (!strcmp(value, "TextPos")) {
CclGetPos(l, &b->TextX, &b->TextY);
} else if (!strcmp(value, "TextAlign")) {
value = LuaToString(l, -1);
if (!strcmp(value, "Center")) {
b->TextAlign = TextAlignCenter;
} else if (!strcmp(value, "Right")) {
b->TextAlign = TextAlignRight;
} else if (!strcmp(value, "Left")) {
b->TextAlign = TextAlignLeft;
} else {
LuaError(l, "Invalid text alignment: %s" _C_ value);
}
} else if (!strcmp(value, "Default")) {
ParseButtonStyleProperties(l, &b->Default);
} else if (!strcmp(value, "Hover")) {
ParseButtonStyleProperties(l, &b->Hover);
} else if (!strcmp(value, "Clicked")) {
ParseButtonStyleProperties(l, &b->Clicked);
} else {
LuaError(l, "Unsupported tag: %s" _C_ value);
}
lua_pop(l, 1);
}
if (b->Default.TextPos.x == 0xFFFFFF) {
b->Default.TextPos.x = b->TextX;
b->Default.TextPos.y = b->TextY;
}
if (b->Hover.TextPos.x == 0xFFFFFF) {
b->Hover.TextPos.x = b->TextX;
b->Hover.TextPos.y = b->TextY;
}
if (b->Clicked.TextPos.x == 0xFFFFFF) {
b->Clicked.TextPos.x = b->TextX;
b->Clicked.TextPos.y = b->TextY;
}
if (b->Default.TextAlign == TextAlignUndefined) {
b->Default.TextAlign = b->TextAlign;
}
if (b->Hover.TextAlign == TextAlignUndefined) {
b->Hover.TextAlign = b->TextAlign;
}
if (b->Clicked.TextAlign == TextAlignUndefined) {
b->Clicked.TextAlign = b->TextAlign;
}
return 0;
}
Description
Set basic map caracteristics.
@param l Lua state.
Example:
PresentMap("Map description", 1, 128, 128, 17)
static int CclPresentMap(lua_State *l)
{
LuaCheckArgs(l, 5);
Map.Info.Description = LuaToString(l, 1);
// Number of players in LuaToNumber(l, 3); // Not used yet.
Map.Info.MapWidth = LuaToNumber(l, 3);
Map.Info.MapHeight = LuaToNumber(l, 4);
Map.Info.MapUID = LuaToNumber(l, 5);
return 0;
}
Description
Define the lua file that will build the map
@param l Lua state.
Example:
-- Load map setup from file
DefineMapSetup("Setup.sms")
static int CclDefineMapSetup(lua_State *l)
{
LuaCheckArgs(l, 1);
Map.Info.Filename = LuaToString(l, 1);
return 0;
}
Set selection style.
@param l Lua state.
static int CclSetSelectionStyle(lua_State *l)
{
if (lua_gettop(l) < 1) {
LuaError(l, "incorrect argument");
}
const char *style = LuaToString(l, 1);
if (!strcmp(style, "rectangle")) {
LuaCheckArgs(l, 1);
DrawSelection = DrawSelectionRectangle;
} else if (!strcmp(style, "alpha-rectangle")) {
LuaCheckArgs(l, 1);
DrawSelection = DrawSelectionRectangleWithTrans;
} else if (!strcmp(style, "circle")) {
LuaCheckArgs(l, 1);
DrawSelection = DrawSelectionCircle;
} else if (!strcmp(style, "alpha-circle")) {
LuaCheckArgs(l, 1);
DrawSelection = DrawSelectionCircleWithTrans;
} else if (!strcmp(style, "corners")) {
LuaCheckArgs(l, 1);
DrawSelection = DrawSelectionCorners;
} else if (!strcmp(style, "ellipse")) {
LuaCheckArgs(l, 2);
float factor = LuaToFloat(l, 2);
DrawSelection = DrawSelectionEllipse(factor);
} else {
LuaError(l, "Unsupported selection style");
}
return 0;
}
Set the keys which are use for grouping units, helpful for other keyboards
@param l Lua state.
static int CclSetGroupKeys(lua_State *l)
{
LuaCheckArgs(l, 1);
UiGroupKeys = LuaToString(l, 1);
return 0;
}
Define a new dependency.
@param l Lua state.
static int CclDefineDependency(lua_State *l)
{
const int args = lua_gettop(l);
const char *target = LuaToString(l, 1);
// All or rules.
int or_flag = 0;
for (int j = 1; j < args; ++j) {
if (!lua_istable(l, j + 1)) {
LuaError(l, "incorrect argument");
}
const int subargs = lua_rawlen(l, j + 1);
for (int k = 0; k < subargs; ++k) {
const char *required = LuaToString(l, j + 1, k + 1);
int count = 1;
if (k + 1 < subargs) {
lua_rawgeti(l, j + 1, k + 2);
if (lua_isnumber(l, -1)) {
count = LuaToNumber(l, -1);
++k;
}
lua_pop(l, 1);
}
AddDependency(target, required, count, or_flag);
or_flag = 0;
}
if (j + 1 < args) {
++j;
const char *value = LuaToString(l, j + 1);
if (strcmp(value, "or")) {
LuaError(l, "not or symbol: %s" _C_ value);
return 0;
}
or_flag = 1;
}
}
return 0;
}
Get the dependency.
@todo not written.
@param l Lua state.
static int CclGetDependency(lua_State *l)
{
DebugPrint("FIXME: write this %p\n" _C_(void *)l);
return 0;
}
Checks if dependencies are met.
@return true if the dependencies are met.
@param l Lua state.
Argument 1: player
Argument 2: object which we want to check the dependencies of
static int CclCheckDependency(lua_State *l)
{
LuaCheckArgs(l, 2);
const char *object = LuaToString(l, 2);
lua_pop(l, 1);
const int plynr = TriggerGetPlayer(l);
if (plynr == -1) {
LuaError(l, "bad player: %i" _C_ plynr);
}
CPlayer &player = Players[plynr];
lua_pushboolean(l, CheckDependByIdent(player, object));
return 1;
}
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, "regeneration-frequency")) {
um->Modifier.Variables[HP_INDEX].IncreaseFrequency = 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, "improve-production")) {
const char *value = LuaToString(l, j + 1, 2);
const int resId = GetResourceIdByName(l, value);
um->Modifier.ImproveIncomes[resId] = LuaToNumber(l, j + 1, 3);
} 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;
}
Description
Define which units/upgrades are allowed.
Example:
DefineAllow("unit-town-hall","AAAAAAAAAAAAAAAA") -- Available for everybody
DefineAllow("unit-stables","FFFFFFFFFFFFFFFF") -- Not available
DefineAllow("upgrade-sword1","RRRRRRRRRRRRRRRR") -- Upgrade already researched.
static int CclDefineAllow(lua_State *l)
{
const int UnitMax = 65536; /// How many units supported
const int args = lua_gettop(l);
for (int j = 0; j < args; ++j) {
const char *ident = LuaToString(l, j + 1);
++j;
const char *ids = LuaToString(l, j + 1);
int n = strlen(ids);
if (n > PlayerMax) {
fprintf(stderr, "%s: Allow string too long %d\n", ident, n);
n = PlayerMax;
}
if (!strncmp(ident, "unit-", 5)) {
int id = UnitTypeIdByIdent(ident);
for (int i = 0; i < n; ++i) {
if (ids[i] == 'A') {
AllowUnitId(Players[i], id, UnitMax);
} else if (ids[i] == 'F') {
AllowUnitId(Players[i], id, 0);
}
}
} else if (!strncmp(ident, "upgrade-", 8)) {
int id = UpgradeIdByIdent(ident);
for (int i = 0; i < n; ++i) {
AllowUpgradeId(Players[i], id, ids[i]);
}
} else {
DebugPrint(" wrong ident %s\n" _C_ ident);
}
}
return 0;
}
Define which units are allowed and how much.
static int CclDefineUnitAllow(lua_State *l)
{
const int args = lua_gettop(l);
const char *ident = LuaToString(l, 0 + 1);
if (strncmp(ident, "unit-", 5)) {
DebugPrint(" wrong ident %s\n" _C_ ident);
return 0;
}
int id = UnitTypeIdByIdent(ident);
int i = 0;
for (int j = 1; j < args && i < PlayerMax; ++j) {
AllowUnitId(Players[i], id, LuaToNumber(l, j + 1));
++i;
}
return 0;
}
Description
Set training queue
@param l Lua state.
@return The old state of the training queue
Example:
-- Training queue available. Train multiple units.
SetTrainingQueue(true)
-- Train one unit at a time.
SetTrainingQueue(false)
static int CclSetTrainingQueue(lua_State *l)
{
LuaCheckArgs(l, 1);
EnableTrainingQueue = LuaToBoolean(l, 1);
return 0;
}
Set capture buildings
@param l Lua state.
@return The old state of the flag
Example:
SetBuildingCapture(true)
SetBuildingCapture(false)
static int CclSetBuildingCapture(lua_State *l)
{
LuaCheckArgs(l, 1);
EnableBuildingCapture = LuaToBoolean(l, 1);
return 0;
}
Set reveal attacker
@param l Lua state.
@return The old state of the flag
Example:
SetRevealAttacker(true)
SetRevealAttacker(false)
static int CclSetRevealAttacker(lua_State *l)
{
LuaCheckArgs(l, 1);
RevealAttacker = LuaToBoolean(l, 1);
return 0;
}
Set cost multiplier to RepairCost for buildings additional workers helping (0 = no additional cost)
@param l Lua state.
Example:
-- No cost
ResourcesMultiBuildersMultiplier(0)
-- Each builder helping will cost 1 resource
ResourcesMultiBuildersMultiplier(1)
-- Each builder helping will cost 10 resource
ResourcesMultiBuildersMultiplier(10)
static int CclResourcesMultiBuildersMultiplier(lua_State *l)
{
LuaCheckArgs(l, 1);
ResourcesMultiBuildersMultiplier = LuaToNumber(l, 1);
return 0;
}
Parse unit
@param l Lua state.
@todo Verify that vision table is always correct (transporter)
@todo (PlaceUnit() and host-info).
Example:
footman = CreateUnit("unit-footman", 0, {0, 1})
-- The unit will appear selected
Unit(footman,{"selected"})
-- The unit will be considered destroyed
Unit(footman,{"destroyed"})
-- The unit will be considered removed
Unit(footman,{"removed"})
-- The unit will be considered as a summoned unit
Unit(footman,{"summoned",500})
-- The unit will face on south
Unit(footman,{"direction",0})
-- The unit will be displayed with his 3rd frame
Unit(footman,{"frame", 3})
-- The footman will have a high sight
Unit(footman,{"current-sight-range",9})
-- Change the unit color to be the ones from player 1
Unit(footman,{"rescued-from",1})
static int CclUnit(lua_State *l)
{
const int slot = LuaToNumber(l, 1);
if (!lua_istable(l, 2)) {
LuaError(l, "incorrect argument");
}
CUnit *unit = &UnitManager->GetSlotUnit(slot);
bool hadType = unit->Type != 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->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, *unit, pos, w, h, unit->CurrentSightRange, MapMarkTileSight);
// Detectcloak works in container
if (unit->Type->BoolFlag[DETECTCLOAK_INDEX].value) {
MapSight(*player, *unit, 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")) {
// FIXME : unsigned long should be better handled
unit->Summoned = LuaToNumber(l, 2, j + 1);
} 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->BoolFlag[REVEALER_INDEX].value) {
MapMarkUnitSight(*unit);
}
if (!hadType && unit->Container) {
// this unit was assigned to a container before it had a type, so we
// need to actually add it now, since only with a type do we know the
// BoardSize it takes up in the container
CUnit *host = unit->Container;
unit->Container = NULL;
unit->AddInContainer(*host);
}
return 0;
}
Move a unit on map.
@param l Lua state.
@return Returns the slot number of the made placed.
Example:
-- Create the unit
footman = CreateUnit("unit-footman", 0, {7, 4})
-- Move the unit to position 20 (x) and 10 (y)
MoveUnit(footman,{20,10})
static int CclMoveUnit(lua_State *l)
{
LuaCheckArgs(l, 2);
lua_pushvalue(l, 1);
CUnit *unit = CclGetUnit(l);
lua_pop(l, 1);
Vec2i ipos;
CclGetPos(l, &ipos.x, &ipos.y, 2);
if (!unit->Removed) {
unit->Remove(unit->Container);
}
if (UnitCanBeAt(*unit, ipos)) {
unit->Place(ipos);
} else {
const int heading = SyncRand() % 256;
unit->tilePos = ipos;
DropOutOnSide(*unit, heading, NULL);
}
lua_pushvalue(l, 1);
return 1;
}
Description
Remove unit from the map.
@param l Lua state.
@return Returns 1.
Example:
ogre = CreateUnit("unit-ogre", 0, {24, 89})
AddTrigger(
function() return (GameCycle > 150) end,
function()
RemoveUnit(ogre)
return false end -- end of function
)
static int CclRemoveUnit(lua_State *l)
{
LuaCheckArgs(l, 1);
lua_pushvalue(l, 1);
CUnit *unit = CclGetUnit(l);
lua_pop(l, 1);
if (unit) {
unit->Remove(NULL);
LetUnitDie(*unit);
}
lua_pushvalue(l, 1);
return 1;
}
Description
Create a unit and place it on the map
@param l Lua state.
@return Returns the slot number of the made unit.
Example:
CreateUnit("unit-human-transport", 1, {94, 0})
static int CclCreateUnit(lua_State *l)
{
LuaCheckArgs(l, 3);
lua_pushvalue(l, 1);
CUnitType *unittype = CclGetUnitType(l);
if (unittype == NULL) {
LuaError(l, "Bad unittype");
}
lua_pop(l, 1);
Vec2i ipos;
CclGetPos(l, &ipos.x, &ipos.y, 3);
lua_pushvalue(l, 2);
const int playerno = TriggerGetPlayer(l);
lua_pop(l, 1);
if (playerno == -1) {
printf("CreateUnit: You cannot use \"any\" in create-unit, specify a player\n");
LuaError(l, "bad player");
return 0;
}
if (Players[playerno].Type == PlayerTypes::PlayerNobody) {
printf("CreateUnit: player %d does not exist\n", playerno);
LuaError(l, "bad player");
return 0;
}
CUnit *unit = MakeUnit(*unittype, &Players[playerno]);
if (unit == NULL) {
DebugPrint("Unable to allocate unit");
return 0;
} else {
if (UnitCanBeAt(*unit, ipos)
|| (unit->Type->Building && CanBuildUnitType(NULL, *unit->Type, ipos, 0))) {
unit->Place(ipos);
} else {
const int heading = SyncRand() % 256;
unit->tilePos = ipos;
DropOutOnSide(*unit, heading, NULL);
}
UpdateForNewUnit(*unit, 0);
lua_pushnumber(l, UnitNumber(*unit));
return 1;
}
}
Description
'Upgrade' a unit in place to a unit of different type.
@param l Lua state.
@return Returns success.
Example:
-- Make a peon for player 5
peon = CreateUnit("unit-peon", 5, {58, 9})
-- The peon will be trasformed into a Grunt
TransformUnit(peon,"unit-grunt")
static int CclTransformUnit(lua_State *l)
{
lua_pushvalue(l, 1);
CUnit *targetUnit = CclGetUnit(l);
lua_pop(l, 1);
lua_pushvalue(l, 2);
const CUnitType *unittype = TriggerGetUnitType(l);
lua_pop(l, 1);
if (unittype && targetUnit) {
CommandUpgradeTo(*targetUnit, *(CUnitType*)unittype, 1, true);
}
lua_pushvalue(l, 1);
return 1;
}
Description
Damages unit, additionally using another unit as first's attacker
@param l Lua state.
@return Returns the slot number of the made unit.
Example:
-- Make a grunt for player 5
grunt = CreateUnit("unit-grunt", 5, {58, 8})
-- Damage the grunt with 15 points
DamageUnit(-1,grunt,15)
static int CclDamageUnit(lua_State *l)
{
LuaCheckArgs(l, 3);
const int attacker = LuaToNumber(l, 1);
CUnit *attackerUnit = NULL;
if (attacker != -1) {
attackerUnit = &UnitManager->GetSlotUnit(attacker);
}
lua_pushvalue(l, 2);
CUnit *targetUnit = CclGetUnit(l);
lua_pop(l, 1);
const int damage = LuaToNumber(l, 3);
HitUnit(attackerUnit, *targetUnit, damage);
return 1;
}
Set resources held by a unit
@param l Lua state.
static int CclSetResourcesHeld(lua_State *l)
{
LuaCheckArgs(l, 2);
if (lua_isnil(l, 1)) {
return 0;
}
lua_pushvalue(l, 1);
CUnit *unit = CclGetUnit(l);
lua_pop(l, 1);
const int value = LuaToNumber(l, 2);
unit->ResourcesHeld = value;
unit->Variable[GIVERESOURCE_INDEX].Value = value;
unit->Variable[GIVERESOURCE_INDEX].Max = value;
unit->Variable[GIVERESOURCE_INDEX].Enable = 1;
return 0;
}
Set teleport deastination for teleporter unit
@param l Lua state.
static int CclSetTeleportDestination(lua_State *l)
{
LuaCheckArgs(l, 2);
lua_pushvalue(l, 1);
CUnit *unit = CclGetUnit(l);
lua_pop(l, 1);
if (unit->Type->BoolFlag[TELEPORTER_INDEX].value == false) {
LuaError(l, "Unit not a teleporter");
}
lua_pushvalue(l, 2);
CUnit *dest = CclGetUnit(l);
lua_pop(l, 1);
if (dest->IsAliveOnMap()) {
unit->Goal = dest;
}
return 0;
}
Description
Order a unit
@param l Lua state.
OrderUnit(player, unit-type, start_loc, dest_loc, order)
Example:
-- Move transport from position x=94,y=0 to x=80,y=9
OrderUnit(1,"unit-human-transport",{94,0},{80,9},"move")
static int CclOrderUnit(lua_State *l)
{
LuaCheckArgs(l, 5);
lua_pushvalue(l, 1);
const int plynr = TriggerGetPlayer(l);
lua_pop(l, 1);
lua_pushvalue(l, 2);
const CUnitType *unittype = TriggerGetUnitType(l);
lua_pop(l, 1);
if (!lua_istable(l, 3)) {
LuaError(l, "incorrect argument");
}
Vec2i pos1;
pos1.x = LuaToNumber(l, 3, 1);
pos1.y = LuaToNumber(l, 3, 2);
Vec2i pos2;
if (lua_rawlen(l, 3) == 4) {
pos2.x = LuaToNumber(l, 3, 3);
pos2.y = LuaToNumber(l, 3, 4);
} else {
pos2 = pos1;
}
if (!lua_istable(l, 4)) {
LuaError(l, "incorrect argument");
}
Vec2i dpos1;
Vec2i dpos2;
dpos1.x = LuaToNumber(l, 4, 1);
dpos1.y = LuaToNumber(l, 4, 2);
if (lua_rawlen(l, 4) == 4) {
dpos2.x = LuaToNumber(l, 4, 3);
dpos2.y = LuaToNumber(l, 4, 4);
} else {
dpos2 = dpos1;
}
const char *order = LuaToString(l, 5);
std::vector<CUnit *> table;
Select(pos1, pos2, table);
for (size_t i = 0; i != table.size(); ++i) {
CUnit &unit = *table[i];
if (unittype == ANY_UNIT
|| (unittype == ALL_FOODUNITS && !unit.Type->Building)
|| (unittype == ALL_BUILDINGS && unit.Type->Building)
|| unittype == unit.Type) {
if (plynr == -1 || plynr == unit.Player->Index) {
if (!strcmp(order, "move")) {
CommandMove(unit, (dpos1 + dpos2) / 2, 1);
} else if (!strcmp(order, "stop")) {
CommandStopUnit(unit); //Stop the unit
} else if (!strcmp(order, "stand-ground")) {
CommandStandGround(unit,0); //Stand and flush every order
} else if (!strcmp(order, "attack")) {
CUnit *attack = TargetOnMap(unit, dpos1, dpos2);
CommandAttack(unit, (dpos1 + dpos2) / 2, attack, 1);
} else if (!strcmp(order, "explore")) {
CommandExplore(unit, 1);
} else if (!strcmp(order, "patrol")) {
CommandPatrolUnit(unit, (dpos1 + dpos2) / 2, 1);
} else {
LuaError(l, "Unsupported order: %s" _C_ order);
}
}
}
}
return 0;
}
Description
Kill a unit
@param l Lua state.
@return Returns true if a unit was killed.
Example:
-- Kills an ogre controlled by player 3
KillUnit("unit-ogre", 3)
static int CclKillUnit(lua_State *l)
{
LuaCheckArgs(l, 2);
lua_pushvalue(l, 1);
const CUnitType *unittype = TriggerGetUnitType(l);
lua_pop(l, 1);
const int plynr = TriggerGetPlayer(l);
if (plynr == -1) {
CUnitManager::Iterator it = std::find_if(UnitManager->begin(), UnitManager->end(), HasSameUnitTypeAs(unittype));
if (it != UnitManager->end()) {
LetUnitDie(**it);
lua_pushboolean(l, 1);
return 1;
}
} else {
CPlayer &player = Players[plynr];
std::vector<CUnit *>::iterator it = std::find_if(player.UnitBegin(), player.UnitEnd(), HasSameUnitTypeAs(unittype));
if (it != player.UnitEnd()) {
LetUnitDie(**it);
lua_pushboolean(l, 1);
return 1;
}
}
lua_pushboolean(l, 0);
return 1;
}
Description
Kill a unit at a location
@param l Lua state.
@return Returns the number of units killed.
Example:
-- Kill 8 peasants controlled by player 7 from position {27,1} to {34,5}
KillUnitAt("unit-peasant",7,8,{27,1},{34,5})
static int CclKillUnitAt(lua_State *l)
{
LuaCheckArgs(l, 5);
lua_pushvalue(l, 1);
const CUnitType *unittype = TriggerGetUnitType(l);
lua_pop(l, 1);
lua_pushvalue(l, 2);
int plynr = TriggerGetPlayer(l);
lua_pop(l, 1);
int q = LuaToNumber(l, 3);
if (!lua_istable(l, 4) || !lua_istable(l, 5)) {
LuaError(l, "incorrect argument");
}
Vec2i pos1;
Vec2i pos2;
CclGetPos(l, &pos1.x, &pos1.y, 4);
CclGetPos(l, &pos2.x, &pos2.y, 5);
if (pos1.x > pos2.x) {
std::swap(pos1.x, pos2.x);
}
if (pos1.y > pos2.y) {
std::swap(pos1.y, pos2.y);
}
std::vector<CUnit *> table;
Select(pos1, pos2, table);
int s = 0;
for (std::vector<CUnit *>::iterator it = table.begin(); it != table.end() && s < q; ++it) {
CUnit &unit = **it;
if (unittype == ANY_UNIT
|| (unittype == ALL_FOODUNITS && !unit.Type->Building)
|| (unittype == ALL_BUILDINGS && unit.Type->Building)
|| unittype == unit.Type) {
if ((plynr == -1 || plynr == unit.Player->Index) && unit.IsAlive()) {
LetUnitDie(unit);
++s;
}
}
}
lua_pushnumber(l, s);
return 1;
}
Find the next reachable resource unit that gives resource starting from a worker.
Optional third argument is the range to search.
@param l Lua state.
Example:
peon = CreateUnit("unit-peon", 5, {58, 8})
goldmine = FindNextResource(peon, 0)
static int CclFindNextResource(lua_State *l)
{
const int nargs = lua_gettop(l);
if (nargs < 2 || nargs > 3) {
LuaError(l, "incorrect argument count");
}
lua_pushvalue(l, 1);
CUnit *unit = CclGetUnit(l);
lua_pop(l, 1);
lua_pushvalue(l, 2);
const int resource = CclGetResourceByName(l);
lua_pop(l, 1);
const int range = nargs == 3 ? LuaToNumber(l, 3) : 1000;
CUnit *resourceUnit = UnitFindResource(*unit, *unit, range, resource);
if (resourceUnit) {
lua_pushnumber(l, UnitNumber(*unit));
} else {
lua_pushnil(l);
}
return 1;
}
Description
Get a player's units
@param l Lua state.
@return Array of units.
Example:
-- Get units from player 0
units = GetUnits(0)
for i, id_unit in ipairs(units) do
print(id_unit)
end
static int CclGetUnits(lua_State *l)
{
LuaCheckArgs(l, 1);
const int plynr = TriggerGetPlayer(l);
lua_newtable(l);
if (plynr == -1) {
int i = 0;
for (CUnitManager::Iterator it = UnitManager->begin(); it != UnitManager->end(); ++it, ++i) {
const CUnit &unit = **it;
lua_pushnumber(l, UnitNumber(unit));
lua_rawseti(l, -2, i + 1);
}
} else {
for (int i = 0; i < Players[plynr].GetUnitCount(); ++i) {
lua_pushnumber(l, UnitNumber(Players[plynr].GetUnit(i)));
lua_rawseti(l, -2, i + 1);
}
}
return 1;
}
Description
Get a player's units in rectangle box specified with 2 coordinates
@param l Lua state.
@return Array of units.
Example:
circlePower = CreateUnit("unit-circle-of-power", 15, {59, 4})
-- Get the units near the circle of power.
unitsOnCircle = GetUnitsAroundUnit(circle,1,true)
static int CclGetUnitsAroundUnit(lua_State *l)
{
const int nargs = lua_gettop(l);
if (nargs != 2 && nargs != 3) {
LuaError(l, "incorrect argument\n");
}
const int slot = LuaToNumber(l, 1);
const CUnit &unit = UnitManager->GetSlotUnit(slot);
const int range = LuaToNumber(l, 2);
bool allUnits = false;
if (nargs == 3) {
allUnits = LuaToBoolean(l, 3);
}
lua_newtable(l);
std::vector<CUnit *> table;
if (allUnits) {
SelectAroundUnit(unit, range, table, HasNotSamePlayerAs(Players[PlayerNumNeutral]));
} else {
SelectAroundUnit(unit, range, table, HasSamePlayerAs(*unit.Player));
}
size_t n = 0;
for (size_t i = 0; i < table.size(); ++i) {
if (table[i]->IsAliveOnMap()) {
lua_pushnumber(l, UnitNumber(*table[i]));
lua_rawseti(l, -2, ++n);
}
}
return 1;
}
Get the value of the unit bool-flag.
@param l Lua state.
@return The value of the bool-flag of the unit.
static int CclGetUnitBoolFlag(lua_State *l)
{
LuaCheckArgs(l, 2);
lua_pushvalue(l, 1);
const CUnit *unit = CclGetUnit(l);
lua_pop(l, 1);
const char *const value = LuaToString(l, 2);
int index = UnitTypeVar.BoolFlagNameLookup[value];// User bool flags
if (index == -1) {
LuaError(l, "Bad bool-flag name '%s'\n" _C_ value);
}
lua_pushboolean(l, unit->Type->BoolFlag[index].value);
return 1;
}
Description
Get the value of the unit variable.
@param l Lua state.
@return The value of the variable of the unit.
Example:
-- Make a grunt for player 5
grunt = CreateUnit("unit-grunt", 5, {58, 8})
-- Take the name of the unit
unit_name = GetUnitVariable(grunt,"Name")
-- Take the player number based on the unit
player_type = GetUnitVariable(grunt,"PlayerType")
-- Take the value of the armor
armor_value = GetUnitVariable(grunt,"Armor")
-- Show the message in the game.
AddMessage(unit_name .. " " .. player_type .. " " .. armor_value)
static int CclGetUnitVariable(lua_State *l)
{
const int nargs = lua_gettop(l);
Assert(nargs == 2 || nargs == 3);
lua_pushvalue(l, 1);
CUnit *unit = CclGetUnit(l);
if (unit == NULL) {
return 1;
}
UpdateUnitVariables(*unit);
lua_pop(l, 1);
const char *const value = LuaToString(l, 2);
if (!strcmp(value, "RegenerationRate")) {
lua_pushnumber(l, unit->Variable[HP_INDEX].Increase);
} else if (!strcmp(value, "RegenerationFrequency")) {
lua_pushnumber(l, std::max((int)unit->Variable[HP_INDEX].IncreaseFrequency, 1));
} else if (!strcmp(value, "Ident")) {
lua_pushstring(l, unit->Type->Ident.c_str());
} else if (!strcmp(value, "ResourcesHeld")) {
lua_pushnumber(l, unit->ResourcesHeld);
} else if (!strcmp(value, "GiveResourceType")) {
lua_pushnumber(l, unit->Type->GivesResource);
} else if (!strcmp(value, "CurrentResource")) {
lua_pushnumber(l, unit->CurrentResource);
} else if (!strcmp(value, "Name")) {
lua_pushstring(l, unit->Type->Name.c_str());
} else if (!strcmp(value, "PlayerType")) {
lua_pushstring(l, PlayerTypeNames[static_cast<int>(unit->Player->Type)].c_str());
} else if (!strcmp(value, "TTLPercent")) {
if (unit->Summoned && unit->TTL) {
unsigned long time_lived = GameCycle - unit->Summoned;
Assert(time_lived >= 0);
unsigned long time_to_live = unit->TTL - unit->Summoned;
Assert(time_to_live > 0);
double pcnt = time_lived * 100.0 / time_to_live;
int pcnt_i = (int)round(pcnt);
lua_pushinteger(l, pcnt_i);
} else {
lua_pushinteger(l, -1);
}
} else if (!strcmp(value, "IndividualUpgrade")) {
LuaCheckArgs(l, 3);
std::string upgrade_ident = LuaToString(l, 3);
if (CUpgrade::Get(upgrade_ident)) {
lua_pushboolean(l, unit->IndividualUpgrades[CUpgrade::Get(upgrade_ident)->ID]);
} else {
LuaError(l, "Individual upgrade \"%s\" doesn't exist." _C_ upgrade_ident.c_str());
}
return 1;
} else if (!strcmp(value, "Active")) {
lua_pushboolean(l, unit->Active);
return 1;
} else if (!strcmp(value, "Idle")) {
lua_pushboolean(l, unit->IsIdle());
return 1;
} else if (!strcmp(value, "PixelPos")) {
PixelPos pos = unit->GetMapPixelPosCenter();
lua_newtable(l);
lua_pushnumber(l, pos.x);
lua_setfield(l, -2, "x");
lua_pushnumber(l, pos.y);
lua_setfield(l, -2, "y");
return 1;
} else {
int index = UnitTypeVar.VariableNameLookup[value];// User variables
if (index == -1) {
if (nargs == 2) {
index = UnitTypeVar.BoolFlagNameLookup[value];
if (index != -1) {
lua_pushboolean(l, unit->Type->BoolFlag[index].value);
return 1;
}
}
}
if (index == -1) {
LuaError(l, "Bad variable name '%s'\n" _C_ value);
}
if (nargs == 2) {
lua_pushnumber(l, unit->Variable[index].Value);
} else {
const char *const type = LuaToString(l, 3);
if (!strcmp(type, "Value")) {
lua_pushnumber(l, unit->Variable[index].Value);
} else if (!strcmp(type, "Max")) {
lua_pushnumber(l, unit->Variable[index].Max);
} else if (!strcmp(type, "Increase")) {
lua_pushnumber(l, unit->Variable[index].Increase);
} else if (!strcmp(type, "IncreaseFrequency")) {
lua_pushnumber(l, std::max((int)unit->Variable[index].IncreaseFrequency, 1));
} else if (!strcmp(type, "Enable")) {
lua_pushnumber(l, unit->Variable[index].Enable);
} else {
LuaError(l, "Bad variable type '%s'\n" _C_ type);
}
}
}
return 1;
}
Description
Set the value of the unit variable.
@param l Lua state.
@return The new value of the unit.
Example:
-- Create a blacksmith for player 2
blacksmith = CreateUnit("unit-human-blacksmith", 2, {66, 71})
-- Specify the amount of hit points to assign to the blacksmith
SetUnitVariable(blacksmith,"HitPoints",344)
-- Set the blacksmiths color to the color of player 4
SetUnitVariable(blacksmith,"Color",4)
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 = 0;
if (!strcmp(name, "Player")) {
value = LuaToNumber(l, 3);
unit->ChangeOwner(Players[value]);
} else if (!strcmp(name, "Color")) {
if (lua_isstring(l, 3)) {
const char *colorName = LuaToString(l, 3);
for (size_t i = 0; i < PlayerColorNames.size(); i++) {
if (PlayerColorNames[i] == colorName) {
unit->Colors = i;
break;
}
}
} else if (lua_isnil(l, 3)) {
unit->Colors = -1;
} else {
value = LuaToNumber(l, 3);
unit->Colors = value;
}
} else if (!strcmp(name, "TTL")) {
value = LuaToNumber(l, 3);
unit->TTL = GameCycle + value;
} else if (!strcmp(name, "Summoned")) {
value = LuaToNumber(l, 3);
unit->Summoned = value;
} 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, "RegenerationFrequency")) {
value = LuaToNumber(l, 3);
unit->Variable[HP_INDEX].IncreaseFrequency = value;
if (unit->Variable[HP_INDEX].IncreaseFrequency != value) {
LuaError(l, "RegenerationFrequency out of range!");
}
} 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, "IncreaseFrequency")) {
unit->Stats->Variables[index].IncreaseFrequency = value;
if (unit->Stats->Variables[index].IncreaseFrequency != value) {
LuaError(l, "%s.IncreaseFrequency out of range!" _C_ type);
}
} 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, "IncreaseFrequency")) {
unit->Variable[index].IncreaseFrequency = value;
if (unit->Variable[index].IncreaseFrequency != value) {
LuaError(l, "%s.IncreaseFrequency out of range!" _C_ type);
}
} 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;
}
Get the usage of unit slots during load to allocate memory
@param l Lua state.
static int CclSlotUsage(lua_State *l)
{
UnitManager->Load(l);
return 0;
}
Description
Select a single unit
@param l Lua state.
@return 0, meaning the unit is selected.
Example:
-- Make the hero unit Grom Hellscream for player 5
grom = CreateUnit("unit-beast-cry", 5, {58, 8})
-- Select only the unit Grom Hellscream
SelectSingleUnit(grom)
static int CclSelectSingleUnit(lua_State *l)
{
const int nargs = lua_gettop(l);
Assert(nargs == 1);
lua_pushvalue(l, 1);
CUnit *unit = CclGetUnit(l);
lua_pop(l, 1);
SelectSingleUnit(*unit);
SelectionChanged();
return 0;
}
Enable/disable simplified auto targeting
@param l Lua state.
@return 0 for success, 1 for wrong type;
static int CclEnableSimplifiedAutoTargeting(lua_State *l)
{
LuaCheckArgs(l, 1);
const bool isSimplified = LuaToBoolean(l, 1);
if (!IsNetworkGame()) {
GameSettings.SimplifiedAutoTargeting = isSimplified;
} else {
NetworkSendExtendedCommand(ExtendedMessageAutoTargetingDB,
int(isSimplified), 0, 0, 0, 0);
}
return 0;
}
Define the sprite to show variables.
@param l Lua_state
static int CclDefineSprites(lua_State *l)
{
const int args = lua_gettop(l);
for (int i = 0; i < args; ++i) {
Decoration deco;
lua_pushnil(l);
const char *name = NULL;// name of the current sprite.
while (lua_next(l, i + 1)) {
const char *key = LuaToString(l, -2); // key name
if (!strcmp(key, "Name")) {
name = LuaToString(l, -1);
} else if (!strcmp(key, "File")) {
deco.File = LuaToString(l, -1);
} else if (!strcmp(key, "Offset")) {
CclGetPos(l, &deco.HotPos.x, &deco.HotPos.y);
} else if (!strcmp(key, "Size")) {
CclGetPos(l, &deco.Width, &deco.Height);
} else { // Error.
LuaError(l, "incorrect field '%s' for the DefineSprite." _C_ key);
}
lua_pop(l, 1); // pop the value;
}
if (name == NULL) {
LuaError(l, "CclDefineSprites requires the Name flag for sprite.");
}
int index = GetSpriteIndex(name); // Index of the Sprite.
if (index == -1) { // new sprite.
index = DecoSprite.SpriteArray.size();
DecoSprite.Name.push_back(name);
DecoSprite.SpriteArray.push_back(deco);
} else {
DecoSprite.SpriteArray[index].File.clear();
DecoSprite.SpriteArray[index] = deco;
}
// Now verify validity.
if (DecoSprite.SpriteArray[index].File.empty()) {
LuaError(l, "CclDefineSprites requires the File flag for sprite.");
}
// FIXME check if file is valid with good size ?
}
return 0;
}
Description
Parse unit-type.
@param l Lua state.
Example:
DefineUnitType("unit-silvermoon-archer", { Name = _("Silvermoon Archer"),
Image = {"file", "human/units/elven_archer.png", "size", {72, 72}},
Animations = "animations-archer", Icon = "icon-archer",
Costs = {"time", 70, "gold", 500, "wood", 50},
Speed = 10,
HitPoints = 45,
DrawLevel = 40,
TileSize = {1, 1}, BoxSize = {33, 33},
SightRange = 6, ComputerReactionRange = 7, PersonReactionRange = 6,
BasicDamage = 4, PiercingDamage = 6, Missile = "missile-arrow",
MaxAttackRange = 4,
Priority = 75,
Points = 60,
Demand = 1,
Corpse = "unit-human-dead-body",
Type = "land",
RightMouseAction = "attack",
CanAttack = true,
CanTargetLand = true, CanTargetSea = true, CanTargetAir = true,
LandUnit = true,
organic = true,
SelectableByRectangle = true,
Sounds = {
"selected", "archer-selected",
"acknowledge", "archer-acknowledge",
"ready", "archer-ready",
"help", "basic human voices help 1",
"dead", "basic human voices dead"} } )
static int CclDefineUnitType(lua_State *l)
{
LuaCheckArgs(l, 2);
if (!lua_istable(l, 2)) {
LuaError(l, "incorrect argument");
}
// Slot identifier
const char *str = LuaToString(l, 1);
CUnitType *type = UnitTypeByIdent(str);
constexpr int redefineSprite = 2;
int redefine;
if (type) {
redefine = 1;
DebugPrint("Redefining unit-type '%s'\n" _C_ str);
} else {
type = NewUnitTypeSlot(str);
redefine = 0;
type->NumDirections = 0;
type->Flip = 1;
}
// Parse the list: (still everything could be changed!)
for (lua_pushnil(l); lua_next(l, 2); lua_pop(l, 1)) {
const char *value = LuaToString(l, -2);
if (!strcmp(value, "Name")) {
type->Name = LuaToString(l, -1);
} else if (!strcmp(value, "Image")) {
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
int subargs = lua_rawlen(l, -1);
for (int k = 0; k < subargs; ++k) {
value = LuaToString(l, -1, k + 1);
++k;
if (!strcmp(value, "file")) {
type->File = LuaToString(l, -1, k + 1);
} else if (!strcmp(value, "alt-file")) {
type->AltFile = LuaToString(l, -1, k + 1);
} else if (!strcmp(value, "size")) {
lua_rawgeti(l, -1, k + 1);
CclGetPos(l, &type->Width, &type->Height);
lua_pop(l, 1);
} else {
LuaError(l, "Unsupported image tag: %s" _C_ value);
}
}
if (redefine) {
if (type->Sprite && type->Sprite->File != type->File) {
redefine |= redefineSprite;
CGraphic::Free(type->Sprite);
type->Sprite = NULL;
}
if (type->AltSprite && type->AltSprite->File != type->AltFile) {
redefine |= redefineSprite;
CGraphic::Free(type->AltSprite);
type->AltSprite = NULL;
}
if (redefine && type->ShadowSprite) {
redefine |= redefineSprite;
CGraphic::Free(type->ShadowSprite);
type->ShadowSprite = NULL;
}
}
if (type->ShadowFile == shadowMarker) {
type->ShadowFile = type->File;
if (type->ShadowWidth == 0 && type->ShadowHeight == 0) {
type->ShadowWidth = type->Width;
type->ShadowHeight = type->Height;
}
}
} else if (!strcmp(value, "Shadow")) {
// default to same spritemap as unit
if (type->File.length() > 0) {
type->ShadowFile = type->File;
type->ShadowWidth = type->Width;
type->ShadowHeight = type->Height;
} else {
type->ShadowFile = shadowMarker;
}
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
const int subargs = lua_rawlen(l, -1);
for (int k = 0; k < subargs; ++k) {
value = LuaToString(l, -1, k + 1);
++k;
if (!strcmp(value, "file")) {
type->ShadowFile = LuaToString(l, -1, k + 1);
} else if (!strcmp(value, "size")) {
lua_rawgeti(l, -1, k + 1);
CclGetPos(l, &type->ShadowWidth, &type->ShadowHeight);
lua_pop(l, 1);
} else if (!strcmp(value, "offset")) {
lua_rawgeti(l, -1, k + 1);
CclGetPos(l, &type->ShadowOffsetX, &type->ShadowOffsetY);
lua_pop(l, 1);
} else if (!strcmp(value, "sprite-frame")) {
type->ShadowSpriteFrame = LuaToNumber(l, -1, k + 1);
} else if (!strcmp(value, "scale")) {
type->ShadowScale = LuaToNumber(l, -1, k + 1);
} else {
LuaError(l, "Unsupported shadow tag: %s" _C_ value);
}
}
if (redefine && type->ShadowSprite) {
redefine |= redefineSprite;
CGraphic::Free(type->ShadowSprite);
type->ShadowSprite = NULL;
}
} else if (!strcmp(value, "Offset")) {
CclGetPos(l, &type->OffsetX, &type->OffsetY);
} else if (!strcmp(value, "Flip")) {
type->Flip = LuaToBoolean(l, -1);
} else if (!strcmp(value, "Animations")) {
type->Animations = AnimationsByIdent(LuaToString(l, -1));
if (!type->Animations) {
DebugPrint("Warning animation '%s' not found\n" _C_ LuaToString(l, -1));
}
} else if (!strcmp(value, "Icon")) {
type->Icon.Name = LuaToString(l, -1);
type->Icon.Icon = NULL;
} else if (!strcmp(value, "Portrait")) {
#ifdef USE_MNG
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
const int subargs = lua_rawlen(l, -1);
int number = 0;
for (int k = 0; k < subargs; ++k) {
const char *s = LuaToString(l, -1, k + 1);
if (strcmp("talking", s)) {
number++;
}
}
type->Portrait.Num = number;
type->Portrait.Talking = 0;
type->Portrait.Files = new std::string[type->Portrait.Num];
type->Portrait.Mngs = new Mng *[type->Portrait.Num];
memset(type->Portrait.Mngs, 0, type->Portrait.Num * sizeof(Mng *));
for (int k = 0; k < subargs; ++k) {
const char *s = LuaToString(l, -1, k + 1);
if (!strcmp("talking", s)) {
type->Portrait.Talking = k;
} else {
type->Portrait.Files[k - (type->Portrait.Talking ? 1 : 0)] = s;
}
}
#endif
} else if (!strcmp(value, "Costs")) {
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
const int subargs = lua_rawlen(l, -1);
for (int k = 0; k < subargs; ++k) {
lua_rawgeti(l, -1, k + 1);
const int res = CclGetResourceByName(l);
lua_pop(l, 1);
++k;
type->DefaultStat.Costs[res] = LuaToNumber(l, -1, k + 1);
}
} else if (!strcmp(value, "Storing")) {
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
const int subargs = lua_rawlen(l, -1);
for (int k = 0; k < subargs; ++k) {
lua_rawgeti(l, -1, k + 1);
const int res = CclGetResourceByName(l);
lua_pop(l, 1);
++k;
type->DefaultStat.Storing[res] = LuaToNumber(l, -1, k + 1);
}
} else if (!strcmp(value, "ImproveProduction")) {
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
const int subargs = lua_rawlen(l, -1);
for (int k = 0; k < subargs; ++k) {
lua_rawgeti(l, -1, k + 1);
const int res = CclGetResourceByName(l);
lua_pop(l, 1);
++k;
type->DefaultStat.ImproveIncomes[res] = DefaultIncomes[res] + LuaToNumber(l, -1, k + 1);
}
} else if (!strcmp(value, "Construction")) {
// FIXME: What if constructions aren't yet loaded?
type->Construction = ConstructionByIdent(LuaToString(l, -1));
} else if (!strcmp(value, "DrawLevel")) {
type->DrawLevel = LuaToNumber(l, -1);
} else if (!strcmp(value, "MaxOnBoard")) {
type->MaxOnBoard = LuaToNumber(l, -1);
} else if (!strcmp(value, "BoardSize")) {
type->BoardSize = LuaToNumber(l, -1);
} else if (!strcmp(value, "ButtonLevelForTransporter")) {
type->ButtonLevelForTransporter = LuaToNumber(l, -1);
} else if (!strcmp(value, "StartingResources")) {
type->StartingResources = LuaToNumber(l, -1);
} else if (!strcmp(value, "RegenerationRate")) {
type->DefaultStat.Variables[HP_INDEX].Increase = LuaToNumber(l, -1);
} else if (!strcmp(value, "RegenerationFrequency")) {
int value = LuaToNumber(l, -1);
type->DefaultStat.Variables[HP_INDEX].IncreaseFrequency = value;
if (type->DefaultStat.Variables[HP_INDEX].IncreaseFrequency != value) {
LuaError(l, "RegenerationFrequency out of range!");
}
} else if (!strcmp(value, "BurnPercent")) {
type->BurnPercent = LuaToNumber(l, -1);
} else if (!strcmp(value, "BurnDamageRate")) {
type->BurnDamageRate = LuaToNumber(l, -1);
} else if (!strcmp(value, "PoisonDrain")) {
type->PoisonDrain = LuaToNumber(l, -1);
} else if (!strcmp(value, "ShieldPoints")) {
if (lua_istable(l, -1)) {
DefineVariableField(l, type->DefaultStat.Variables + SHIELD_INDEX, -1);
} else if (lua_isnumber(l, -1)) {
type->DefaultStat.Variables[SHIELD_INDEX].Max = LuaToNumber(l, -1);
type->DefaultStat.Variables[SHIELD_INDEX].Value = 0;
type->DefaultStat.Variables[SHIELD_INDEX].Increase = 1;
type->DefaultStat.Variables[SHIELD_INDEX].Enable = 1;
}
} else if (!strcmp(value, "TileSize")) {
CclGetPos(l, &type->TileWidth, &type->TileHeight);
} else if (!strcmp(value, "NeutralMinimapColor")) {
type->NeutralMinimapColorRGB.Parse(l);
} else if (!strcmp(value, "Neutral")) {
type->Neutral = LuaToBoolean(l, -1);
} else if (!strcmp(value, "BoxSize")) {
CclGetPos(l, &type->BoxWidth, &type->BoxHeight);
} else if (!strcmp(value, "BoxOffset")) {
CclGetPos(l, &type->BoxOffsetX, &type->BoxOffsetY);
} else if (!strcmp(value, "NumDirections")) {
type->NumDirections = LuaToNumber(l, -1);
} else if (!strcmp(value, "ComputerReactionRange")) {
type->ReactRangeComputer = LuaToNumber(l, -1);
} else if (!strcmp(value, "PersonReactionRange")) {
type->ReactRangePerson = LuaToNumber(l, -1);
} else if (!strcmp(value, "Missile")) {
type->Missile.Name = LuaToString(l, -1);
type->Missile.Missile = NULL;
} else if (!strcmp(value, "MinAttackRange")) {
type->MinAttackRange = LuaToNumber(l, -1);
} else if (!strcmp(value, "MaxAttackRange")) {
type->DefaultStat.Variables[ATTACKRANGE_INDEX].Value = LuaToNumber(l, -1);
type->DefaultStat.Variables[ATTACKRANGE_INDEX].Max = LuaToNumber(l, -1);
type->DefaultStat.Variables[ATTACKRANGE_INDEX].Enable = 1;
} else if (!strcmp(value, "MaxHarvesters")) {
type->DefaultStat.Variables[MAXHARVESTERS_INDEX].Value = LuaToNumber(l, -1);
type->DefaultStat.Variables[MAXHARVESTERS_INDEX].Max = LuaToNumber(l, -1);
} else if (!strcmp(value, "Priority")) {
type->DefaultStat.Variables[PRIORITY_INDEX].Value = LuaToNumber(l, -1);
type->DefaultStat.Variables[PRIORITY_INDEX].Max = LuaToNumber(l, -1);
} else if (!strcmp(value, "AnnoyComputerFactor")) {
type->AnnoyComputerFactor = LuaToNumber(l, -1);
} else if (!strcmp(value, "AiAdjacentRange")) {
type->AiAdjacentRange = LuaToNumber(l, -1);
} else if (!strcmp(value, "DecayRate")) {
type->DecayRate = LuaToNumber(l, -1);
} else if (!strcmp(value, "Corpse")) {
type->CorpseName = LuaToString(l, -1);
type->CorpseType = NULL;
} else if (!strcmp(value, "DamageType")) {
value = LuaToString(l, -1);
//int check = ExtraDeathIndex(value);
type->DamageType = value;
} else if (!strcmp(value, "ExplodeWhenKilled")) {
type->ExplodeWhenKilled = 1;
type->Explosion.Name = LuaToString(l, -1);
type->Explosion.Missile = NULL;
} else if (!strcmp(value, "TeleportCost")) {
type->TeleportCost = LuaToNumber(l, -1);
} else if (!strcmp(value, "TeleportEffectIn")) {
type->TeleportEffectIn = new LuaCallback(l, -1);
} else if (!strcmp(value, "TeleportEffectOut")) {
type->TeleportEffectOut = new LuaCallback(l, -1);
} else if (!strcmp(value, "OnDeath")) {
type->OnDeath = new LuaCallback(l, -1);
} else if (!strcmp(value, "OnHit")) {
type->OnHit = new LuaCallback(l, -1);
} else if (!strcmp(value, "OnEachCycle")) {
type->OnEachCycle = new LuaCallback(l, -1);
} else if (!strcmp(value, "OnEachSecond")) {
type->OnEachSecond = new LuaCallback(l, -1);
} else if (!strcmp(value, "OnInit")) {
type->OnInit = new LuaCallback(l, -1);
} else if (!strcmp(value, "OnReady")) {
type->OnReady = new LuaCallback(l, -1);
} else if (!strcmp(value, "Type")) {
value = LuaToString(l, -1);
if (!strcmp(value, "land")) {
type->UnitType = UnitTypeLand;
} else if (!strcmp(value, "fly")) {
type->UnitType = UnitTypeFly;
} else if (!strcmp(value, "naval")) {
type->UnitType = UnitTypeNaval;
} else {
LuaError(l, "Unsupported Type: %s" _C_ value);
}
} else if (!strcmp(value, "MissileOffsets")) {
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
const int subargs = lua_rawlen(l, -1);
for (int k = 0; k < subargs; ++k) {
lua_rawgeti(l, -1, k + 1);
if (!lua_istable(l, -1) || lua_rawlen(l, -1) != UnitSides) {
LuaError(l, "incorrect argument");
}
for (int m = 0; m < UnitSides; ++m) {
lua_rawgeti(l, -1, m + 1);
CclGetPos(l, &type->MissileOffsets[m][k].x, &type->MissileOffsets[m][k].y);
lua_pop(l, 1);
}
lua_pop(l, 1);
}
} else if (!strcmp(value, "Impact")) {
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
const int subargs = lua_rawlen(l, -1);
for (int k = 0; k < subargs; ++k) {
const char *dtype = LuaToString(l, -1, k + 1);
++k;
if (!strcmp(dtype, "general")) {
type->Impact[ANIMATIONS_DEATHTYPES].Name = LuaToString(l, -1, k + 1);
type->Impact[ANIMATIONS_DEATHTYPES].Missile = NULL;
} else if (!strcmp(dtype, "shield")) {
type->Impact[ANIMATIONS_DEATHTYPES + 1].Name = LuaToString(l, -1, k + 1);
type->Impact[ANIMATIONS_DEATHTYPES + 1].Missile = NULL;
} else {
int num = 0;
for (; num < ANIMATIONS_DEATHTYPES; ++num) {
if (dtype == ExtraDeathTypes[num]) {
break;
}
}
if (num == ANIMATIONS_DEATHTYPES) {
LuaError(l, "Death type not found: %s" _C_ dtype);
} else {
type->Impact[num].Name = LuaToString(l, -1, k + 1);
type->Impact[num].Missile = NULL;
}
}
}
} else if (!strcmp(value, "RightMouseAction")) {
value = LuaToString(l, -1);
if (!strcmp(value, "none")) {
type->MouseAction = MouseActionNone;
} else if (!strcmp(value, "attack")) {
type->MouseAction = MouseActionAttack;
} else if (!strcmp(value, "move")) {
type->MouseAction = MouseActionMove;
} else if (!strcmp(value, "harvest")) {
type->MouseAction = MouseActionHarvest;
} else if (!strcmp(value, "spell-cast")) {
type->MouseAction = MouseActionSpellCast;
} else if (!strcmp(value, "sail")) {
type->MouseAction = MouseActionSail;
} else {
LuaError(l, "Unsupported RightMouseAction: %s" _C_ value);
}
} else if (!strcmp(value, "CanAttack")) {
type->CanAttack = LuaToBoolean(l, -1);
} else if (!strcmp(value, "RepairRange")) {
type->RepairRange = LuaToNumber(l, -1);
} else if (!strcmp(value, "RepairHp")) {
type->RepairHP = LuaToNumber(l, -1);
} else if (!strcmp(value, "RepairCosts")) {
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
const int subargs = lua_rawlen(l, -1);
for (int k = 0; k < subargs; ++k) {
lua_rawgeti(l, -1, k + 1);
const int res = CclGetResourceByName(l);
lua_pop(l, 1);
++k;
type->RepairCosts[res] = LuaToNumber(l, -1, k + 1);
}
} else if (!strcmp(value, "CanTargetLand")) {
if (LuaToBoolean(l, -1)) {
type->CanTarget |= CanTargetLand;
} else {
type->CanTarget &= ~CanTargetLand;
}
} else if (!strcmp(value, "CanTargetSea")) {
if (LuaToBoolean(l, -1)) {
type->CanTarget |= CanTargetSea;
} else {
type->CanTarget &= ~CanTargetSea;
}
} else if (!strcmp(value, "CanTargetAir")) {
if (LuaToBoolean(l, -1)) {
type->CanTarget |= CanTargetAir;
} else {
type->CanTarget &= ~CanTargetAir;
}
} else if (!strcmp(value, "Building")) {
type->Building = LuaToBoolean(l, -1);
} else if (!strcmp(value, "BuildingRules")) {
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
const int subargs = lua_rawlen(l, -1);
// Free any old restrictions if they are redefined
for (std::vector<CBuildRestriction *>::iterator b = type->BuildingRules.begin();
b != type->BuildingRules.end(); ++b) {
delete *b;
}
type->BuildingRules.clear();
for (int k = 0; k < subargs; ++k) {
lua_rawgeti(l, -1, k + 1);
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
ParseBuildingRules(l, type->BuildingRules);
lua_pop(l, 1);
}
} else if (!strcmp(value, "AiBuildingRules")) {
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
const int subargs = lua_rawlen(l, -1);
// Free any old restrictions if they are redefined
for (std::vector<CBuildRestriction *>::iterator b = type->AiBuildingRules.begin();
b != type->AiBuildingRules.end(); ++b) {
delete *b;
}
type->AiBuildingRules.clear();
for (int k = 0; k < subargs; ++k) {
lua_rawgeti(l, -1, k + 1);
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
ParseBuildingRules(l, type->AiBuildingRules);
lua_pop(l, 1);
}
} else if (!strcmp(value, "AutoBuildRate")) {
type->AutoBuildRate = LuaToNumber(l, -1);
} else if (!strcmp(value, "LandUnit")) {
type->LandUnit = LuaToBoolean(l, -1);
} else if (!strcmp(value, "AirUnit")) {
type->AirUnit = LuaToBoolean(l, -1);
} else if (!strcmp(value, "SeaUnit")) {
type->SeaUnit = LuaToBoolean(l, -1);
} else if (!strcmp(value, "RandomMovementProbability")) {
type->RandomMovementProbability = LuaToNumber(l, -1);
} else if (!strcmp(value, "RandomMovementDistance")) {
type->RandomMovementDistance = LuaToNumber(l, -1);
} else if (!strcmp(value, "ClicksToExplode")) {
type->ClicksToExplode = LuaToNumber(l, -1);
} else if (!strcmp(value, "CanTransport")) {
// Warning: CanTransport should only be used AFTER all bool flags
// have been defined.
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
if (type->MaxOnBoard == 0) { // set default value.
type->MaxOnBoard = 1;
}
if (type->BoolFlag.size() < UnitTypeVar.GetNumberBoolFlag()) {
type->BoolFlag.resize(UnitTypeVar.GetNumberBoolFlag());
}
const int subargs = lua_rawlen(l, -1);
for (int k = 0; k < subargs; ++k) {
value = LuaToString(l, -1, k + 1);
++k;
const int index = UnitTypeVar.BoolFlagNameLookup[value];
if (index != -1) {
value = LuaToString(l, -1, k + 1);
type->BoolFlag[index].CanTransport = Ccl2Condition(l, value);
continue;
}
LuaError(l, "Unsupported flag tag for CanTransport: %s" _C_ value);
}
} else if (!strcmp(value, "CanGatherResources")) {
const int args = lua_rawlen(l, -1);
for (int j = 0; j < args; ++j) {
lua_rawgeti(l, -1, j + 1);
ResourceInfo *res = new ResourceInfo;
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
const int subargs = lua_rawlen(l, -1);
for (int k = 0; k < subargs; ++k) {
value = LuaToString(l, -1, k + 1);
++k;
if (!strcmp(value, "resource-id")) {
lua_rawgeti(l, -1, k + 1);
res->ResourceId = CclGetResourceByName(l);
lua_pop(l, 1);
type->ResInfo[res->ResourceId] = res;
} else if (!strcmp(value, "resource-step")) {
res->ResourceStep = LuaToNumber(l, -1, k + 1);
} else if (!strcmp(value, "final-resource")) {
lua_rawgeti(l, -1, k + 1);
res->FinalResource = CclGetResourceByName(l);
lua_pop(l, 1);
} else if (!strcmp(value, "wait-at-resource")) {
res->WaitAtResource = LuaToNumber(l, -1, k + 1);
} else if (!strcmp(value, "wait-at-depot")) {
res->WaitAtDepot = LuaToNumber(l, -1, k + 1);
} else if (!strcmp(value, "resource-capacity")) {
res->ResourceCapacity = LuaToNumber(l, -1, k + 1);
} else if (!strcmp(value, "terrain-harvester")) {
res->TerrainHarvester = 1;
--k;
} else if (!strcmp(value, "lose-resources")) {
res->LoseResources = 1;
--k;
} else if (!strcmp(value, "harvest-from-outside")) {
res->HarvestFromOutside = 1;
--k;
} else if (!strcmp(value, "refinery-harvester")) {
res->RefineryHarvester = 1;
--k;
} else if (!strcmp(value, "file-when-empty")) {
res->FileWhenEmpty = LuaToString(l, -1, k + 1);
} else if (!strcmp(value, "file-when-loaded")) {
res->FileWhenLoaded = LuaToString(l, -1, k + 1);
} else {
printf("\n%s\n", type->Name.c_str());
LuaError(l, "Unsupported tag: %s" _C_ value);
}
}
if (!res->FinalResource) {
res->FinalResource = res->ResourceId;
}
Assert(res->ResourceId);
lua_pop(l, 1);
}
type->BoolFlag[HARVESTER_INDEX].value = 1;
} else if (!strcmp(value, "GivesResource")) {
lua_pushvalue(l, -1);
type->GivesResource = CclGetResourceByName(l);
lua_pop(l, 1);
} else if (!strcmp(value, "CanStore")) {
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
const int subargs = lua_rawlen(l, -1);
for (int k = 0; k < subargs; ++k) {
lua_rawgeti(l, -1, k + 1);
type->CanStore[CclGetResourceByName(l)] = 1;
lua_pop(l, 1);
}
} else if (!strcmp(value, "CanCastSpell")) {
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
//
// Warning: can-cast-spell should only be used AFTER all spells
// have been defined. FIXME: MaxSpellType=500 or something?
//
if (!type->CanCastSpell) {
type->CanCastSpell = new char[SpellTypeTable.size()];
memset(type->CanCastSpell, 0, SpellTypeTable.size() * sizeof(char));
}
const int subargs = lua_rawlen(l, -1);
if (subargs == 0) {
delete[] type->CanCastSpell;
type->CanCastSpell = NULL;
}
for (int k = 0; k < subargs; ++k) {
value = LuaToString(l, -1, k + 1);
const SpellType *spell = SpellTypeByIdent(value);
if (spell == NULL) {
LuaError(l, "Unknown spell type: %s" _C_ value);
}
type->CanCastSpell[spell->Slot] = 1;
}
} else if (!strcmp(value, "AutoCastActive")) {
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
//
// Warning: AutoCastActive should only be used AFTER all spells
// have been defined.
//
if (!type->AutoCastActive) {
type->AutoCastActive = new char[SpellTypeTable.size()];
memset(type->AutoCastActive, 0, SpellTypeTable.size() * sizeof(char));
}
const int subargs = lua_rawlen(l, -1);
if (subargs == 0) {
delete[] type->AutoCastActive;
type->AutoCastActive = NULL;
}
for (int k = 0; k < subargs; ++k) {
value = LuaToString(l, -1, k + 1);
const SpellType *spell = SpellTypeByIdent(value);
if (spell == NULL) {
LuaError(l, "AutoCastActive : Unknown spell type: %s" _C_ value);
}
if (!spell->AutoCast) {
LuaError(l, "AutoCastActive : Define autocast method for %s." _C_ value);
}
type->AutoCastActive[spell->Slot] = 1;
}
} else if (!strcmp(value, "CanTargetFlag")) {
//
// Warning: can-target-flag should only be used AFTER all bool flags
// have been defined.
//
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
if (type->BoolFlag.size() < UnitTypeVar.GetNumberBoolFlag()) {
type->BoolFlag.resize(UnitTypeVar.GetNumberBoolFlag());
}
const int subargs = lua_rawlen(l, -1);
for (int k = 0; k < subargs; ++k) {
value = LuaToString(l, -1, k + 1);
++k;
int index = UnitTypeVar.BoolFlagNameLookup[value];
if (index != -1) {
value = LuaToString(l, -1, k + 1);
type->BoolFlag[index].CanTargetFlag = Ccl2Condition(l, value);
continue;
}
LuaError(l, "Unsupported flag tag for can-target-flag: %s" _C_ value);
}
} else if (!strcmp(value, "PriorityTarget")) {
//
// Warning: ai-priority-target should only be used AFTER all bool flags
// have been defined.
//
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
if (type->BoolFlag.size() < UnitTypeVar.GetNumberBoolFlag()) {
type->BoolFlag.resize(UnitTypeVar.GetNumberBoolFlag());
}
const int subargs = lua_rawlen(l, -1);
for (int k = 0; k < subargs; ++k) {
value = LuaToString(l, -1, k + 1);
++k;
int index = UnitTypeVar.BoolFlagNameLookup[value];
if (index != -1) {
value = LuaToString(l, -1, k + 1);
type->BoolFlag[index].AiPriorityTarget = Ccl2Condition(l, value);
continue;
}
LuaError(l, "Unsupported flag tag for ai-priority-target: %s" _C_ value);
}
} else if (!strcmp(value, "Sounds")) {
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
const int subargs = lua_rawlen(l, -1);
for (int k = 0; k < subargs; ++k) {
value = LuaToString(l, -1, k + 1);
++k;
if (!strcmp(value, "selected")) {
type->Sound.Selected.Name = LuaToString(l, -1, k + 1);
} else if (!strcmp(value, "acknowledge")) {
type->Sound.Acknowledgement.Name = LuaToString(l, -1, k + 1);
} else if (!strcmp(value, "attack")) {
type->Sound.Attack.Name = LuaToString(l, -1, k + 1);
} else if (!strcmp(value, "build")) {
type->Sound.Build.Name = LuaToString(l, -1, k + 1);
} else if (!strcmp(value, "ready")) {
type->Sound.Ready.Name = LuaToString(l, -1, k + 1);
} else if (!strcmp(value, "repair")) {
type->Sound.Repair.Name = LuaToString(l, -1, k + 1);
} else if (!strcmp(value, "harvest")) {
const std::string name = LuaToString(l, -1, k + 1);
++k;
const int resId = GetResourceIdByName(l, name.c_str());
type->Sound.Harvest[resId].Name = LuaToString(l, -1, k + 1);
} else if (!strcmp(value, "help")) {
type->Sound.Help.Name = LuaToString(l, -1, k + 1);
} else if (!strcmp(value, "work-complete")) {
type->Sound.WorkComplete.Name = LuaToString(l, -1, k + 1);
} else if (!strcmp(value, "dead")) {
int death;
const std::string name = LuaToString(l, -1, k + 1);
for (death = 0; death < ANIMATIONS_DEATHTYPES; ++death) {
if (name == ExtraDeathTypes[death]) {
++k;
break;
}
}
if (death == ANIMATIONS_DEATHTYPES) {
type->Sound.Dead[ANIMATIONS_DEATHTYPES].Name = name;
} else {
type->Sound.Dead[death].Name = LuaToString(l, -1, k + 1);
}
} else {
LuaError(l, "Unsupported sound tag: %s" _C_ value);
}
}
} else {
int index = UnitTypeVar.VariableNameLookup[value];
if (index != -1) { // valid index
if (lua_isboolean(l, -1)) {
type->DefaultStat.Variables[index].Enable = LuaToBoolean(l, -1);
} else if (lua_istable(l, -1)) {
DefineVariableField(l, type->DefaultStat.Variables + index, -1);
} else if (lua_isnumber(l, -1)) {
type->DefaultStat.Variables[index].Enable = 1;
type->DefaultStat.Variables[index].Value = LuaToNumber(l, -1);
type->DefaultStat.Variables[index].Max = LuaToNumber(l, -1);
} else { // Error
LuaError(l, "incorrect argument for the variable in unittype");
}
continue;
}
if (type->BoolFlag.size() < UnitTypeVar.GetNumberBoolFlag()) {
type->BoolFlag.resize(UnitTypeVar.GetNumberBoolFlag());
}
index = UnitTypeVar.BoolFlagNameLookup[value];
if (index != -1) {
if (lua_isnumber(l, -1)) {
type->BoolFlag[index].value = LuaToNumber(l, -1);
} else {
type->BoolFlag[index].value = LuaToBoolean(l, -1);
}
} else {
printf("\n%s\n", type->Name.c_str());
LuaError(l, "Unsupported tag: %s" _C_ value);
}
}
}
// If number of directions is not specified, make a guess
// Building have 1 direction and units 8
if (type->Building && type->NumDirections == 0) {
type->NumDirections = 1;
} else if (type->NumDirections == 0) {
type->NumDirections = 8;
}
// FIXME: try to simplify/combine the flags instead
if (type->MouseAction == MouseActionAttack && !type->CanAttack) {
LuaError(l, "Unit-type '%s': right-attack is set, but can-attack is not\n" _C_ type->Name.c_str());
}
UpdateDefaultBoolFlags(*type);
if (!CclInConfigFile) {
if (redefine & redefineSprite) {
LoadUnitTypeSprite(*type);
}
UpdateUnitStats(*type, 1);
}
return 0;
}
Description
Copy a unit type.
@param l Lua state.
Example:
CopyUnitType("unit-peasant", "unit-peasant-copy")
static int CclCopyUnitType(lua_State *l)
{
LuaCheckArgs(l, 2);
// Slot identifier
const char* fromName = LuaToString(l, 1);
CUnitType *from = UnitTypeByIdent(fromName);
const char* toName = LuaToString(l, 2);
CUnitType *to = UnitTypeByIdent(toName);
if (to) {
DebugPrint("Redefining unit-type '%s'\n" _C_ toName);
} else {
to = NewUnitTypeSlot(toName);
}
if (!from) {
LuaError(l, "Unknown unit-type '%s'\n" _C_ fromName);
}
to->Flip = from->Flip;
to->Name = toName;
to->File = from->File;
to->AltFile = from->AltFile;
to->Width = from->Width;
to->Height = from->Height;
if (to->Sprite) {
CGraphic::Free(to->Sprite);
to->Sprite = NULL;
}
if (to->AltSprite) {
CGraphic::Free(to->AltSprite);
to->AltSprite = NULL;
}
to->ShadowFile = from->ShadowFile;
to->ShadowWidth = from->ShadowWidth;
to->ShadowHeight = from->ShadowHeight;
to->ShadowOffsetX = from->ShadowOffsetX;
to->ShadowOffsetY = from->ShadowOffsetY;
to->ShadowSpriteFrame = from->ShadowSpriteFrame;
to->ShadowScale = from->ShadowScale;
if (to->ShadowSprite) {
CGraphic::Free(to->ShadowSprite);
to->ShadowSprite = NULL;
}
to->OffsetX = from->OffsetX;
to->OffsetY = from->OffsetY;
to->Animations = from->Animations;
to->Icon.Name = from->Icon.Name;
to->Icon.Icon = NULL;
#ifdef USE_MNG
to->Portrait.Num = from->Portrait.Num;
to->Portrait.Talking = from->Portrait.Talking;
to->Portrait.Files = new std::string[to->Portrait.Num];
for (int i = 0; i < to->Portrait.Num; i++) {
to->Portrait.Files[i] = from->Portrait.Files[i];
}
to->Portrait.Mngs = new Mng *[to->Portrait.Num];
memset(to->Portrait.Mngs, 0, to->Portrait.Num * sizeof(Mng *));
#endif
memcpy(to->DefaultStat.Costs, from->DefaultStat.Costs, sizeof(from->DefaultStat.Costs));
memcpy(to->DefaultStat.Storing, from->DefaultStat.Storing, sizeof(from->DefaultStat.Storing));
memcpy(to->DefaultStat.ImproveIncomes, from->DefaultStat.ImproveIncomes, sizeof(from->DefaultStat.ImproveIncomes));
to->Construction = from->Construction;
to->DrawLevel = from->DrawLevel;
to->MaxOnBoard = from->MaxOnBoard;
to->BoardSize = from->BoardSize;
to->ButtonLevelForTransporter = from->ButtonLevelForTransporter;
to->StartingResources = from->StartingResources;
to->DefaultStat.Variables[HP_INDEX].Increase = from->DefaultStat.Variables[HP_INDEX].Increase;
to->DefaultStat.Variables[HP_INDEX].IncreaseFrequency = from->DefaultStat.Variables[HP_INDEX].IncreaseFrequency;
to->BurnPercent = from->BurnPercent;
to->BurnDamageRate = from->BurnDamageRate;
to->PoisonDrain = from->PoisonDrain;
to->DefaultStat.Variables[SHIELD_INDEX].Max = from->DefaultStat.Variables[SHIELD_INDEX].Max;
to->DefaultStat.Variables[SHIELD_INDEX].Value = from->DefaultStat.Variables[SHIELD_INDEX].Value;
to->DefaultStat.Variables[SHIELD_INDEX].Increase = from->DefaultStat.Variables[SHIELD_INDEX].Increase;
to->DefaultStat.Variables[SHIELD_INDEX].Enable = from->DefaultStat.Variables[SHIELD_INDEX].Enable;
to->TileWidth = from->TileWidth;
to->TileHeight = from->TileHeight;
to->NeutralMinimapColorRGB = from->NeutralMinimapColorRGB;
to->Neutral = from->Neutral;
to->BoxWidth = from->BoxWidth;
to->BoxHeight = from->BoxHeight;
to->BoxOffsetX = from->BoxOffsetX;
to->BoxOffsetY = from->BoxOffsetY;
to->NumDirections = from->NumDirections;
to->ReactRangeComputer = from->ReactRangeComputer;
to->ReactRangePerson = from->ReactRangePerson;
to->Missile.Name = from->Missile.Name;
to->Missile.Missile = NULL; // filled in later
to->MinAttackRange = from->MinAttackRange;
to->DefaultStat.Variables[ATTACKRANGE_INDEX].Value = from->DefaultStat.Variables[ATTACKRANGE_INDEX].Value;
to->DefaultStat.Variables[ATTACKRANGE_INDEX].Max = from->DefaultStat.Variables[ATTACKRANGE_INDEX].Max;
to->DefaultStat.Variables[ATTACKRANGE_INDEX].Enable = from->DefaultStat.Variables[ATTACKRANGE_INDEX].Enable;
to->DefaultStat.Variables[MAXHARVESTERS_INDEX].Value = from->DefaultStat.Variables[MAXHARVESTERS_INDEX].Value;
to->DefaultStat.Variables[MAXHARVESTERS_INDEX].Max = from->DefaultStat.Variables[MAXHARVESTERS_INDEX].Max;
to->DefaultStat.Variables[PRIORITY_INDEX].Value = from->DefaultStat.Variables[PRIORITY_INDEX].Value;
to->DefaultStat.Variables[PRIORITY_INDEX].Max = from->DefaultStat.Variables[PRIORITY_INDEX].Max;
to->AnnoyComputerFactor = from->AnnoyComputerFactor;
to->AiAdjacentRange = from->AiAdjacentRange;
to->DecayRate = from->DecayRate;
to->CorpseName = from->CorpseName;
to->CorpseType = from->CorpseType;
to->DamageType = from->DamageType;
to->ExplodeWhenKilled = from->ExplodeWhenKilled;
to->Explosion.Name = from->Explosion.Name;
to->Explosion.Missile = NULL; // filled later
to->TeleportCost = from->TeleportCost;
to->TeleportEffectIn = from->TeleportEffectIn;
to->TeleportEffectOut = from->TeleportEffectOut;
to->OnDeath = from->OnDeath;
to->OnHit = from->OnHit;
to->OnEachCycle = from->OnEachCycle;
to->OnEachSecond = from->OnEachSecond;
to->OnInit = from->OnInit;
to->OnReady = from->OnReady;
to->UnitType = from->UnitType;
for (int k = 0; k < MaxAttackPos; ++k) {
for (int m = 0; m < UnitSides; ++m) {
to->MissileOffsets[m][k].x = from->MissileOffsets[m][k].x;
to->MissileOffsets[m][k].y = from->MissileOffsets[m][k].y;
}
}
for (int i = 0; i < ANIMATIONS_DEATHTYPES + 2; i++) {
to->Impact[i].Name = from->Impact[i].Name;
to->Impact[i].Missile = from->Impact[i].Missile;
}
to->MouseAction = from->MouseAction;
to->CanAttack = from->CanAttack;
to->RepairRange = from->RepairRange;
to->RepairHP = from->RepairHP;
memcpy(to->RepairCosts, from->RepairCosts, sizeof(from->RepairCosts));
to->CanTarget = from->CanTarget;
to->Building = from->Building;
to->BuildingRules.clear();
if (!from->BuildingRules.empty()) {
printf("WARNING: unit type copy %s of %s does not inherit BuildingRules\n", fromName, toName);
}
// XXX: should copy, not share, this will crash
// for (auto rule : from->BuildingRules) {
// to->BuildingRules.push_back(rule);
// }
to->AiBuildingRules.clear();
if (!from->AiBuildingRules.empty()) {
printf("WARNING: unit type copy %s of %s does not inherit AiBuildingRules\n", fromName, toName);
}
// XXX: should copy, not share, this would crash
// for (auto rule : from->AiBuildingRules) {
// to->AiBuildingRules.push_back(rule);
// }
to->AutoBuildRate = from->AutoBuildRate;
to->LandUnit = from->LandUnit;
to->AirUnit = from->AirUnit;
to->SeaUnit = from->SeaUnit;
to->RandomMovementProbability = from->RandomMovementProbability;
to->RandomMovementDistance = from->RandomMovementDistance;
to->ClicksToExplode = from->ClicksToExplode;
to->MaxOnBoard = from->MaxOnBoard;
for (unsigned int i = 0; i < from->BoolFlag.size(); i++) {
to->BoolFlag[i].value = from->BoolFlag[i].value;
to->BoolFlag[i].CanTransport = from->BoolFlag[i].CanTransport;
to->BoolFlag[i].CanTargetFlag = from->BoolFlag[i].CanTargetFlag;
to->BoolFlag[i].AiPriorityTarget = from->BoolFlag[i].AiPriorityTarget;
}
memcpy(to->ResInfo, from->ResInfo, sizeof(from->ResInfo));
to->GivesResource = from->GivesResource;
memcpy(to->CanStore, from->CanStore, sizeof(from->CanStore));
to->CanCastSpell = from->CanCastSpell;
to->AutoCastActive = from->AutoCastActive;
to->Sound.Selected.Name = from->Sound.Selected.Name;
to->Sound.Acknowledgement.Name = from->Sound.Acknowledgement.Name;
to->Sound.Attack.Name = from->Sound.Attack.Name;
to->Sound.Build.Name = from->Sound.Build.Name;
to->Sound.Ready.Name = from->Sound.Ready.Name;
to->Sound.Repair.Name = from->Sound.Repair.Name;
for (int i = 0; i < MaxCosts; i++) {
to->Sound.Harvest[i].Name = from->Sound.Harvest[i].Name;
}
to->Sound.Help.Name = from->Sound.Help.Name;
to->Sound.WorkComplete.Name = from->Sound.WorkComplete.Name;
for (unsigned int i = 0; i < ANIMATIONS_DEATHTYPES + 1; i++) {
to->Sound.Dead[i].Name = from->Sound.Dead[i].Name;
}
for (unsigned int i = 0; i < UnitTypeVar.GetNumberVariable(); i++) {
to->DefaultStat.Variables[i].Enable = from->DefaultStat.Variables[i].Enable;
to->DefaultStat.Variables[i].Value = from->DefaultStat.Variables[i].Value;
to->DefaultStat.Variables[i].Max = from->DefaultStat.Variables[i].Max;
to->DefaultStat.Variables[i].Increase = from->DefaultStat.Variables[i].Increase;
to->DefaultStat.Variables[i].IncreaseFrequency = from->DefaultStat.Variables[i].IncreaseFrequency;
}
UpdateDefaultBoolFlags(*to);
if (!CclInConfigFile) {
UpdateUnitStats(*to, 1);
}
LoadUnitTypes();
return 0;
}
Description
Parse unit-stats.
@param l Lua state.
Example:
DefineUnitStats("unit-berserker", 2, {
"HitPoints", {Value = 55, Max = 55, Increase = 0, Enable = true},
"AttackRange", {Value = 5, Max = 6, Increase = 0, Enable = true},
"SightRange", {Value = 7, Max = 7, Increase = 0, Enable = true},
})
static int CclDefineUnitStats(lua_State *l)
{
CUnitType *type = UnitTypeByIdent(LuaToString(l, 1));
const int playerId = LuaToNumber(l, 2);
Assert(type);
Assert(playerId < PlayerMax);
CUnitStats *stats = &type->Stats[playerId];
if (!stats->Variables) {
stats->Variables = new CVariable[UnitTypeVar.GetNumberVariable()];
}
// Parse the list: (still everything could be changed!)
const int args = lua_rawlen(l, 3);
for (int j = 0; j < args; ++j) {
const char *value = LuaToString(l, 3, j + 1);
++j;
if (!strcmp(value, "costs")) {
lua_rawgeti(l, 3, j + 1);
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
const int subargs = lua_rawlen(l, -1);
for (int k = 0; k < subargs; ++k) {
lua_rawgeti(l, 3, j + 1);
value = LuaToString(l, -1, k + 1);
++k;
const int resId = GetResourceIdByName(l, value);
stats->Costs[resId] = LuaToNumber(l, -1, k + 1);
lua_pop(l, 1);
}
} else if (!strcmp(value, "storing")) {
lua_rawgeti(l, 3, j + 1);
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
const int subargs = lua_rawlen(l, -1);
for (int k = 0; k < subargs; ++k) {
lua_rawgeti(l, 3, j + 1);
value = LuaToString(l, -1, k + 1);
++k;
const int resId = GetResourceIdByName(l, value);
stats->Storing[resId] = LuaToNumber(l, -1, k + 1);
lua_pop(l, 1);
}
} else if (!strcmp(value, "improve-production")) {
lua_rawgeti(l, 3, j + 1);
if (!lua_istable(l, -1)) {
LuaError(l, "incorrect argument");
}
const int subargs = lua_rawlen(l, -1);
for (int k = 0; k < subargs; ++k) {
lua_rawgeti(l, 3, j + 1);
value = LuaToString(l, -1, k + 1);
++k;
const int resId = GetResourceIdByName(l, value);
stats->ImproveIncomes[resId] = LuaToNumber(l, -1, k + 1);
lua_pop(l, 1);
}
} else {
int i = UnitTypeVar.VariableNameLookup[value];// User variables
if (i != -1) { // valid index
lua_rawgeti(l, 3, j + 1);
if (lua_istable(l, -1)) {
DefineVariableField(l, stats->Variables + i, -1);
} else if (lua_isnumber(l, -1)) {
stats->Variables[i].Enable = 1;
stats->Variables[i].Value = LuaToNumber(l, -1);
stats->Variables[i].Max = LuaToNumber(l, -1);
} else { // Error
LuaError(l, "incorrect argument for the variable in unittype");
}
continue;
}
// This leaves a half initialized unit
LuaError(l, "Unsupported tag: %s" _C_ value);
}
}
return 0;
}
Define boolean flag.
@param l Lua state.
static int CclDefineBoolFlags(lua_State *l)
{
const unsigned int old = UnitTypeVar.GetNumberBoolFlag();
const int args = lua_gettop(l);
for (int j = 0; j < args; ++j) {
const char *str = LuaToString(l, j + 1);
UnitTypeVar.BoolFlagNameLookup.AddKey(str);
}
if (0 < old && old != UnitTypeVar.GetNumberBoolFlag()) {
size_t new_size = UnitTypeVar.GetNumberBoolFlag();
for (std::vector<CUnitType *>::size_type i = 0; i < UnitTypes.size(); ++i) { // adjust array for unit already defined
UnitTypes[i]->BoolFlag.resize(new_size);
}
}
return 0;
}
Define user variables.
@param l Lua state.
static int CclDefineVariables(lua_State *l)
{
int old = UnitTypeVar.GetNumberVariable();
const int args = lua_gettop(l);
for (int j = 0; j < args; ++j) {
const char *str = LuaToString(l, j + 1);
const int index = UnitTypeVar.VariableNameLookup.AddKey(str);
if (index == old) {
old++;
UnitTypeVar.Variable.resize(old);
} else {
DebugPrint("Warning, User Variable \"%s\" redefined\n" _C_ str);
}
if (!lua_istable(l, j + 2)) { // No change => default value.
continue;
}
++j;
DefineVariableField(l, &(UnitTypeVar.Variable[index]), j + 1);
}
return 0;
}
Define Decorations for user variables
@param l Lua state.
@todo modify Assert with luastate with User Error.
@todo continue to add configuration.
static int CclDefineDecorations(lua_State *l)
{
struct {
int Index;
int OffsetX;
int OffsetY;
int OffsetXPercent;
int OffsetYPercent;
bool IsCenteredInX;
bool IsCenteredInY;
bool ShowIfNotEnable;
bool ShowWhenNull;
bool HideHalf;
bool ShowWhenMax;
bool ShowOnlySelected;
bool HideNeutral;
bool HideAllied;
bool ShowOpponent;
bool BoolFlagInvert;
int BoolFlag;
} tmp;
const int nargs = lua_gettop(l);
for (int i = 0; i < nargs; i++) {
Assert(lua_istable(l, i + 1));
CDecoVar *decovar = NULL;
memset(&tmp, 0, sizeof(tmp));
lua_pushnil(l);
while (lua_next(l, i + 1)) {
const char *key = LuaToString(l, -2);
if (!strcmp(key, "Index")) {
const char *const value = LuaToString(l, -1);
tmp.Index = UnitTypeVar.VariableNameLookup[value];