Merge branch 'fixes' into main/gingo-test
[ryzomcore.git] / ryzom / common / data_common / r2 / r2_features_quest.lua
blobfcc4ffbb36313c7bd81e848d60038c6ec3b2fde4
2 r2.Features.Quest = {}
4 local feature = r2.Features.Quest
6 feature.Name="Quest"
8 feature.Description=""
10 feature.Components = {}
12 feature.Components.Quest =
14 BaseClass="LogicEntity",
15 Name="Quest",
16 InEventUI = true,
17 Menu="ui:interface:r2ed_feature_menu",
19 DisplayerProperties = "R2::CDisplayerLua",
20 DisplayerPropertiesParams = "questDisplayer",
22 DisplayerUI = "R2::CDisplayerLua",
23 DisplayerUIParams = "defaultUIDisplayer",
24 DisplayerVisual = "R2::CDisplayerVisualEntity",
26 Parameters = {},
28 ApplicableActions = { "activate", "deactivate", "validate current task", "complete"},
30 Events = {"activation", "deactivation", "success"},
32 Conditions = { "is active", "is inactive", "is finished" },
34 TextContexts = {},
36 TextParameters = {},
38 LiveParameters = {},
40 Prop =
42 {Name="InstanceId", Type="String", WidgetStyle="StaticText", Visible = false},
43 {Name="Components", Type="Table"},
44 {Name="Name", Type="String", MaxNumChar="32"},
46 {Name="TaskNumber", Type="Number", Category="uiR2EDRollout_Targets", WidgetStyle="EnumDropDown", Enum={"1", "2", "3", "4", "5"},},
47 {Name="TaskStep1Id", Type="RefId", Category="uiR2EDRollout_Targets", PickFunction="r2:canPickTaskComponent", SetRefIdFunction="r2:setTaskComponentTarget",Visible= function(this) return this:displayRefId(1) end},
48 {Name="TaskStep2Id", Type="RefId", Category="uiR2EDRollout_Targets", PickFunction="r2:canPickTaskComponent", SetRefIdFunction="r2:setTaskComponentTarget",Visible= function(this) return this:displayRefId(2) end},
49 {Name="TaskStep3Id", Type="RefId", Category="uiR2EDRollout_Targets", PickFunction="r2:canPickTaskComponent", SetRefIdFunction="r2:setTaskComponentTarget",Visible= function(this) return this:displayRefId(3) end},
50 {Name="TaskStep4Id", Type="RefId", Category="uiR2EDRollout_Targets", PickFunction="r2:canPickTaskComponent", SetRefIdFunction="r2:setTaskComponentTarget",Visible= function(this) return this:displayRefId(4) end},
51 {Name="TaskStep5Id", Type="RefId", Category="uiR2EDRollout_Targets", PickFunction="r2:canPickTaskComponent", SetRefIdFunction="r2:setTaskComponentTarget",Visible= function(this) return this:displayRefId(5) end},
53 {Name="QuestCompletedText", Type="String", Category="uiR2EDRollout_TextToSay"},
55 {Name="Active", Type="Number", WidgetStyle="Boolean", DefaultValue="1"},
56 {Name="Repeatable", Type="Number", WidgetStyle="Boolean", DefaultValue="0"},
61 getAvailableCommands = function(this, dest)
62 r2.Classes.LogicEntity.getAvailableCommands(this, dest) -- fill by ancestor
63 this:getAvailableDisplayModeCommands(dest)
64 end,
66 getParentTreeNode = function(this)
67 return this:getFeatureParentTreeNode()
68 end,
70 appendInstancesByType = function(this, destTable, kind)
71 assert(type(kind) == "string")
72 --this:delegate():appendInstancesByType(destTable, kind)
73 r2.Classes.LogicEntity.appendInstancesByType(this, destTable, kind)
74 for k, component in specPairs(this.Components) do
75 component:appendInstancesByType(destTable, kind)
76 end
77 end,
79 getSelectBarSons = function(this)
80 return Components
81 end,
83 canHaveSelectBarSons = function(this)
84 return false;
85 end,
87 onPostCreate = function(this)
88 --this:createGhostComponents()
89 if this.User.DisplayProp and this.User.DisplayProp == 1 then
90 r2:setSelectedInstanceId(this.InstanceId)
91 r2:showProperties(this)
92 this.User.DisplayProp = nil
93 end
94 end,
96 pretranslate = function(this, context)
97 r2.Translator.createAiGroup(this, context)
98 end,
104 local component = feature.Components.Quest
107 function component:displayRefId(index)
108 local nbRefId = self.TaskNumber + 1
109 if index <= nbRefId then
110 return true
112 return false
115 function component:textAdapter(text)
116 assert(self)
117 assert(type(text) == "string")
118 local str = text
120 str=string.gsub(str, "<quest_name>", self.Name)
121 return str
124 -----------------------------------------------------------------------------------------------------------
125 -----------------------------------------------------------------------------------------------------------
126 local questDisplayerTable = clone(r2:propertySheetDisplayer())
129 -- If the message is received by a client that didn't request the modification, we must make sure this client
130 -- doesn't modify the data because it has already been done by the initial client.
132 local function checkPickedEntity(this, instanceId, attributeName)
133 if instanceId == "" then
134 return false
136 local tmpInstance = r2:getInstanceFromId(instanceId)
137 assert(tmpInstance)
138 local i = 1
139 while i < 5 do
140 local attrName = "Task" ..i.. "Id"
141 if attrName ~= attributeName and this[attrName] == tmpInstance.InstanceId then
142 return false
144 i = i + 1
146 return true
150 local oldOnAttrModified = questDisplayerTable.onAttrModified
151 function questDisplayerTable:onAttrModified(instance, attributeName)
153 oldOnAttrModified(self, instance, attributeName)
155 if attributeName == "TaskNumber" then
156 local propertySheet = r2:getPropertySheet(instance)
157 local nbRefId = instance.TaskNumber + 1
158 local i = 1
159 while i <= 5 do
160 if i > nbRefId then
161 local name = "TaskStep"..tostring(i).."Id"
162 local refId = propertySheet:find(name)
163 local refIdName = refId:find("name")
164 refIdName.hardtext = "NONE"
165 r2.requestSetNode(instance.InstanceId, name, "")
167 i = i + 1
169 propertySheet.Env.updatePropVisibility()
170 return
173 if (string.find(attributeName, "Id") == nil or attributeName == "InstanceId") then return end
175 local propertySheet = r2:getPropertySheet(instance)
176 local refId = propertySheet:find(attributeName)
177 if refId == nil then return end
178 local refIdName = refId:find("name")
179 local instanceId = instance[attributeName]
180 if not instanceId then
181 return
184 if instanceId == "" then
185 refIdName.hardtext = "NONE"
186 return
189 local inserted = checkPickedEntity(instance, instanceId, attributeName)
190 if inserted == true then
191 local tmpInstance = r2:getInstanceFromId(instanceId)
192 refIdName.hardtext = tmpInstance.Name
193 else
194 r2.requestSetNode(instance.InstanceId, attributeName, "")
196 instance.User.onHrcMove = false
197 end
199 function questDisplayerTable:onSelect(instance, isSelected)
200 r2:logicEntityPropertySheetDisplayer():onSelect(instance, isSelected)
203 function component:onTargetInstancePreHrcMove(targetAttr, targetIndexInArray)
205 local targetId = self[targetAttr]
206 local tmpInstance = r2:getInstanceFromId(targetId)
207 tmpInstance.User.SelfModified = true
213 function r2:questDisplayer()
214 return questDisplayerTable -- returned shared displayer to avoid wasting memory
217 ---------------------------------------------------------------------------------------
218 ---------------------------------------------------------------------------------------
219 function component:createGhostComponents(act)
220 local comp = self
222 local nbTask = 0
223 local firstId = true
225 for id = 1, 5 do
226 local propertyName = "TaskStep"..tonumber(id).."Id"
227 if comp[propertyName] ~= nil and comp[propertyName] ~= "" then
228 local taskInstance = r2:getInstanceFromId(comp[propertyName])
229 if taskInstance then
230 if firstId == true then
231 if comp.Active == 1 then
232 r2.requestSetGhostNode(taskInstance.InstanceId, "Active", 1)
233 else
234 local eventHandler = r2.newComponent("LogicEntityAction")
235 eventHandler.Event.Type = "activation"
236 eventHandler.Event.Value = ""
237 eventHandler.Name = "activation"
239 local action = r2.newComponent("ActionStep")
241 action.Entity = r2.RefId(taskInstance.InstanceId)
242 action.Action.Type = "activate"
243 action.Action.Value = ""
245 table.insert(eventHandler.Actions, action)
247 local behaviorId = comp.Behavior.InstanceId
248 assert(behaviorId)
249 r2.requestInsertGhostNode(behaviorId, "Actions", -1, "", eventHandler)
251 firstId = false
252 else
253 r2.requestSetGhostNode(taskInstance.InstanceId, "Active", 0)
255 r2.requestSetGhostNode(taskInstance.InstanceId, "Repeatable", 0)
258 local eventHandler = r2.newComponent("LogicEntityAction")
259 eventHandler.Event.Type = "deactivation"
260 eventHandler.Event.Value = ""
261 eventHandler.Name = "deactivation"
263 local action = r2.newComponent("ActionStep")
265 action.Entity = r2.RefId(taskInstance.InstanceId)
266 action.Action.Type = "deactivate"
267 action.Action.Value = ""
269 table.insert(eventHandler.Actions, action)
271 local behaviorId = comp.Behavior.InstanceId
272 assert(behaviorId)
273 r2.requestInsertGhostNode(behaviorId, "Actions", -1, "", eventHandler)
279 end
280 end --!FOR
283 function component:getTaskRtIds(context)
284 local rtGroups = {}
285 for id = 1, 5 do
286 local taskId = self["TaskStep"..id.."Id"]
287 if taskId and taskId ~= "" then
288 local rtGroup = r2.Translator.getRtGroup(context, taskId)
289 local prefix = ""
290 if rtGroup.Id and rtGroup.Id ~= "" then
291 prefix = r2:getNamespace()..rtGroup.Id.."."
293 table.insert(rtGroups, prefix)
296 return rtGroups
299 function component:getTaskInstances(context)
300 local instances = {}
301 for id = 1, 5 do
302 local taskId = self["TaskStep"..id.."Id"]
303 if taskId and taskId ~= "" then
304 local instance = r2:getInstanceFromId(taskId)
305 if instance then
306 table.insert(instances, instance)
310 return instances
313 function component:translate(context)
314 --EVENTS :
315 -- 4: activate
316 -- 5 : deactivate
317 -- 4 (for steps) : init (activate reinit default values on mission steps)
318 -- 8 : quest completed
319 -- 9 : validate task step
320 r2.Translator.translateAiGroup(self, context)
322 local rtGrp = r2.Translator.getRtGroup(context, self.InstanceId)
324 local taskInstances = self:getTaskInstances(context)
325 if table.getn(taskInstances) == 0 then return end
326 local taskRtGrps = self:getTaskRtIds(context)
328 local baseAct = r2.Scenario:getBaseAct()
329 local baseActRtGrp = r2.Translator.getRtGroup(context, baseAct.InstanceId)
331 -- Start of state
333 -- v1 = repeatable
334 -- v2 = current step index
335 -- v3 = completed (at least once)
336 local rtAction1 = r2.Translator.createAction("set_value", rtGrp.Id, "Active", self.Active)
337 local rtAction2 = r2.Translator.createAction("set_value", rtGrp.Id, "v1", self.Repeatable)
338 local rtAction3 = r2.Translator.createAction("set_value", rtGrp.Id, "v2", 1)
339 local rtAction4 = r2.Translator.createAction("set_value", rtGrp.Id, "v3", 0)
340 local rtAction = r2.Translator.createAction("multi_actions", { rtAction1, rtAction2, rtAction3, } )
341 r2.Translator.translateAiGroupEvent("start_of_state" , self, context, rtAction)
346 local k, v = next(taskInstances, nil)
347 while k do
348 local taskIndex = k
349 local taskInstance = v
351 local rtActionIncrement = r2.Translator.createAction("increment_quest_step_index", rtGrp.Id, taskIndex)
352 local event = r2.Translator.getEventFromType(taskInstance, context, "succeeded") --get the right event for "succeeded"
353 r2.Translator.translateAiGroupEvent(event.Event, taskInstance, context, rtActionIncrement)
355 k, v = next(taskInstances, k)
360 local actionValidateTask = r2.Translator.createAction("validate_quest_step", rtGrp.Id, taskRtGrps)
362 r2.Translator.translateAiGroupEvent("user_event_9", self, context, actionValidateTask)
366 --Deactivation (event 5)
368 local rtAction = r2.Translator.createAction("multi_actions", {
369 r2.Translator.createAction("set_value", rtGrp.Id, "Active", 0 ),
370 r2.Translator.createAction("set_value", rtGrp.Id, "v2", 0 ),
372 r2.Translator.translateAiGroupEvent("user_event_5", self, context, rtAction)
375 --Mission completed (event 8)
377 local actionBroadcast = r2.Translator.createAction("broadcast_msg", baseActRtGrp.Id, self:textAdapter(self.QuestCompletedText))
379 local actionRepeatable = r2.Translator.createAction("if_value_equal", rtGrp.Id, "v1", 1, --if Repeatable
380 r2.Translator.createAction("multi_actions", {
381 r2.Translator.createAction("set_value", rtGrp.Id, "Active", 1 ),
382 r2.Translator.createAction("set_value", rtGrp.Id, "v2", 1 ),
383 r2.Translator.createAction("set_value", rtGrp.Id, "v3", 1 ),
386 local actionNotRepeatable = r2.Translator.createAction("if_value_equal", rtGrp.Id, "v1", 0, -- if not Repeatable
387 r2.Translator.createAction("multi_actions", {
388 r2.Translator.createAction("user_event_trigger", rtGrp.Id, 5),
389 r2.Translator.createAction("set_value", rtGrp.Id, "v3", 1 ),
393 local actionCompleted = r2.Translator.createAction("multi_actions", {
394 actionRepeatable, actionNotRepeatable, actionBroadcast,
396 r2.Translator.translateAiGroupEvent("user_event_8", self, context, actionCompleted)
398 r2.Translator.translateFeatureActivation(self, context)
402 component.getLogicAction = function(entity, context, action)
404 assert( action.Class == "ActionStep")
405 local component = r2:getInstanceFromId(action.Entity)
406 assert(component)
407 local rtGrp = r2.Translator.getRtGroup(context, component.InstanceId)
408 assert(rtGrp)
410 local prefix = ""
411 if rtGrp.Id and rtGrp.Id ~= "" then
412 prefix = r2:getNamespace() .. rtGrp.Id.."."
415 local taskRtGrps = component:getTaskRtIds(context)
417 if action.Action.Type == "validate current task" then
418 local actionSet = r2.Translator.createAction("set_value", rtGrp.Id, "v2", prefix.."v2 + 1")
419 local actionEvent = r2.Translator.createAction("user_event_trigger", rtGrp.Id, 9)
420 --local actionValidateStep = r2.Translator.createAction("validate_task_step", rtGrp.Id, taskRtGrps)
421 local rtAction = r2.Translator.createAction("multi_actions", {actionSet, actionEvent})
422 local ifActive = r2.Translator.createAction("if_value_equal", rtGrp.Id, "Active", 1, rtAction)
423 return ifActive, ifActive
425 elseif action.Action.Type == "complete" then
427 local actionEventComplete = r2.Translator.createAction("user_event_trigger", rtGrp.Id, 8)
428 local ifActive = r2.Translator.createAction("if_value_equal", rtGrp.Id, "Active", 1, actionEventComplete)
430 return ifActive, ifActive
433 return r2.Translator.getFeatureActivationLogicAction(rtGrp, action)
436 component.getLogicCondition = function(this, context, condition)
438 assert( condition.Class == "ConditionStep")
439 local component = r2:getInstanceFromId(condition.Entity)
440 assert(component)
441 local rtNpcGrp = r2.Translator.getRtGroup(context, component.InstanceId)
442 assert(rtNpcGrp)
445 if condition.Condition.Type == "is active" then
446 local action1 = r2.Translator.createAction("if_value_equal", rtNpcGrp.Id, "Active", 1);
447 return action1, action1
448 elseif condition.Condition.Type == "is inactive" then
449 local action1 = r2.Translator.createAction("if_value_equal", rtNpcGrp.Id, "Active", 0);
450 return action1, action1
451 elseif condition.Condition.Type == "is finished" then
452 local action1 = r2.Translator.createAction("if_value_equal", rtNpcGrp.Id, "v3", 1);
453 return action1, action1
455 return nil,nil
459 component.getLogicEvent = function(this, context, event)
460 assert( event.Class == "LogicEntityAction")
462 local rtNpcGrp = r2.Translator.getRtGroup(context, this.InstanceId)
463 assert(rtNpcGrp)
465 local eventType = tostring(event.Event.Type)
467 local eventHandler, lastCondition = nil, nil
469 if eventType == "success" then
470 return r2.Translator.getComponentUserEvent(rtNpcGrp, 8)
473 return r2.Translator.getFeatureActivationLogicEvent(rtNpcGrp, event)
476 component.createComponent = function(x, y)
478 local questCompletedText = i18n.get("uiR2EdQuest_QuestCompletedText"):toUtf8()
480 local comp = r2.newComponent("Quest")
481 assert(comp)
483 comp.Base = r2.Translator.getDebugBase("palette.entities.botobjects.bot_chat")
484 comp.Name = r2:genInstanceName(i18n.get("uiR2EdQuest")):toUtf8()
486 comp.QuestCompletedText = questCompletedText
487 comp.TaskNumber = 0
489 comp.Position.x = x
490 comp.Position.y = y
491 comp.Position.z = r2:snapZToGround(x, y)
493 comp._Seed = os.time()
495 return comp
498 component.create = function()
499 if r2:getLeftQuota() <= 0 then
500 r2:makeRoomMsg()
501 return
503 local function paramsOk(resultTable)
506 local x = tonumber( resultTable["X"] )
507 local y = tonumber( resultTable["Y"] )
508 local showAgain = tonumber(resultTable["Display"])
511 if showAgain == 1 then
512 r2.setDisplayInfo("Quest", 0)
513 else r2.setDisplayInfo("Quest", 1) end
515 if not x or not y
516 then
517 debugInfo("Can't create Component")
518 return
520 local component = feature.Components.Quest.createComponent( x, y)
521 r2:setCookie(component.InstanceId, "DisplayProp", 1)
522 r2.requestInsertNode(r2:getCurrentAct().InstanceId, "Features", -1, "", component)
525 local function paramsCancel()
526 debugInfo("Cancel form for 'Quest' creation")
528 local function posOk(x, y, z)
529 debugInfo(string.format("Validate creation of 'Quest' at pos (%d, %d, %d)", x, y, z))
530 if r2.mustDisplayInfo("Quest") == 1 then
531 r2.displayFeatureHelp("Quest")
533 r2.requestNewAction(i18n.get("uiR2EDNewQuestAction"))
534 local component = feature.Components.Quest.createComponent( x, y)
535 r2:setCookie(component.InstanceId, "DisplayProp", 1)
536 r2.requestInsertNode(r2:getCurrentAct().InstanceId, "Features", -1, "", component)
538 local function posCancel()
539 debugInfo("Cancel choice 'Quest' position")
541 local creature = r2.Translator.getDebugCreature("object_component_bot_chat.creature")
542 r2:choosePos(creature, posOk, posCancel, "createFeatureQuest")
546 function component:registerMenu(logicEntityMenu)
547 local name = i18n.get("uiR2EdQuest")
548 logicEntityMenu:addLine(ucstring(name), "lua", "", "Quest")
551 function component:getLogicTranslations()
552 local logicTranslations = {
553 ["ApplicableActions"] = {
554 ["activate"] = { menu=i18n.get( "uiR2AA0Activate" ):toUtf8(),
555 text=i18n.get( "uiR2AA1Activate" ):toUtf8()},
556 ["deactivate"] = { menu=i18n.get( "uiR2AA0Deactivate" ):toUtf8(),
557 text=i18n.get( "uiR2AA1Deactivate" ):toUtf8()},
558 ["validate current task"] = { menu=i18n.get( "uiR2AA0ValidateCurrentTask" ):toUtf8(),
559 text=i18n.get( "uiR2AA1ValidateCurrentTask" ):toUtf8()},
560 ["complete"] = { menu=i18n.get( "uiR2AA0CompleteQuest" ):toUtf8(),
561 text=i18n.get( "uiR2AA1CompleteQuest" ):toUtf8()},
563 ["Events"] = {
564 ["activation"] = { menu=i18n.get( "uiR2Event0Activation" ):toUtf8(),
565 text=i18n.get( "uiR2Event1Activation" ):toUtf8()},
566 ["deactivation"] = { menu=i18n.get( "uiR2Event0Deactivation" ):toUtf8(),
567 text=i18n.get( "uiR2Event1Deactivation" ):toUtf8()},
568 ["success"] = { menu=i18n.get( "uiR2Event0TaskSuccess" ):toUtf8(),
569 text=i18n.get( "uiR2Event1TaskSuccess" ):toUtf8()},
571 ["Conditions"] = {
572 ["is active"] = { menu=i18n.get( "uiR2Test0Active" ):toUtf8(),
573 text=i18n.get( "uiR2Test1Active" ):toUtf8()},
574 ["is inactive"] = { menu=i18n.get( "uiR2Test0Inactive" ):toUtf8(),
575 text=i18n.get( "uiR2Test1Inactive" ):toUtf8()},
576 ["is finished"] = { menu=i18n.get( "uiR2Test0TaskSuccess" ):toUtf8(),
577 text=i18n.get( "uiR2Test1TaskSuccess" ):toUtf8()},
580 return logicTranslations
584 r2.Features["Quest"] = feature