Merge branch 'fixes' into main/gingo-test
[ryzomcore.git] / ryzom / common / data_common / r2 / r2_base_class.lua
blob9b053e26e0f3240c6f0d1ae81ea9fc60a9de7be4
1 ---------------------------------------------
2 -- Base class for R2 components / features --
3 ---------------------------------------------
5 -- NB : throughout the file 'this' is used instead of the lua 'self' to denote the fact
6 -- that method are not called on the class definition, but on the instances of this class
8 baseClass = -- not local here because of the implementation ...
10 ----------------------
11 -- CLASS PROPERTIES --
12 ----------------------
14 BaseClass = "", -- Name of the base class
15 Version = 0,
16 Name = "BaseClass", -- Name of the class
17 BuildPropertySheet = true, -- True if objects of this class should be editable by using a generic property sheet
18 -- Setting to 'true' will cause the framework to create the property sheet ui at launch
19 Menu="", -- ui path of the contextual menu for this class
21 DisplayerUI = "", -- name of the C++ class that displays this object in the ui
22 DisplayerUIParams = "", -- parameters passed to the ui displayer when it is created
24 DisplayerProperties = "R2::CDisplayerLua", -- 'lua' type of displayer takes the name of a lua function that must return the class of the displayer
25 DisplayerPropertiesParams = "propertySheetDisplayer", -- name of the function that build the displayer that update the property sheet.
27 TreeIcon="", -- icon to be displayed in the tree (or a method returning it)
28 PermanentTreeIcon="", -- icon to be displayed in the tree if in permanent content (or a method returning it)
29 SelectBarType = "", -- type in select bar
30 -- rollout header for the generic property sheet (a string giving the xml code for the header)
31 PropertySheetHeader = nil,
33 -------------------
34 -- CLASS METHODS --
35 -------------------
36 ClassMethods = {},
38 --------------------------
39 -- INSTANCES PROPERTIES --
40 --------------------------
41 -- * Unlike class properties above, they are created in each instance of the class. They are directly accessible in the objects.
42 -- * They are C++ objects exposed through the metatable mechanism
43 -- * They are *READ-ONLY*. They should be modified by network commands like r2.requestSetNode and the like
44 -- To store client side transcient values, use the native property 'User' (detailed below) instead
45 -- TODO complete doc (available types, widgets ...)
46 Prop =
48 {Name="InstanceId", Type="String", WidgetStyle="StaticText", Category="Advanced", Visible=false },
49 },
52 -- 'VIRTUAL' PROPERTIES (NOT IMPLEMENTED)
53 -- Not really properties of this class, but 'shortcuts' to other, real properties
54 -- The virtual properties are use by the property sheet to display properties than are jnot directly in the object,
55 -- but that can be found in another place. (son object for example, or object that require an indirection)
56 -- each virtual prop should provide a 'getPath' function which takes the InstanceId of the instance in which it is contained as its parameter
57 -- It should return a couple : realOwnerInstanceId, realPropName (arrays not supported yet)
58 -- an observer is placed on the real property to take its changes in account in the property sheet
59 -- IMPORTANT: the getPath function will be called each frame to see if observers should be removed
60 -- and placed on another target
62 -- Example : indirection to fictive string property 'Toto' in the Scenario
63 --VirtualProp =
64 --{
65 -- {Name="InstanceId", Type="String", WidgetStyle="StaticText", Category="Advanced", Visible=true,
66 -- getPath = function()
67 -- return r2.Scenario.InstanceId, "Toto"
68 -- end,
69 -- },
70 --},
72 ---------------------------------
73 -- NATIVE / SPECIAL PROPERTIES --
74 ---------------------------------
76 -- They are implemented in C++ (thanks to the lua metatables)
77 -- Parent : (R/O) : The direct parent of this object. If parent object is in a table, returns the table
78 -- ParentInstance : (R/O) : Next parent of this object with an InstanceId. In object is contained in a property that
79 -- is a table, the object containing the table will be returned, not the table
80 -- IndexInParent : (R/O) : If parent is a table, then return index into it, -1 otherwise
81 -- User : Read/Write lua table attached to the object. The 'User' field itself is R/O This is the place to store client side
82 -- edition variables.
83 -- Size : (R/O) : If object is a table, returns its size, nil otherwise
84 -- DisplayerUI : (R/O) : for instances : returns reference to the ui displayer. May be nil.
85 -- In the case where the field 'DisplayerUI' of this class definition id 'R2::CDisplayerLua', it would return
86 -- the displayer object created by a call to the function defined in 'DisplayerUIParams' (in this class definition)
87 -- DisplayerVisual : (R/O) : Same as DisplayerUI but for 'in scene' displayer
88 -- DisplayerProperties : (R/O) : Same as DisplayerUI but for the properties displayer
89 -- Selectable : (R/W) : default is true. When false, the object can't be selected in the scene. This flag is local to the instance (ancestor state not inherited)
90 -- SelectableFromRoot : (R/O) : True if this object and also its ancestor are selectable
92 ------------
93 -- EVENTS --
94 ------------
95 -- Events that are sent to the displayers, are also sent to instances that hold those displayers :
96 -- By default they are not handled
97 -- To handle, one shouldd redefine :
98 -- function baseClass.onCreate(this)
99 -- function baseClass.onErase(this)
100 -- etc ...
101 -- see r2_ui_displayers.lua for details
105 ---------------------
106 -- GENERAL METHODS --
107 ---------------------
109 -- Methods are defined in the class definition and are nevertheless callable on instances as follow :
110 -- instance:methodName(params ...)
111 -- In the class, the method would be defined as follow:
112 -- methodName = function(this, param1, param2 ...) ... some code ... end
113 -- 'this' will be filled at runtime by a reference on the instance on which the method is called.
114 -- Method calling is possible thanks to the metamethod mechanism (in this case it is implemented in C++)
115 -- Calling a method is in fact equivalent to doing the following :
116 -- r2:getClass(instance).methodName(instance, param1, param2 ..)
119 ---------------------------------------
120 -- TYPE / CLASS / PROPERTIES METHODS --
121 ---------------------------------------
123 ---------------------------------------------------------------------------------------------------------
124 -- return a reference to the class of this object
125 function baseClass.getClass(this)
126 return r2:getClass(this)
129 ---------------------------------------------------------------------------------------------------------
130 -- get description of a property (as found in the 'Prop' table of the class definition) from its name
131 function baseClass.getPropDesc(this, propName)
132 return this:getClass().NameToProp[propName]
135 ---------------------------------------------------------------------------------------------------------
136 -- return name of the parent class
137 function baseClass.getClassName(this)
138 return this.Class
141 ---------------------------------------------------------------------------------------------------------
142 -- test if object is of the given class (or derived from the class)
143 -- param 'class' should be a string identifying the class
144 function baseClass.isKindOf(this, className)
146 assert( type(this) == "userdata")
147 local currClass = this:getClass()
148 while currClass do
149 if currClass.Name == className then
150 return true
152 currClass = r2.Classes[currClass.BaseClass]
154 return false
158 ---------------------------------------------------------------------------------------------------------
159 -- return a 'this' of the base class
160 -- Use this to access a method defined in a base class from a derived class
162 -- example : this:delegate():doThis() -- Call the doThis function in the parent class
164 -- Expected behavior is the same than with C++ :
165 -- Call from a delegated pointer is static
166 -- any further call is the call chain is polymorphic
167 -- Calls to delegate can be chained
168 -- NB : this function shouldn't be redefined in derived classes (the delegation mechanism uses a pointer on this function)
169 --function baseClass.delegate(this)
170 -- return __baseClassImpl.delegate(this) -- implementation defined in "r2_base_class_private.lua"
171 --end
173 ---------------------------------------------------------------------------------------------------------
174 -- Get actual C++ "this" for this object. Because of the delegation mechanism, this may be a raw C++ object
175 -- or a lua table that performs the delegation
176 -- OBSOLETE, TO REMOVE
177 function baseClass.getRawThis(this)
178 -- return __baseClassImpl.getRawThis(this) -- implementation defined in "r2_base_class_private.lua"
179 return this
183 ---------------------------------------------------------------------------------------------------------
184 -- compare current "this", with another "this" pointer
185 -- This should be the standard way to compare instance in memory because 'this' may sometime be a userdata
186 -- (raw C++ pointer to internal C++ object), or a table (delegated 'this' pointer)
187 -- OBSOLETE, TO REMOVE
188 function baseClass.isSameObjectThan(this, other)
189 --if this:isKindOf("Act") then
190 -- breakPoint()
191 --end
192 --return this:getRawThis() == other:getRawThis()
193 return this == other
199 ---------------------------------------------------------------------------------------------------------
200 -- Helper : Return world position (that is, absolute position). By default, object deriving from the base class have no position
201 function baseClass.getWorldPos()
202 return { x = 0, y = 0, z = 0 }
205 ---------------------------------------------------------------------------------------------------------
206 -- When adding content, pionneer have a limited budget. This method gives the 'cost' of this object (0 by default)
207 --function baseClass.getUsedQuota(this)
208 -- return 0
209 --end
211 -------------------
212 -- SCENARIO COST --
213 -------------------
215 ---------------------------------------------------------------------------------------------------------
216 -- See wether this element has a cost in the scenario
217 function baseClass.hasScenarioCost(this)
218 return false
221 ---------------------------------------------------------------------------------------------------------
222 -- get local cost cached in object
223 function baseClass.getLocalCost(this)
224 return defaulting(this.User.Cost, 0)
227 ---------------------------------------------------------------------------------------------------------
228 -- set local cost in object
229 function baseClass.setLocalCost(this, cost)
230 this.User.Cost = cost
233 ---------------------------------------------------------------------------------------------------------
234 -- get local static cost cached in object
235 function baseClass.getLocalStaticCost(this)
236 return defaulting(this.User.StaticCost, 0)
239 ---------------------------------------------------------------------------------------------------------
240 -- set local static cost in object
241 function baseClass.setLocalStaticCost(this, cost)
242 this.User.StaticCost = cost
247 function baseClass.getStaticObjectCost(this)
248 return 0
251 function baseClass.getAiCost(this)
252 return 0
255 ----------------------
256 -- OBJECT HIERARCHY --
257 ----------------------
259 ---------------------------------------------------------------------------------------------------------
260 -- append all sub-content that is "kind of" 'kind' to 'destTable'
261 -- NB : this is very SLOW!!! please use iterators instead (see r2:enumInstances)
262 function baseClass.appendInstancesByType(this, destTable, kind)
263 assert(type(kind) == "string")
264 if this.CompareClass and this.CompareClass == true then
265 if this.Class == kind then
266 if destTable == nil then
267 dumpCallStack(1)
269 table.insert(destTable, this:getRawThis())
271 elseif this:isKindOf(kind) then
272 if destTable == nil then
273 dumpCallStack(1)
275 table.insert(destTable, this:getRawThis())
276 end
279 ---------------------------------------------------------------------------------------------------------
280 -- Append all instances rooted at this object (including this one)
282 function baseClass.getSons(this, destTable)
283 r2:exploreInstanceTree(this, destTable)
286 ---------------------------------------------------------------------------------------------------------
287 -- Search first ancestor of the wanted kind (that is of class 'className' or a derived class)
288 function baseClass.getParentOfKind(this, className)
289 local parent = this.ParentInstance
290 while parent do
291 assert(parent.isKindOf)
292 if parent:isKindOf(className) then return parent end
293 parent = parent.ParentInstance
295 return nil
298 ---------------------------------------------------------------------------------------------------------
299 -- Search parent until an act is found
300 function baseClass.getParentAct(this)
301 return this:getParentOfKind("Act")
304 ---------------------------------------------------------------------------------------------------------
305 -- Search parent until a scenario is found
306 function baseClass.getParentScenario(this)
307 return this:getParentOfKind("Scenario")
310 ---------------------------------------------------------------------------------------------------------
311 -- See if hits object is inserted in the default feature (that is : single npcs, bot objects, roads etc. with no enclosing group or feature)
312 function baseClass.isInDefaultFeature(this)
313 return this.ParentInstance:isKindOf('DefaultFeature')
317 --------------------------
318 -- UI METHODS / DISPLAY --
319 --------------------------
321 ---------------------------------------------------------------------------------------------------------
322 -- Called by the contextual menu/toolbar when the 'delete' option is chosen by the user
323 -- on THIS client
324 -- This is the place to perform additionnal deletion tasks
325 -- Example : a vertex may want to delete its containing region when there are 2 vertices left only
326 -- default -> just call 'r2.requestEraseNode'
327 function baseClass.onDelete(this)
328 if this.User.DeleteInProgress == true then return end
329 this.User.DeleteInProgress = true
330 this:setDeleteActionName()
331 this:selectNext()
332 r2.requestEraseNode(this.InstanceId, "", -1)
333 r2.requestEndAction()
336 -- helper : add "delete : name_of_the_thing_being_deleted" in the action historic as the name of the delete action that is about
337 -- to be done
338 function baseClass.setDeleteActionName(this)
339 r2.requestNewAction(concatUCString(i18n.get("uiR2EDDeleteAction"), this:getDisplayName()))
343 ---------------------------------------------------------------------------------------------------------
344 -- Test wether the user can delete this object
345 function baseClass.isDeletable(this)
346 if this.Deletable and this.Deletable == 0 then return false end
347 return true
350 ---------------------------------------------------------------------------------------------------------
351 -- called when the instance is selected (default is no op)
352 function baseClass.onSelect(this)
356 ---------------------------------------------------------------------------------------------------------
357 -- Tell if object can be selected as next object if a predecessor object
358 -- has been selected in the parent list
359 function baseClass.isNextSelectable(this)
360 return false
363 ---------------------------------------------------------------------------------------------------------
364 -- get next selectable object (or nil else)
365 function baseClass.getNextSelectableObject(this)
366 local startIndex = this.IndexInParent
367 if type(startIndex) ~= "number" then return nil end
368 local currIndex = startIndex
369 while true do
370 currIndex = currIndex + 1
371 if currIndex == this.Parent.Size then
372 currIndex = 0
374 local instance = this.Parent[currIndex]
375 if currIndex == startIndex then break end
376 if instance ~= nil then
377 local firstSon = instance:getFirstSelectableSon()
378 if firstSon ~= nil and firstSon:isNextSelectable() then
379 return firstSon
380 elseif instance.Selectable and instance:isNextSelectable() then
381 return instance
385 if this.ParentInstance:isKindOf("DefaultFeature") then
386 return this.ParentInstance:getNextSelectableObject()
388 return nil
391 ---------------------------------------------------------------------------------------------------------
392 -- select object next to this one, if there's one
393 function baseClass.selectNext(this)
394 local nextSelection = this
395 while 1 do
396 nextSelection = nextSelection:getNextSelectableObject()
397 if not nextSelection or nextSelection == this then
398 r2:setSelectedInstanceId("")
399 return
400 end
401 if nextSelection then
402 -- should not be frozen or hiden
403 if (not nextSelection.DisplayerVisual) or (nextSelection.DisplayerVisual.DisplayMode ~= 1 and nextSelection.DisplayerVisual.DisplayMode ~= 2) then
404 r2:setSelectedInstanceId(nextSelection.InstanceId)
405 return
406 end
407 end
411 ---------------------------------------------------------------------------------------------------------
412 -- if an object is not selectable if may nevertheless contain selectable object, the first one is returned by this method
413 function baseClass.getFirstSelectableSon(this)
414 return nil
417 ---------------------------------------------------------------------------------------------------------
418 -- get select bar type
419 function baseClass.getSelectBarType(this)
420 return r2:evalProp(this:getClass().SelectBarType, this, "")
423 ---------------------------------------------------------------------------------------------------------
424 -- get name of tree icon
425 function baseClass.getTreeIcon(this)
426 return r2:evalProp(this:getClass().TreeIcon, this, "")
427 end
429 ---------------------------------------------------------------------------------------------------------
430 -- get name of tree icon
431 function baseClass.getPermanentTreeIcon(this)
432 return r2:evalProp(this:getClass().PermanentTreeIcon, this, "")
433 end
435 ---------------------------------------------------------------------------------------------------------
436 -- get name of tree icon according to permanent or current act
437 function baseClass.getContextualTreeIcon(this)
438 if this:getParentAct() and this:getParentAct():isBaseAct() then
439 return this:getPermanentTreeIcon()
440 else
441 return this:getTreeIcon()
443 end
445 ---------------------------------------------------------------------------------------------------------
446 -- get name of permanent statut icon
447 function baseClass.getPermanentStatutIcon(this)
448 return ""
449 end
450 ---------------------------------------------------------------------------------------------------------
451 -- get name of icon to be displayed in the slect bar
452 function baseClass.getSelectBarIcon(this)
453 return this:getContextualTreeIcon()
456 ---------------------------------------------------------------------------------------------------------
457 -- Get the display name (in i18n format). This name will be displayed in the property sheet or inthe instance tree
458 function baseClass.getDisplayName(this)
459 local displayName = ucstring()
460 if this.Name ~= nil and this.Name ~= "" then
461 displayName:fromUtf8(this.Name)
462 else
463 return i18n.get("uiR2EDNoName")
464 -- local className = this.Class
465 -- -- tmp patch
466 -- if this:isKindOf("Npc") then
467 -- if this:isBotObject() then
468 -- className = "Bot object"
469 -- end
470 -- end
471 -- className = className .. " : " .. this.InstanceId
472 -- displayName:fromUtf8(className)
474 return displayName
477 ---------------------------------------------------------------------------------------------------------
478 -- Get the base name for instance name generation (should return a ucstring)
479 function baseClass.getBaseName(this)
480 return ucstring("")
484 ---------------------------------------------------------------------------------------------------------
485 -- return true if this instance can by displayed as a button in the select bar
486 function baseClass.displayInSelectBar(this)
487 return true
490 ---------------------------------------------------------------------------------------------------------
491 -- get first parent that is selectable in the select bar
492 function baseClass.getFirstSelectBarParent(this)
493 local curr = this.ParentInstance
494 while curr and not curr:displayInSelectBar() do
495 curr = curr.ParentInstance
497 return curr
500 ---------------------------------------------------------------------------------------------------------
501 -- search the first son that could be inserted in the select bar
502 -- default is to look recursively in the 'son select bar container'
503 function baseClass.getFirstSelectBarSon(this)
504 local sons = this:getSelectBarSons()
505 if not sons then return nil end
506 for k, v in specPairs(sons) do
507 if v:displayInSelectBar() then
508 return v
510 local firstSelectBarSon = v:getFirstSelectBarSon()
511 if firstSelectBarSon ~= nil then
512 return firstSelectBarSon
517 ---------------------------------------------------------------------------------------------------------
518 -- test if object can have sons than are displayable in the select bar
519 function baseClass.canHaveSelectBarSons(this)
520 return false;
523 ---------------------------------------------------------------------------------------------------------
524 -- return the default container that may contain object displayable in the select bar
525 function baseClass.getSelectBarSons()
526 return nil
529 ---------------------------------------------------------------------------------------------------------
530 -- called by the select bar when it displays its menu. Additionnal can be added there
531 function baseClass.completeSelectBarMenu(rootMenu)
532 -- no-op
535 ---------------------------------------------------------------------------------------------------------
536 -- The following method is called when the default ui displayer wants to know where to attach an object in the instance tree
537 -- Default behaviour is to return the tree node of the parent object when one is found
538 function baseClass.getParentTreeNode(this)
539 parent = this.ParentInstance
540 while parent ~= nil do
541 if parent.User.TreeNodes ~= nil then
542 return parent.User.TreeNodes
544 parent = parent.ParentInstance
545 end
546 return nil
549 --------------------------------------------------------------------------------------------
550 -- Helper function for features : return the feature parent tree node in their act
551 function baseClass.getFeatureParentTreeNode(this)
553 --return this:getParentAct():getContentTreeNodes("macro_components")
554 return this:getParentAct():getContentTreeNodes()
558 --------------------------------------------------------------------------------------------
559 -- TODO: test if the object can be exported (true by default)
560 function baseClass.isExportable(this)
561 return true
564 ---------------------------------------------------------------------------------------------------------
565 -- This method is called by the C++ when the contextual menu is about to be displayed
566 function baseClass.onSetupMenu(this)
567 local class = r2:getClass(this)
568 local menuName = class.Menu
569 if menuName == nil then return end
570 local menu = getUI(menuName)
571 -- setup menu entries to select parents
572 --for i = 1,8 do
573 -- menu["selectParent" .. tostring(i)].active = false
574 --end
575 -- local parent = this.ParentInstance
576 -- for i = 1,9 do
577 -- if parent == nil or parent.Parent == nil then break end
578 -- menu["selectParent" .. tostring(i)].active = true
579 -- menu["selectParent" .. tostring(i)].uc_hardtext = i18n.get("uimR2EDSelectParent") + (parent.InstanceId .. " (" .. parent.Class .. ")")
580 -- --debugInfo(colorTag(0, 255, 255) .. tostring(i))
581 -- parent = parent.ParentInstance
582 -- end
583 -- -- setup cut & paste entries
584 -- local cuttedSelection = r2:getCuttedSelection()
585 -- if cuttedSelection and this.accept ~= nil then
586 -- local canPaste = this:accept(cuttedSelection)
587 -- debugInfo("canPaste = " .. tostring(canPaste))
588 -- menu.paste.grayed = not canPaste
589 -- else
590 -- menu.paste.grayed = true
591 -- end
592 -- debug options
593 local extDebug = config.R2EDExtendedDebug == 1
594 menu.dump_lua_table.active = extDebug
595 menu.inspect_lua_table.active = extDebug
596 menu.translateFeatures.active = extDebug
597 menu.dump_dialogs_as_text.active = extDebug
598 menu.update_dialogs_from_text.active = extDebug
600 menu.cut.active = extDebug
601 menu.paste.active = extDebug
605 r2.ContextualCommands:setupMenu(this, menu)
609 -- delete entries for dynamic content
610 -- local menuRoot = menu:getRootMenu()
611 -- local startLine = menuRoot:getLineFromId("dynamic_content_start")
612 -- local endLine = menuRoot:getLineFromId("dynamic_content_end")
613 -- assert(startLine ~= -1 and endLine ~= -1)
614 -- for lineToDel = endLine - 1, startLine + 1, -1 do
615 -- menuRoot:removeLine(lineToDel)
616 -- end
617 -- retrieve dynamic commands
618 -- local commands = this:getAvailableCommands()
619 -- local currentLine = startLine + 1
620 -- local commandIndex = 1
621 -- for commandIndex, command in commands do
622 -- menuRoot:addLineAtIndex(currentLine, i18n.get(command.TextId), "lua", "", "dyn_command_" .. tostring(commandIndex))
623 -- if there's a bitmap, build a group with the buitmap in it, and add to menu
624 -- if command.ButtonBitmap and command.ButtonBitmap ~= "" then
625 -- local menuButton = createGroupInstance("r2_menu_button", "", { bitmap = command.ButtonBitmap, })
626 -- if menuButton then
627 -- menuRoot:setUserGroupLeft(currentLine, menuButton)
628 -- end
629 -- end
630 --currentLine = currentLine + 1
631 --end
633 ---------------------------------------------------------------------------------------------------------
634 -- Show the property window for this instance
635 -- (react to the event 'show properties' triggered in the ui, by contextual menu or toolbar)
636 function baseClass.onShowProperties(this)
637 -- for now a global (see r2_ui_property_sheet.lua)
638 r2:showProperties(this)
641 ---------------------------------------------------------------------------------------------------------
642 -- Return list of currently available commands to launch on that instance.
643 -- such commands are displayed in the contextual toolbar or in the contextual menu.
644 -- Returned value should be an array (starting at index 1) with commands of the following format :
646 -- { DoCommand = function(instance) ..., -- code to execute when the command is triggered (by menu or toolbar)
647 -- -- Because the function takes 'instance' as a parameter, it may be
648 -- -- either a global function or a method of this class
649 -- Id = "", -- Id of the action. The action "r2ed_context_command" defined in actions.xml
650 -- -- will search for this id when a key is pressed to find the good action
651 -- TextId = "...", -- Text id for entry menu & toolbar tooltip
652 -- ButtonBitmap = "filename.tga", -- Name of the button to display in the toolbar, nil
653 -- -- or "" if the command should not appear in the contextual toolbar
654 -- Separator = "true", -- optionnal, false by default : specify if there should be a separator
655 -- -- between this button and previous buttons
656 -- ShowInMenu = false, -- false if the entry shouldn't be displayed in the menu
657 -- IsActivity = false -- true if event is an activity
658 -- }
660 -- 'getAvailableCommands' should be called by derived class, possibly adding their
661 -- own commands
663 -- See also : 'buildCommand'
665 function baseClass.getAvailableCommands(this, dest)
666 if this.ParentInstance:isKindOf("UserComponentHolder") then
667 table.insert(dest, this:buildCommand(this.onRemoveFromUserComponent, "removeFromUserComponent", "uimR2EDRemoveFromUserComponent", ""))
669 if this:isDeletable() and this.User.DeleteInProgress ~= true then
670 table.insert(dest, this:buildCommand(this.onDelete, "delete", "uimR2EDMenuDelete", "r2_toolbar_delete.tga"))
672 if this:getClass().BuildPropertySheet then
673 table.insert(dest, this:buildCommand(this.onShowProperties, "properties", "uimR2EDMenuProperties", "r2_toolbar_properties.tga", true))
676 if this:isKindOf("NpcCustom") then
677 table.insert(dest, this:buildCommand(this.customizeLook, "customize_look", "uiR2EDCustomizeLook", "r2_toolbar_customize_look.tga", false))
682 ---------------------------------------------------------------------------------------------------------
683 -- Build a single command entry to be used by 'getAvailableCommands'
684 -- A command entry translates into a button in the contextual toolbar
685 function baseClass.buildCommand(this, command, id, textId, bitmap, separator, showInMenu)
686 if showInMenu == nil then showInMenu = true end
687 return
689 DoCommand = command,
690 TextId = textId,
691 Id = id,
692 ButtonBitmap = bitmap,
693 Separator = separator,
694 ShowInMenu = showInMenu,
695 IsActivity = false
699 ---------------------------------------------------------------------------------------------------------
700 -- same as 'buildCommand', but for activities
701 function baseClass.buildActivityCommand(this, command, id, textId, bitmap, separator, showInMenu)
702 local result = this:buildCommand(command, id, textId, bitmap, separator, showInMenu)
703 result.IsActivity = true
704 return result
707 ---------------------------------------------------------------------------------------------------------
708 -- Special, class method (not instance method) for dev : returns a table containing all infos on which the xml generic property sheet depends
709 -- When this table is modified, then the xml property sheet will be rebuild for this class (when 'resetEditor'
710 -- is called for example.
711 function baseClass.ClassMethods.getGenericPropertySheetCacheInfos(this)
712 local infos = {}
713 infos.Prop = this.Prop -- if one of the properties change, then must rebuild the property sheet
714 infos.PropertySheetHeader = this.PropertySheetHeader -- if the xml header change, then must rebuild the sheet, too
715 return infos
719 ---------------------------------------------------------------------------------------------------------
720 -- get list of command for display in the mini toolbar
721 function baseClass.getAvailableMiniCommands(this, result)
722 -- OBSOLETE
723 -- table.insert(result, this:buildCommand(this.editDialogs, "edit_dialogs", "uiR2EDEditDialogs", "r2_icon_dialog_mini.tga"))
724 -- table.insert(result, this:buildCommand(this.editActions, "edit_actions", "uiR2EDEditActions", "r2_icon_action_mini.tga"))
725 -- table.insert(result, this:buildCommand(this.editReactions, "edit_reactions", "uiR2EDEditReactions", "r2_icon_reaction_mini.tga"))
728 ---------------------------------------------------------------------------------------------------------
729 -- Return true if sequences can be edited on that object
730 function baseClass.isSequencable(this)
731 return false
734 ---------------------------------------------------------------------------------------------------------
735 -- For sequencable object only (baseClass.isSequencable) : return the lookup string for a verb from an activity name
736 -- Indeed, an activity may have different name depending on who performs it
737 -- for example, the "Feed In Zone" activity will be name "work" for a worker kitin instead of "feed" for a carnivore
738 function baseClass.getActivityVerbLookupName(this, activityName)
739 return activityName
743 ---------------------------------------------------------------------------------------------------------
744 -- is the object global to the scenario ? The select bar will call this to force the good update
745 -- for global objects that are selectable (plot items ...)
746 function baseClass.isGlobalObject(this)
747 return false
752 function baseClass.onRemoveFromUserComponent(this)
753 r2_core.CurrentHolderId = this.ParentInstance.InstanceId
754 r2_core:removeUserComponentElement(this.InstanceId)
757 -------------
758 -- REF IDS --
759 -------------
761 -- Set the value of a refId inside this object. (empty string to delete)
762 -- This will push a new action name & call r2.requestNode
763 function baseClass.setRefIdValue(this, refIdName, targetId)
764 local name = this:getDisplayName()
765 local refIdUCName = r2:getPropertyTranslation(this:getClass().NameToProp[refIdName])
766 if targetId == "" then
767 r2.requestNewAction(concatUCString(i18n.get("uiR2EDRemovingTargetAction"), name,
768 i18n.get("uiR2EDAddingReferenceSeparator"), refIdname))
769 else
770 local targetName = r2:getInstanceFromId(targetId):getDisplayName()
771 r2.requestNewAction(concatUCString(i18n.get("uiR2EDAddingReferenceAction"), name,
772 i18n.get("uiR2EDAddingReferenceSeparator"), refIdUCName,
773 i18n.get("uiR2EDAddingReferenceToAction"), targetName))
775 r2.requestSetNode(this.InstanceId, refIdName, targetId)
780 ---------------------------
781 -- COPY / PASTE HANDLING --
782 ---------------------------
784 ---------------------------------------------------------------------------------------------------------
785 -- see if this object support copy (false by default)
786 function baseClass.isCopyable(this)
787 return false
790 ---------------------------------------------------------------------------------------------------------
791 -- Create a canonical copy of this object, this copy can be used by subsequent calls to 'newCopy'
792 function baseClass.copy(this)
793 -- implementation in "r2_base_class_private.lua"
797 -- Create a new copy from a canonical copy
798 -- New instance ids are generated
799 -- Default behavior is to remove all external dependencies and to rename
800 -- internal dependencies
801 -- The result can be used as input to 'paste' & 'ghostPaste'
802 function baseClass.newCopy(canonicalCopy)
803 -- implementation in "r2_base_class_private.lua"
807 ---------------------------------------------------------------------------------------------------------
808 -- Paste the current clipboard
809 -- not really a method here, because 'src' id a lua table (should be the content of the clipboard ...) that can be used with
810 -- a r2.request.. command.
811 -- - this function should copy the result in a suitable place (maybe into current selection, or at global scope)
812 -- NB : if newPlace is not true, then the result should be past at the coordinates found in src
813 -- - It should check that there is some room in the scenario before doing the copy
814 function baseClass.paste(src, newPlace, srcInstanceId)
815 if r2:getLeftQuota() <= 0 then
816 r2:makeRoomMsg()
817 return
821 function baseClass.pasteGhost(src)
823 if r2:getLeftQuota() <= 0 then
824 r2:makeRoomMsg()
825 return
831 -- TMP TMP : move events test
834 function baseClass.onTargetInstancePreHrcMove(this, targetAttr, targetIndexInArray)
835 debugInfo(string.format("instance: pre hrc move : %s", targetAttr))
838 function baseClass.onTargetInstancePostHrcMove(this, targetAttr, targetIndexInArray)
839 debugInfo(string.format("instance : post hrc move : %s", targetAttr))
844 -- IMPLEMENTATION
845 r2.doFile("r2_base_class_private.lua")
848 r2.registerComponent(baseClass)
850 baseClass = nil