if FirstLoad then PrgEditorIds = false end PrgExportData = false PrgSelected = false function OnMsg.GedOpened(ged_id) local ged = GedConnections[ged_id] if ged and ged.app_template == "PrgEditor" then PrgEditorIds = PrgEditorIds or {} table.insert(PrgEditorIds, ged_id) end end function OnMsg.GedClosing(ged_id) if PrgEditorIds and table.remove_entry(PrgEditorIds, ged_id) and #PrgEditorIds == 0 then PrgEditorIds = false PrgExportData = false PrgSelected = false end end --- TO DO: need to make PrgExportData and PrgSelected to work if both editors are open.. function OnMsg.GedPropertyEdited(ged_id, obj, prop_id, old_value) local ged = GedConnections[ged_id] if ged and (ged.app_template == "PrgEditor" or ged.app_template == "UnitAIEditor") and PrgExportData then PrgExportData[ged.bound_objects.SelectedPrg] = nil end end function OnMsg.ObjModified(obj) if IsKindOf(obj, "XPrg") then obj:GenCode() end end function OnMsg.GedOnEditorSelect(selection, is_selected, ged) if ged and (ged.app_template == "PrgEditor" or ged.app_template == "UnitAIEditor") then if is_selected and IsKindOf(selection, "XPrg") then PrgSelected = selection end if not PrgExportData or not PrgExportData[ged.bound_objects.SelectedPrg] then return end if IsKindOf(selection, "XPrgCommand") then PrgExportData[ged.bound_objects.SelectedPrg].selected_item = is_selected and selection or nil end if is_selected then ObjModified(ged:ResolveObj("SelectedPrg")) end end end ----- function GedFormatXPrgError(obj) if not IsKindOf(obj, "XPrg") then return end return obj:GetPrgData().error_line or false end function GedFormatXPrgCodeSelection(obj) if not IsKindOf(obj, "XPrg") then return end return obj:GetSelectedCommandLines() end function PrgEditorBuildMenuCommands(editor, cmd_class) local list = {} local classes = g_Classes for _, classname in ipairs(ClassDescendantsList(cmd_class)) do local class = classes[classname] local menubar = class.Menubar if menubar then local bars = list[menubar] if not bars then bars = {} list[menubar] = bars end local sec = bars[class.MenubarSection] if not sec then sec = {} bars[class.MenubarSection] = sec end sec[#sec + 1] = classname end end for menubar, sections in pairs(list) do local add_sep for section, commands in sorted_pairs(sections) do if add_sep then XAction:new({ ActionMenubar = menubar, ActionName = Untranslated("-----"), }, editor) end add_sep = true for i = 1, #commands do local classname = commands[i] local class = classes[classname] local action = class.ActionName or "XPrg" == string.sub(classname, 1, #"XPrg") and string.sub(classname, #"XPrg" + 1) or classname XAction:new({ ActionId = "New" .. classname, ActionMenubar = menubar, ActionName = Untranslated(action), OnAction = function() local panel = editor.idCommands editor:Op("GedOpTreeNewItem", panel.context, panel:GetSelection(), classname) end, }, editor) end end end end function GedToggleDebugWaypoints(socket, waypoints_toggled) LocalStorage.DebugWaypoints = waypoints_toggled SaveLocalStorage() ReloadLua() end function validate_var(obj, value) if type(value) ~= "string" or not (value == "" or value:match("^%a[%w_]*$")) then return "var must be a valid identifier" end end -------------- Global Prg functions ------------------ function PrgNewVar(name, scope, prgdata) assert(name) local idx = table.find(scope, "name", name) if idx then return scope[idx] end local var = { name = name } scope[#scope + 1] = var prgdata.used_vars[name] = true return var end function PrgGetFreeVarName(prgdata, base_name) local name = base_name local k = 1 while prgdata.used_vars[name] do k = k + 1 name = string.format('%s%d', base_name, k) end return name end function PrgGetScopeVarNames(scope) local names = {} for i = 1, #scope do names[i] = scope[i].name end return names end function PrgAddExecLine(prgdata, level, text) table.insert(prgdata.exec, string.rep("\t", level) .. text) end function PrgAddExternalLine(prgdata, level, text) table.insert(prgdata.external, string.rep("\t", level) .. text) end function PrgAddDtorLine(prgdata, level, text) table.insert(prgdata.dtor, string.rep("\t", level) .. text) end function PrgInsertLine(list, idx, level, text) table.insert(list, idx, string.rep("\t", level) .. text) end function PrgSplitStr(str, pattern, format) local res = {} local i = 1 while true do local istart, iend = string.find(str, pattern, i, true) local value = str:sub(i, (istart or 0) - 1):trim_spaces() if value ~= "" then res[#res + 1] = format and string.format(format, value) or value end if not istart then break end i = iend + 1 end return res end --------------------------------------- DefineClass.XPrg = { __parents = { "Preset" }, properties = { { category = "Params", id = "param1", name = "Param 1", editor = "text", default = "", }, { category = "Params", id = "param2", name = "Param 2", editor = "text", default = "", }, { category = "Params", id = "param3", name = "Param 3", editor = "text", default = "", }, { category = "Params", id = "param4", name = "Param 4", editor = "text", default = "", }, { category = "Params", id = "param5", name = "Param 5", editor = "text", default = "", }, { category = "Params", id = "param6", name = "Param 6", editor = "text", default = "", }, { category = "Params", id = "param7", name = "Param 7", editor = "text", default = "", }, { category = "Params", id = "param8", name = "Param 8", editor = "text", default = "", }, }, ParamsCount = 8, SingleFile = false, GedEditor = false, PrgGlobalMap = false, ContainerClass = "XPrgCommand", } function XPrg:GetSavePrgData() local class = self.PresetClass or self.class local code = pstr(exported_files_header_warning, 16384) if self.SingleFile then ForEachPresetExtended(class, save_prg_lua, code) else --save prg local prgdata = self:GenCode() if prgdata.error then code:append(prgdata.fallback) else code:append(prgdata.lua_code) end if self.SingleFile then code:append("\n\n") end end return code end function XPrg:GetLuaSavePath(savepath) local relpath = string.match(savepath, "Data/(.*)$") if relpath then return string.format("Lua/%s", relpath) end local save_in, relpath = string.match(savepath, "^(.*)/Presets/(.*)$") if save_in then return string.format("%s/Code/%s", save_in, relpath) end end function XPrg:OnPreSave() local savepath = self:GetSavePath() local lua_savepath = self:GetLuaSavePath(savepath) local last_lua_savepath local last_save_path = g_PresetLastSavePaths[self] if last_save_path and last_save_path ~= savepath then last_lua_savepath = self:GetLuaSavePath(last_save_path) end if last_lua_savepath and last_lua_savepath ~= lua_savepath then if self.LocalPreset then AsyncFileDelete(last_lua_savepath) else SVNMoveFile(last_lua_savepath, lua_savepath) end end PrgExportData = PrgExportData or setmetatable({}, weak_keys_meta) local prgdata = self:GenCode() if not prgdata.error then local lua_export = self:GetSavePrgData() local err = SaveSVNFile(lua_savepath, lua_export, self.LocalPreset) if err then printf("Error '%s' saving %s", tostring(err), lua_savepath) end end end function XPrg:GetPrgData() PrgExportData = PrgExportData or setmetatable({}, weak_keys_meta) local prgdata = PrgExportData[self] or self:GenCode() return prgdata end function XPrg:GetCode(param1, param2) local prgdata = self:GetPrgData() return prgdata.text or self.id end function XPrg:GetError() return self:GetPrgData().error end function GenBlockCode(parent, prgdata, level, add_scope_vars) local start_custom_dtors = prgdata.custom_dtors local block_start_exec = #prgdata.exec + 1 local parent_data = prgdata[parent] parent_data.children_scope = parent_data.children_scope or { parent = parent_data.scope } local scope = parent_data.children_scope if not scope then scope = { parent = parent_data.scope } parent_data.children_scope = scope end local child_start_line = block_start_exec -- we will insert some things up later if #scope > 0 and add_scope_vars ~= false then child_start_line = child_start_line + 1 end for i = 1, #parent do local action = parent[i] local exec_lines_start = #prgdata.exec + 1 -- to count new lines prgdata[action] = { parent = parent, level = level + 1, scope = scope, start_line = child_start_line } if action.comment ~= "" then PrgAddExecLine(prgdata, level, "-- " .. action.comment) end action:GenCode(prgdata, level) local exec_lines_end = #prgdata.exec if exec_lines_end < exec_lines_start then -- variables defined outside of scope prgdata[action].end_line = 0 prgdata[action].start_line = 0 else prgdata[action].end_line = child_start_line + (exec_lines_end - exec_lines_start) -- start_line + exec lines generated by the action end child_start_line = prgdata[action].end_line + 1 end if prgdata.custom_dtors > start_custom_dtors then local unit = prgdata.params[1] and prgdata.params[1].name assert(unit) while unit and prgdata.custom_dtors > start_custom_dtors do PrgAddExecLine(prgdata, level, string.format('%s:PopAndCallDestructor()', unit)) prgdata.custom_dtors = prgdata.custom_dtors - 1 end end if #scope > 0 and add_scope_vars ~= false then PrgInsertLine(prgdata.exec, block_start_exec, level, "local " .. table.concat(PrgGetScopeVarNames(scope), ", ")) end if not prgdata[parent].start_line then prgdata[parent].start_line = block_start_exec end if not prgdata[parent].end_line then prgdata[parent].end_line = #prgdata.exec end end function XPrg:GenCode() local prgdata = {} prgdata.PrgGlobalMap = self.PrgGlobalMap prgdata.class = self.class prgdata.id = self.id prgdata.used_vars = {} prgdata.exec = {} prgdata.dtor = {} prgdata.custom_dtors = 0 prgdata.external = {} prgdata.external_vars = { parent = false } prgdata.upvalues = { parent = prgdata.external_vars } prgdata.params = { parent = prgdata.upvalues } prgdata.exec_scope = { parent = prgdata.params } prgdata.def_locals = { parents = prgdata.exec_scope } prgdata[self] = { parent = false, level = 1, scope = prgdata.params, children_scope = prgdata.exec_scope } local code_line_offset = 0 local offset_increase = function(offset_size) code_line_offset = code_line_offset + (offset_size or 1) end local params_txt, params_txt_long for i = 1, self.ParamsCount do local param = string.match(self["param" .. i], "[^%s]+") if param then PrgNewVar(param, prgdata.params, prgdata) params_txt_long = params_txt_long and params_txt_long .. ", " .. param or param params_txt = params_txt_long else params_txt_long = params_txt_long and params_txt_long .. ", _" or "_" end end params_txt = params_txt or "" GenBlockCode(self, prgdata, 1, false) local unit = prgdata.params[1] and prgdata.params[1].name local visit_restart_str = #prgdata.dtor == 0 and string.format('if %s.visit_restart then return end', unit) or string.format('if %s.visit_restart then %s:PopAndCallDestructor() return end', unit, unit) local list = prgdata.exec for i = 2, #list do if list[i] == "VISIT_RESTART" then list[i] = (string.match(list[i-1], "^[%s]+") or "") .. visit_restart_str end end if #prgdata.dtor > 0 then PrgInsertLine(prgdata.exec, 1, 1, string.format('%s:PushDestructor(function(%s)', unit, unit)) for i = 1, #prgdata.dtor do PrgInsertLine(prgdata.exec, i + 1, 0, prgdata.dtor[i]) end PrgInsertLine(prgdata.exec, #prgdata.dtor + 2, 1, "end)") PrgInsertLine(prgdata.exec, #prgdata.dtor + 3, 0, "") PrgAddExecLine(prgdata, 0, "") PrgAddExecLine(prgdata, 1, string.format('%s:PopAndCallDestructor()', unit)) offset_increase(#prgdata.dtor + 3) end if #prgdata.exec_scope > 0 then PrgInsertLine(prgdata.exec, 1, 1, "local " .. table.concat(PrgGetScopeVarNames(prgdata.exec_scope), ", ")) if #prgdata.dtor > 0 then PrgInsertLine(prgdata.exec, 2, 0, "") offset_increase() end offset_increase() end -- the begining of the execution program PrgInsertLine(prgdata.exec, 1, 0, string.format('%s["%s"] = function(%s)', self.PrgGlobalMap, self.id, params_txt)) offset_increase() if #prgdata.upvalues > 0 then PrgInsertLine(prgdata.exec, 2, 1, string.format('local %s', table.concat(PrgGetScopeVarNames(prgdata.upvalues), ", "))) PrgInsertLine(prgdata.exec, 3, 0, "") offset_increase() end if #prgdata.def_locals > 0 then local vnames, vvals = {}, {} for _, var in ipairs(prgdata.def_locals) do if not var.inline then table.insert(vvals, var.value) table.insert(vnames, var.name) end end PrgInsertLine(prgdata.exec, 2, 1, "local " .. table.concat(vnames, ", ") .." = ".. table.concat(vvals, ", ")) offset_increase() end PrgAddExecLine(prgdata, 0, "end") PrgAddExecLine(prgdata, 0, "") prgdata.lua_code = table.concat(prgdata.exec, "\r\n") local external_vars = prgdata.external_vars if #external_vars > 0 then for i = 1, #external_vars do local var = external_vars[i] if var.value then local cnt = 0 local lua_code_value = TableToLuaCode(var.value, "") for v in string.gmatch(lua_code_value, "\n") do cnt = cnt + 1 end PrgInsertLine(prgdata.external, 1, 0, string.format('local %s = %s', var.name, lua_code_value)) offset_increase(cnt + 1) -- +1 for local name end end end if #prgdata.external > 0 then PrgAddExternalLine(prgdata, 0, "") prgdata.lua_code = table.concat(prgdata.external, "\r\n") .. prgdata.lua_code end prgdata.text = prgdata.lua_code local func, err = load(prgdata.lua_code, nil, nil, _ENV) if err then prgdata.error = err local line, err_text = err:match('^%[string [^%]]*%]:(%d+):(.*)') --prgdata.text = prgdata.text .. "\r\nErrors:\r\nline: " .. (line or "") .. (err_text or "") prgdata.text = prgdata.text prgdata.fallback = string.format('%s.%s = function() end -- FALLBACK!!!', self.PrgGlobalMap, self.id) prgdata.error_line = line end prgdata.global_code_offset = code_line_offset PrgExportData[self] = prgdata PrgSelected = self return prgdata end function XPrg:GetSelectedCommandLines() local prgdata = PrgExportData[self] if not prgdata then return { 0, 0 } end local selected_item = prgdata.selected_item if selected_item and prgdata[selected_item] then local offset = prgdata.global_code_offset local start_line = prgdata[selected_item].start_line local end_line = prgdata[selected_item].end_line if start_line == 0 and end_line == 0 then start_line = 1 end_line = offset - 2 else start_line = start_line + offset end_line = end_line + offset end return { start_line, end_line } end return { 0, 0 } end -- XPrgCommand DefineClass.XPrgCommand = { __parents = { "Container" }, properties = { { category = "General", id = "comment", name = "Comment", editor = "text", default = "", }, { category = "General", id = "CmdType", name = "Command", editor = "text", default = "", read_only = true, dont_save = true }, }, Menubar = false, MenubarSection = "", ActionName = false, TreeView = T(357198499972, " "), ContainerClass = "XPrgCommand", } function XPrgCommand:GetCmdType() return string.starts_with(self.class, "XPrg", true) and string.sub(self.class, 5) or self.class end function XPrgCommand:GenCode(prgdata, level) end function XPrgCommand:GenCodeCommandCallPrg(prgdata, level, name, unit, ...) local params_txt local params = { ... } for i = table.maxn(params), 1, -1 do local param = params[i] and string.match(params[i], "[^%s]+") if param then params_txt = params_txt and param .. ", " .. params_txt or param elseif params_txt then params_txt = "nil, " .. params_txt end end local prg = string.format('%s[%s]', prgdata.PrgGlobalMap, name) params_txt = params_txt and ", " .. params_txt or "" PrgAddExecLine(prgdata, level, string.format('%s(%s%s)', prg, unit, params_txt)) end function XPrgCommand:GenCodeCallPrg(prgdata, level, name, ...) local params_txt local params = { ... } for i = #params, 1, -1 do local param = string.match(params[i], "[^%s]+") if param then params_txt = params_txt and param .. ", " .. params_txt or param elseif params_txt then params_txt = "nil, " .. params_txt end end params_txt = params_txt or "" local prg = "_prg" PrgNewVar(prg, prgdata.exec_scope, prgdata) PrgAddExecLine(prgdata, level, string.format('%s = %s[%s]', prg, prgdata.PrgGlobalMap, name)) PrgAddExecLine(prgdata, level, string.format('if %s then', prg)) PrgAddExecLine(prgdata, level + 1, string.format('%s(%s)', prg, params_txt)) PrgAddExecLine(prgdata, level, string.format('end')) end function XPrgCommand:GenCodeSelectSlot(prgdata, level, eval, group, attach_var, bld, unit, var_spot, var_obj, var_pos, var_slot_desc, var_slot, var_slotname) local slots_var_name = "_slots" if attach_var == "" then attach_var = nil end local spot_obj_desc_resolved if eval == "Random" then spot_obj_desc_resolved = string.format('PrgGetObjRandomSpotFromGroup(%s, %s, "%s", %s, %s)', bld, attach_var, group, slots_var_name, unit, var_pos) elseif eval == "Nearest" then spot_obj_desc_resolved = string.format('PrgGetObjNearestSpotFromGroup(%s, %s, "%s", %s, %s)', bld, attach_var, group, slots_var_name, unit, var_pos) end if not spot_obj_desc_resolved then return end var_pos = var_pos ~= "" and var_pos or nil var_slotname = var_slotname ~= "" and var_slotname var_slot = var_slot ~= "" and var_slot or var_slotname and "_slot" var_slot_desc = var_slot_desc ~= "" and var_slot_desc or (var_slot or var_slotname) and "_slot_data" var_obj = var_obj ~= "" and var_obj or (var_pos or var_slot_desc or var_slot or var_slotname) and "_obj" var_spot = var_spot ~= "" and var_spot or (var_obj or var_pos or var_slot_desc) and "_spot" if var_slotname then PrgNewVar(var_spot, prgdata.exec_scope, prgdata) PrgNewVar(var_obj, prgdata.exec_scope, prgdata) PrgNewVar(var_slot_desc, prgdata.exec_scope, prgdata) PrgNewVar(var_slot, prgdata.exec_scope, prgdata) PrgNewVar(var_slotname, prgdata.exec_scope, prgdata) PrgAddExecLine(prgdata, level, string.format('%s, %s, %s, %s, %s = %s', var_spot, var_obj, var_slot_desc, var_slot, var_slotname, spot_obj_desc_resolved)) elseif var_slot then PrgNewVar(var_spot, prgdata.exec_scope, prgdata) PrgNewVar(var_obj, prgdata.exec_scope, prgdata) PrgNewVar(var_slot_desc, prgdata.exec_scope, prgdata) PrgNewVar(var_slot, prgdata.exec_scope, prgdata) PrgAddExecLine(prgdata, level, string.format('%s, %s, %s, %s = %s', var_spot, var_obj, var_slot_desc, var_slot, spot_obj_desc_resolved)) elseif var_slot_desc then PrgNewVar(var_spot, prgdata.exec_scope, prgdata) PrgNewVar(var_obj, prgdata.exec_scope, prgdata) PrgNewVar(var_slot_desc, prgdata.exec_scope, prgdata) PrgAddExecLine(prgdata, level, string.format('%s, %s, %s = %s', var_spot, var_obj, var_slot_desc, spot_obj_desc_resolved)) elseif var_obj then PrgNewVar(var_spot, prgdata.exec_scope, prgdata) PrgNewVar(var_obj, prgdata.exec_scope, prgdata) PrgAddExecLine(prgdata, level, string.format('%s, %s = %s', var_spot, var_obj, spot_obj_desc_resolved)) elseif var_spot then PrgNewVar(var_spot, prgdata.exec_scope, prgdata) PrgAddExecLine(prgdata, level, string.format('%s = %s', var_spot, spot_obj_desc_resolved)) end if var_pos then PrgNewVar(var_pos, prgdata.exec_scope, prgdata) PrgAddExecLine(prgdata, level, string.format('%s = %s and %s:GetSpotLocPos(%s)', var_pos, var_spot, var_obj, var_spot)) end end function XPrgCommand:GenCodePlaceObject(prgdata, level, var, attach, classname, entity, anim, scale, flags, material, opacity, fade_in) if var == "" then var = "_obj" PrgNewVar(var, prgdata.exec_scope, prgdata) end local components = {} if (tonumber(fade_in) or 0) > 0 then components[#components+1] = "const.cofComponentInterpolation" end if anim ~= "" and anim ~= "idle" then components[#components+1] = "const.cofComponentAnim" end if attach then components[#components+1] = "const.cofComponentAttach" end if next(components) then PrgAddExecLine(prgdata, level, string.format('%s = PlaceObject("%s", nil, %s)', var, classname, table.concat(components, " + "))) else PrgAddExecLine(prgdata, level, string.format('%s = PlaceObject("%s")', var, classname)) end PrgAddExecLine(prgdata, level, string.format('NetTempObject(%s)', var)) if entity ~= "" then PrgAddExecLine(prgdata, level, string.format('ChangeEntity(%s)', var, entity)) end if (tonumber(scale) or 100) ~= 100 then PrgAddExecLine(prgdata, level, string.format('%s:SetScale(%s)', var, scale)) end if anim ~= "" and anim ~= "idle" then PrgAddExecLine(prgdata, level, string.format('%s:SetState("%s", 0, 0)', var, anim)) end if flags == "Mirrored" then PrgAddExecLine(prgdata, level, string.format('%s:SetMirrored(true)', var)) elseif flags == "LockedOrientation" then PrgAddExecLine(prgdata, level, string.format('%s:SetGameFlags(const.gofLockedOrientation)', var)) elseif flags == "OnGround" or flags == "OnGroundTiltByGround" then PrgAddExecLine(prgdata, level, string.format('%s:SetGameFlags(const.gofAttachedOnGround)', var)) elseif flags == "SyncWithParent" then PrgAddExecLine(prgdata, level, string.format('%s:SetGameFlags(const.gofSyncState)', var)) end if (tonumber(fade_in) or 0) > 0 then PrgAddExecLine(prgdata, level, string.format('%s:SetOpacity(0)', var)) PrgAddExecLine(prgdata, level, string.format('%s:SetOpacity(100, %s)', var, fade_in)) end end function XPrgCommand:GenCodeSetPos(prgdata, level, actor, obj, spot, spot_type, offset, time) if spot == "" then if spot_type == "" then spot = "-1" else spot = string.format('%s:GetRandomSpot("%s")', obj, spot_type) end end if offset and offset ~= point30 and offset ~= point20 then PrgAddExecLine(prgdata, level, string.format('%s:SetPos(%s:GetSpotLocPos(%s) + %s, %s)', actor, obj, spot, ValueToLuaCode(offset), time)) else PrgNewVar("_x", prgdata.exec_scope, prgdata) PrgNewVar("_y", prgdata.exec_scope, prgdata) PrgNewVar("_z", prgdata.exec_scope, prgdata) PrgAddExecLine(prgdata, level, string.format('_x, _y, _z = %s:GetSpotLocPosXYZ(%s)', obj, spot)) PrgAddExecLine(prgdata, level, string.format('%s:SetPos(_x, _y, _z, %s)', actor, time)) end end function XPrgCommand:UpdateLocalVarCombo() if not PrgExportData or not PrgSelected then return {} end local var_list = {} local prgdata = PrgExportData[PrgSelected] if not prgdata then return {} end for var, _ in pairs(prgdata.used_vars or empty_table) do var_list[#var_list + 1] = var end return var_list end function XPrgCommand:GenCodeOrient(prgdata, level, orient_obj, orient_obj_axis, obj, spot, spot_type, direction, attach, attach_offset, time, add_dtor, orient_obj_valid) if spot == "" then if spot_type == "" then spot = "-1" else spot = string.format('%s:GetRandomSpot("%s")', obj, spot_type) end end if time == "" then time = "0" end if direction == "" then direction = "" end orient_obj_axis = tonumber(orient_obj_axis) or 1 local direction_axis local get_angle, get_axis_angle if direction == "" or direction == "SpotX 2D" and abs(orient_obj_axis) ~= 3 then get_angle = string.format('%s:GetSpotAngle2D(%s)', obj, spot) if orient_obj_axis == 1 then elseif orient_obj_axis == -1 then get_angle = string.format('-%s', get_angle) elseif orient_obj_axis == 2 then get_angle = string.format('%s + %s', get_angle, 90*60) elseif orient_obj_axis == -2 then get_angle = string.format('%s - %s', get_angle, 90*60) end elseif direction == "SpotX 2D" then PrgNewVar("_x", prgdata.exec_scope, prgdata) PrgNewVar("_y", prgdata.exec_scope, prgdata) PrgAddExecLine(prgdata, level, string.format('_x, _y = %s:GetSpotAxisVecXYZ(%s, 1)', obj, spot)) get_axis_angle = string.format('OrientAxisToVectorXYZ(%s, _x, _y, 0)', orient_obj_axis) elseif direction == "SpotX" or direction == "SpotY" or direction == "SpotZ" then direction_axis = direction == "SpotX" and 1 or direction == "SpotY" and 2 or 3 get_axis_angle = string.format('OrientAxisToVectorXYZ(%s, %s:GetSpotAxisVecXYZ(%s, %d))', orient_obj_axis, obj, spot, direction_axis) elseif direction == "Face3D" then PrgNewVar("_x", prgdata.exec_scope, prgdata) PrgNewVar("_y", prgdata.exec_scope, prgdata) PrgNewVar("_z", prgdata.exec_scope, prgdata) PrgNewVar("_x2", prgdata.exec_scope, prgdata) PrgNewVar("_y2", prgdata.exec_scope, prgdata) PrgNewVar("_z2", prgdata.exec_scope, prgdata) PrgAddExecLine(prgdata, level, string.format('_x, _y, _z = %s:GetSpotLocPosXYZ(%s)', obj, spot)) PrgAddExecLine(prgdata, level, string.format('_x2, _y2, _z2 = %s:GetSpotLocPosXYZ(-1)', orient_obj)) get_axis_angle = string.format('OrientAxisToVectorXYZ(%s, _x - _x2, _y - _y2, _y - _y2)', orient_obj_axis) elseif direction == "Face" then PrgNewVar("_x", prgdata.exec_scope, prgdata) PrgNewVar("_y", prgdata.exec_scope, prgdata) PrgNewVar("_x2", prgdata.exec_scope, prgdata) PrgNewVar("_y2", prgdata.exec_scope, prgdata) PrgAddExecLine(prgdata, level, string.format('_x, _y = %s:GetSpotLocPosXYZ(%s)', obj, spot)) PrgAddExecLine(prgdata, level, string.format('_x2, _y2 = %s:GetSpotLocPosXYZ(-1)', orient_obj)) get_axis_angle = string.format('OrientAxisToVectorXYZ(%s, _x - _x2, _y - _y2, 0)', orient_obj_axis) elseif direction == "Random2D" then get_angle = string.format('InteractionRand(360*60, "XPrg")') end if attach then local indent = 0 if not orient_obj_valid then PrgAddExecLine(prgdata, level, string.format('if IsValid(%s) then', orient_obj)) level = level + 1 end if spot then PrgAddExecLine(prgdata, level, string.format('%s:Attach(%s, %s)', obj, orient_obj, spot)) else PrgAddExecLine(prgdata, level, string.format('%s:Attach(%s)', obj, orient_obj)) end if add_dtor then local param_idx = table.find(prgdata.params, "name", orient_obj) if param_idx == 1 then PrgAddDtorLine(prgdata, 2, string.format('%s:Detach()', orient_obj)) else local g_attach if param_idx then g_attach = orient_obj else g_attach = PrgGetFreeVarName(prgdata, "_attach") PrgNewVar(g_attach, prgdata.exec_scope, prgdata) PrgAddExecLine(prgdata, level, string.format('%s = %s', g_attach, orient_obj)) end PrgAddDtorLine(prgdata, 2, string.format('if IsValid(%s) then', g_attach)) PrgAddDtorLine(prgdata, 3, string.format('%s:Detach()', g_attach)) PrgAddDtorLine(prgdata, 2, 'end') end end if attach_offset and attach_offset ~= point30 and attach_offset ~= point20 then PrgAddExecLine(prgdata, level, string.format('%s:SetAttachOffset(%s)', orient_obj, ValueToLuaCode(attach_offset))) end if direction == "" or orient_obj_axis == direction_axis then -- leave default attach orientation elseif get_axis_angle then PrgNewVar("_x", prgdata.exec_scope, prgdata) PrgNewVar("_y", prgdata.exec_scope, prgdata) PrgNewVar("_z", prgdata.exec_scope, prgdata) PrgNewVar("_angle", prgdata.exec_scope, prgdata) PrgAddExecLine(prgdata, level, string.format('_x, _y, _z, _angle = %s', get_axis_angle)) PrgAddExecLine(prgdata, level, string.format('%s:SetAttachAxis(_x, _y, _z)', orient_obj)) PrgAddExecLine(prgdata, level, string.format('%s:SetAttachAngle(_angle)', orient_obj)) elseif get_angle then PrgAddExecLine(prgdata, level, string.format('%s:SetAttachAxis(axis_z)', orient_obj)) PrgAddExecLine(prgdata, level, string.format('%s:SetAttachAngle(%s)', orient_obj, get_angle)) end if not orient_obj_valid then level = level - 1 PrgAddExecLine(prgdata, level, "end") end else if get_axis_angle then PrgNewVar("_x", prgdata.exec_scope, prgdata) PrgNewVar("_y", prgdata.exec_scope, prgdata) PrgNewVar("_z", prgdata.exec_scope, prgdata) PrgNewVar("_angle", prgdata.exec_scope, prgdata) PrgAddExecLine(prgdata, level, string.format('_x, _y, _z, _angle = %s', get_axis_angle)) PrgAddExecLine(prgdata, level, string.format('%s:SetAxisAngle(_x, _y, _z, _angle, %s)', orient_obj, time)) elseif get_angle then PrgAddExecLine(prgdata, level, string.format('%s:SetAngle(%s, %s)', orient_obj, get_angle, time)) end end end function XPrgCommand:AddSpotFlags(prgdata, level, obj, spot, flags) local list = flags and PrgSplitStr(flags, ",", '"%s"') or empty_table if #list == 0 then return end PrgAddExecLine(prgdata, level, string.format('PrgSetSpotFlags(%s, %s, %s)', obj, spot, table.concat(list, ", "))) end --------- Commands that are shared between UnitAI editor and Ambient Life editor DefineClass.XPrgBasicCommand = { __parents = { "XPrgCommand" }, } --------- local ConditionTypes = { { text = "if then ... end", value = "if-then"}, { text = "else if ", value = "else-if"}, { text = "---", value = ""}, { text = "while do ... end", value = "while-do"}, { text = "repeat until ", value = "repeat-until"}, { text = "break if ", value = "break-if"}, { text = "---", value = ""}, { text = "A = ", value = "A="}, { text = "A = A or ", value = "A|="}, { text = "A = A and ", value = "A&="}, } DefineClass.XPrgCondition = { __parents = { "XPrgCommand" }, properties = { { id = "form", name = "Type of condition", editor = "dropdownlist", default = "if-then", items = ConditionTypes}, { id = "var", name = "Var", editor = "text", default = "", no_edit = function(self) return self.form ~= "A=" and self.form ~= "A|=" and self.form ~= "A&=" end }, { id = "Not", editor = "bool", default = false }, }, Menubar = "_", MenubarSection = "", TreeView = T{414394759813, "
", form = function(obj) local condition = obj:GenConditionTreeView() if obj.form == "if-then" then return T{763212526438, "if then", condition = condition} elseif obj.form == "else-if" then return condition == "" and T(370973930815, "else") or T{357274843415, "else if then", condition = condition} elseif obj.form == "while-do" then return T{166857454796, "while do", condition = condition} elseif obj.form == "repeat-until" then return T{229657315864, "repeat until ", condition = condition} elseif obj.form == "break-if" then return (condition == "" or condition == "true") and T(802798572963, "break") or T{490884085957, "break if ", condition = condition} elseif obj.form == "A=" then return T{802606782926, " = ", var = obj.var, condition = condition} elseif obj.form == "A|=" then return T{921587144436, " = or ", var = obj.var, condition = condition} elseif obj.form == "A&=" then return T{731825742704, " = and ", var = obj.var, condition = condition} end return "" end}, } function XPrgCondition:GenConditionTreeView() return "" end function XPrgCondition:GenConditionCode() return "" end function XPrgCondition:GenCode(prgdata, level) local condition = self:GenConditionCode(prgdata, level) if self.form == "if-then" then PrgAddExecLine(prgdata, level, string.format('if %s then', condition)) GenBlockCode(self, prgdata, level + 1) local parent = prgdata[self].parent local next_command = parent[table.find(parent, self) + 1] if not next_command or not IsKindOf(next_command, "XPrgCondition") or next_command.form ~= "else-if" then PrgAddExecLine(prgdata, level, "end", level) end elseif self.form == "else-if" then if condition == "" then PrgAddExecLine(prgdata, level, 'else') else PrgAddExecLine(prgdata, level, string.format('elseif %s then', condition)) end GenBlockCode(self, prgdata, level + 1) local parent = prgdata[self].parent local next_command = parent[table.find(parent, self) + 1] if not next_command or not IsKindOf(next_command, "XPrgCondition") or next_command.form ~= "else-if" then PrgAddExecLine(prgdata, level, "end", level) end elseif self.form == "while-do" then PrgAddExecLine(prgdata, level, string.format('while %s do', condition)) GenBlockCode(self, prgdata, level + 1) PrgAddExecLine(prgdata, 0, "VISIT_RESTART") PrgAddExecLine(prgdata, level, "end", level) elseif self.form == "repeat-until" then PrgAddExecLine(prgdata, level, 'repeat') GenBlockCode(self, prgdata, level + 1) PrgAddExecLine(prgdata, 0, "VISIT_RESTART") PrgAddExecLine(prgdata, level, string.format('until %s', condition)) elseif self.form == "break-if" then if condition == "true" then PrgAddExecLine(prgdata, level, 'break') elseif condition ~= "false" then PrgAddExecLine(prgdata, level, string.format('if %s then', condition)) GenBlockCode(self, prgdata, level + 1) PrgAddExecLine(prgdata, level + 1, 'break') PrgAddExecLine(prgdata, level, 'end') end elseif self.form == "A=" then if not prgdata.used_vars[self.var] then PrgNewVar(self.var, prgdata.exec_scope, prgdata) end PrgAddExecLine(prgdata, level, string.format('%s = %s', self.var, condition)) GenBlockCode(self, prgdata, level) elseif self.form == "A|=" then if condition ~= "" then PrgAddExecLine(prgdata, level, string.format('%s = %s or %s', self.var, self.var, condition)) end GenBlockCode(self, prgdata, level) elseif self.form == "A&=" then if condition ~= "" then PrgAddExecLine(prgdata, level, string.format('%s = %s and %s', self.var, self.var, condition)) end GenBlockCode(self, prgdata, level) end end DefineClass.XPrgCheckExpression = { __parents = { "XPrgCondition", "XPrgBasicCommand"}, properties = { { id = "expression", name = "Expression", default = "true", editor = "text"}, }, Menubar = "Condition", MenubarSection = "", } function XPrgCheckExpression:GenConditionTreeView() if self.Not then return Untranslated(string.format('not (%s)', self.expression)) end return Untranslated(self.expression) end function XPrgCheckExpression:GenConditionCode() if self.Not then return string.format('not (%s)', self.expression) end return self.expression end ----- DefineClass.XPrgCall = { __parents = { "XPrgBasicCommand" }, properties = { { id = "__call", name = "Call", editor = "preset_id", default = "", preset_class = "AmbientLife", }, { category = "Params", id = "param1", name = "Param 1", editor = "text", default = "", }, { category = "Params", id = "param2", name = "Param 2", editor = "text", default = "", }, { category = "Params", id = "param3", name = "Param 3", editor = "text", default = "", }, { category = "Params", id = "param4", name = "Param 4", editor = "text", default = "", }, { category = "Params", id = "param5", name = "Param 5", editor = "text", default = "", }, { category = "Params", id = "param6", name = "Param 6", editor = "text", default = "", }, { category = "Params", id = "param7", name = "Param 7", editor = "text", default = "", }, { category = "Params", id = "param8", name = "Param 8", editor = "text", default = "", }, }, Menubar = "Prg", MenubarSection = "SubPrg", TreeView = T{672565887843, "call <__call>() ", params = function(obj) local params_txt, params_txt_long for i = 1, XPrg.ParamsCount do local param = string.match(obj["param" .. i], "[^%s]+") if param then params_txt_long = params_txt_long and params_txt_long .. ", " .. param or param params_txt = params_txt_long else params_txt_long = params_txt_long and params_txt_long .. ", nil" or "nil" end end return Untranslated(params_txt or "") end}, } function XPrgCall:GenCode(prgdata, level) local name = string.format('"%s"', self.__call) local params = {} for i = 1, XPrg.ParamsCount do params[i] = self["param" .. i] end if #prgdata.params > 0 and self.param1 == prgdata.params[1].name then self:GenCodeCommandCallPrg(prgdata, level, name, table.unpack(params)) else self:GenCodeCallPrg(prgdata, level, name, table.unpack(params)) end end DefineClass.XPrgWait = { __parents = { "XPrgBasicCommand" }, properties = { { id = "anim_end", name = "Wait unit animation end", editor = "bool", default = false, }, { id = "time", name = "Time", editor = "text", default = "", scale = "sec" }, }, Menubar = "Prg", MenubarSection = "SubPrg", TreeView = T(347671088865, "Wait