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
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,
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 ...)
48 {Name
="InstanceId", Type
="String", WidgetStyle
="StaticText", Category
="Advanced", Visible
=false },
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
65 -- {Name="InstanceId", Type="String", WidgetStyle="StaticText", Category="Advanced", Visible=true,
66 -- getPath = function()
67 -- return r2.Scenario.InstanceId, "Toto"
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
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
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)
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
)
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()
149 if currClass
.Name
== className
then
152 currClass
= r2
.Classes
[currClass
.BaseClass
]
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"
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"
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
192 --return this:getRawThis() == other:getRawThis()
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)
215 ---------------------------------------------------------------------------------------------------------
216 -- See wether this element has a cost in the scenario
217 function baseClass
.hasScenarioCost(this
)
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
)
251 function baseClass
.getAiCost(this
)
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
269 table.insert(destTable
, this
:getRawThis())
271 elseif this
:isKindOf(kind
) then
272 if destTable
== nil then
275 table.insert(destTable
, this
:getRawThis())
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
291 assert(parent
.isKindOf
)
292 if parent
:isKindOf(className
) then return parent
end
293 parent
= parent
.ParentInstance
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
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()
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
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
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
)
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
370 currIndex
= currIndex
+ 1
371 if currIndex
== this
.Parent
.Size
then
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
380 elseif instance
.Selectable
and instance
:isNextSelectable() then
385 if this
.ParentInstance
:isKindOf("DefaultFeature") then
386 return this
.ParentInstance
:getNextSelectableObject()
391 ---------------------------------------------------------------------------------------------------------
392 -- select object next to this one, if there's one
393 function baseClass
.selectNext(this
)
394 local nextSelection
= this
396 nextSelection
= nextSelection
:getNextSelectableObject()
397 if not nextSelection
or nextSelection
== this
then
398 r2
:setSelectedInstanceId("")
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
)
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
)
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
, "")
429 ---------------------------------------------------------------------------------------------------------
430 -- get name of tree icon
431 function baseClass
.getPermanentTreeIcon(this
)
432 return r2
:evalProp(this
:getClass().PermanentTreeIcon
, this
, "")
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()
441 return this
:getTreeIcon()
445 ---------------------------------------------------------------------------------------------------------
446 -- get name of permanent statut icon
447 function baseClass
.getPermanentStatutIcon(this
)
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
)
463 return i18n
.get("uiR2EDNoName")
464 -- local className = this.Class
466 -- if this:isKindOf("Npc") then
467 -- if this:isBotObject() then
468 -- className = "Bot object"
471 -- className = className .. " : " .. this.InstanceId
472 -- displayName:fromUtf8(className)
477 ---------------------------------------------------------------------------------------------------------
478 -- Get the base name for instance name generation (should return a ucstring)
479 function baseClass
.getBaseName(this
)
484 ---------------------------------------------------------------------------------------------------------
485 -- return true if this instance can by displayed as a button in the select bar
486 function baseClass
.displayInSelectBar(this
)
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
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
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
)
523 ---------------------------------------------------------------------------------------------------------
524 -- return the default container that may contain object displayable in the select bar
525 function baseClass
.getSelectBarSons()
529 ---------------------------------------------------------------------------------------------------------
530 -- called by the select bar when it displays its menu. Additionnal can be added there
531 function baseClass
.completeSelectBarMenu(rootMenu
)
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
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
)
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
573 -- menu["selectParent" .. tostring(i)].active = false
575 -- local parent = this.ParentInstance
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
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
590 -- menu.paste.grayed = true
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)
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)
630 --currentLine = currentLine + 1
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
660 -- 'getAvailableCommands' should be called by derived class, possibly adding their
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
692 ButtonBitmap
= bitmap
,
693 Separator
= separator
,
694 ShowInMenu
= showInMenu
,
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
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
)
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
719 ---------------------------------------------------------------------------------------------------------
720 -- get list of command for display in the mini toolbar
721 function baseClass
.getAvailableMiniCommands(this
, result
)
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
)
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
)
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
)
752 function baseClass
.onRemoveFromUserComponent(this
)
753 r2_core
.CurrentHolderId
= this
.ParentInstance
.InstanceId
754 r2_core
:removeUserComponentElement(this
.InstanceId
)
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
))
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
)
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
821 function baseClass
.pasteGhost(src
)
823 if r2
:getLeftQuota() <= 0 then
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
))
845 r2
.doFile("r2_base_class_private.lua")
848 r2
.registerComponent(baseClass
)