khanat-client-data-NeL/data/ryz/ryz_ring/r2_ui_event_handlers.lua

1394 lines
46 KiB
Lua

--------------
--------------
-- GROUPING --
--------------
--------------
-- TODO nico : find a better file organisation for ui & logic handling
-- TODO nico : most of these global function should be made class members
-- TODO nico : some other part are API to the C++ : these would better fit in a separate, dedicated file.
r2.MaxGroupRadius = 20
function r2.checkGroupDistance(instanceId, targetId)
assert(instanceId)
if not targetId then targetId = instanceId end
assert(targetId)
local target = r2:getInstanceFromId(targetId)
local npc = r2:getInstanceFromId(instanceId)
local group = target.ParentInstance
assert(group)
-- The two npc are not grouped
if not group:isKindOf("NpcGrpFeature") then
local leader = target
local npcPosX = npc.Position.x
local npcPosY = npc.Position.y
local leaderPosX = leader.Position.x
local leaderPosY = leader.Position.y
local x = (npcPosX - leaderPosX)^2 --* (npcPosX - leaderPosX)
local y = (npcPosY - leaderPosY)^2 --* (npcPosY - leaderPosY)
local dist = math.sqrt(x + y)
if dist > r2.MaxGroupRadius then return false end
return true
end
local size = table.getn(group.Components)
local i = 0
for i = 0, size-1 do
local leader = group.Components[i]
if npc and leader and npc.Position and leader.Position then
local npcPosX = npc.Position.x
local npcPosY = npc.Position.y
local leaderPosX = leader.Position.x
local leaderPosY = leader.Position.y
local x = (npcPosX - leaderPosX)^2 --* (npcPosX - leaderPosX)
local y = (npcPosY - leaderPosY)^2 --* (npcPosY - leaderPosY)
local dist = math.sqrt(x + y)
if dist > r2.MaxGroupRadius then return false end
end
end
return true
end
function r2.checkLeaderDistAndUngroup(instanceId)
local ok = r2.checkGroupDistance(instanceId)
if not ok then r2:ungroup(movedNpc) end
end
------------------------------------------------------------------------------------
-- test if an object can be grouped with 'target'
-- 'src' must be a an entity
-- 'target' may be another entity or a group
function r2:testCanGroup(src, target)
if target:isSameObjectThan(src) then return false end -- can't group itself
if src:getCategory() ~= target:getCategory() or
src:getSubCategory() ~= target:getSubCategory()
then
return false
end
if target:isKindOf("Npc") then
if target:isBotObject() or target:isPlant() then return false end -- can't group bot objects together
-- if instance is in a group, then can't goup with an instance of the same group
if not src:isInDefaultFeature() then
return target.ParentInstance ~= src.ParentInstance
end
if r2.checkGroupDistance(src.InstanceId, target.InstanceId) == false then return false end
return true
elseif target:isKindOf("NpcGrpFeature") then
-- can group if src is not already in the same group
if not src:isInDefaultFeature() then
return target ~= src.ParentInstance
end
local leader = r2:getInstanceFromId(target.Components[0].InstanceId)
assert(leader)
if r2.checkGroupDistance(src.InstanceId, leader.InstanceId) == false then return false end
return true
end
end
function r2:testCanGroupSelectedInstance(targetInstanceId)
return r2:testCanGroup(r2:getSelectedInstance(), r2:getInstanceFromId(targetInstanceId))
end
------------------------------------------------------------------------------------
-- group an entity with another entity or group
-- 'src' must be a an entity
-- 'target' may be another entity or a group
function r2:group(src, target)
r2.requestNewAction(i18n.get("uiR2EDGroupAction"))
if target:getParentAct() ~= src:getParentAct() then
src:togglePermanentCurrentAct(false)
end
--debugInfo(colorTag(0, 255, 255) .. "Grouping instance " .. src.InstanceId .. " with instance or group " .. target.InstanceId)
-- remove previous behavior (when grouped, only the group has a behavior, not his members)
-- if target has no group then create a new group
local targetGroup
if target:isKindOf("NpcGrpFeature") then
targetGroup = target
-- recompute relative position
src:requestMakePosRelativeTo(targetGroup)
elseif target:isInDefaultFeature() then
assert(target:isKindOf("Npc"))
--r2.requestSetNode(target.Behavior.InstanceId, "Type", "")
-- create a new group
-- debugInfo(colorTag(255, 255, 0) .. "Creating new group")
local newGroup = r2.newComponent("NpcGrpFeature")
local uc_groupName = ucstring()
uc_groupName:fromUtf8(r2.PaletteIdToGroupTranslation[src.Base].. " " .. i18n.get("uiR2EDNameGroup"):toUtf8())
local groupName = r2:genInstanceName(uc_groupName):toUtf8()
newGroup.Name = groupName
--r2.requestSetNode(target.Behavior.InstanceId, "Type", "")
--newGroup.Behavior.Type = src.Behavior.Type
--newGroup.Behavior.ZoneId = src.Behavior.ZoneId
__tmpNewGroup = newGroup
--runCommand("luaObject", "__tmpNewGroup", maxDepth)
__tmpNewGroup = nil
-- create new group
--r2.requestInsertNode(src:getParentAct().InstanceId, "Features", -1, "", newGroup)
r2.requestInsertNode(target:getParentAct().InstanceId, "Features", -1, "", newGroup)
-- move the target instance into that group
r2.requestMoveNode(target.InstanceId, "", -1, newGroup.InstanceId, "Components", -1)
targetGroup = newGroup
else
--debugInfo(colorTag(255, 0, 255) .. "Group already created")
targetGroup = target.ParentInstance
-- recompute relative position
src:requestMakePosRelativeTo(targetGroup)
end
-- is src is in a group then ungroup before doing the move
if not src:isInDefaultFeature() then
r2:ungroup(src)
end
-- nico patch : locaaly mark the entity so that it can't be grouped again before net msg is received
src.User.Grouped = true
-- force update of the available options
if src == r2:getSelectedInstance() then
r2.ContextualCommands:update()
end
-- move the selected instance into the target group
r2.requestMoveNode(src.InstanceId, "", -1, targetGroup.InstanceId, "Components", -1)
--r2.requestSetNode(src.Behavior.InstanceId, "Type", "")
end
------------------------------------------------------------------------------------
-- group the selected instance with the given instance
function r2:groupSelectedInstance(instanceId)
r2:group(r2:getSelectedInstance(), r2:getInstanceFromId(instanceId))
end
------------------------------------------------------------------------------------
-- ungroup a npc
function r2:ungroup(instance)
r2.requestNewAction(i18n.get("uiR2EDUngroupAction"))
if instance == nil then
debugInfo(colorTag(255, 255, 0) .. "No selected instance")
return
end
local target = instance:getParentAct():getDefaultFeature()
--debugInfo(colorTag(255, 255, 0) .. "Parent group size is " .. tostring(instance.Parent.Size))
if instance.Parent.Size <= 2 then
-- debugInfo(colorTag(255, 255, 0) .. "Empty group left, removing it")
-- this was the last object of the group, so remove the group
local groupId = instance.ParentInstance.InstanceId
local parentTable = instance.Parent -- must keep pointer on parent because parent will be changed
-- a son is moved
for i = parentTable.Size - 1, 0, -1 do
parentTable[i]:requestMakePosRelativeTo(target)
r2.requestMoveNode(parentTable[i].InstanceId, "", -1, instance:getParentAct():getDefaultFeature().InstanceId, "Components", -1)
end
--debugInfo(colorTag(255, 255, 0) .. "request erase node")
r2.requestEraseNode(groupId, "", -1)
else
-- make pos relative to the new target
instance:requestMakePosRelativeTo(target)
r2.requestMoveNode(instance.InstanceId, "", -1, target.InstanceId, "Components", -1)
--give a new Behavior to the degrouped member
--r2.requestSetNode(instance.InstanceId,"Behavior",r2.newComponent("Behavior"))
end
end
------------------------------------------------------------------------------------
-- set instance as the new leader of its group
function r2:setAsGroupLeader(instance)
r2.requestNewAction(i18n.get("uiR2EDSetAsLeaderAction"))
if instance:isInDefaultFeature() then
debugInfo("Instance is not in a group, can't set as leader")
return
end
--local oldLeader = instance.ParentInstance.Components[0]
--if (oldLeader:isSameObjectThan(instance)) then return end
-- copy behaviour from the entity (becomes the group behavior)
--r2:setActivityType(instance.Behavior, oldLeader.Behavior.Type, oldLeader.Behavior.ZoneId)
--r2:setActivityType(oldLeader.Behavior, "", "")
-- put leader in first position of the group
r2.requestMoveNode(instance.InstanceId, "", -1, instance.ParentInstance.InstanceId, "Components", 0)
end
-------------------
-------------------
-- NPC BEHAVIOR --
-------------------
-------------------
------------------------------------------------------------------------------------
function r2:testCanPickZoneForNPC(instanceId)
local targetInstance = r2:getInstanceFromId(instanceId)
--if targetInstance:isKindOf("WayPoint") or targetInstance:isKindOf("RegionVertex") or
-- targetInstance:isKindOf("Region") or targetInstance:isKindOf("Road")
if targetInstance:isKindOf("RegionVertex") or targetInstance:isKindOf("Region") then
return true
end
return false
end
------------------------------------------------------------------------------------
function r2:testCanPickRoadForNPC(instanceId)
local targetInstance = r2:getInstanceFromId(instanceId)
if targetInstance:isKindOf("WayPoint") or targetInstance:isKindOf("Road")
then
return true
end
return false
end
------------------------------------------------------------------------------------
--if instance is a grouped npc, return the leader's behavior
--else, return the npc's behavior
function r2:getBehavior(instance)
return r2:getLeader(instance).Behavior
end
------------------------------------------------------------------------------------
function r2:getLeader (instance)
--local grouped = not instance:isInDefaultFeature()
local grouped = instance:isGrouped()
if grouped == true then
local group = instance.ParentInstance
assert(group ~=nil)
instance = group.Components[0]
elseif instance:isKindOf("NpcGrpFeature") then
instance = instance.Components[0]
end
return instance
end
------------------------------------------------------------------------------------
--the arguments must be verified before calling this function
--function r2:setActivityType(behaviorObject, activity, zoneInstanceId)
-- r2.requestSetNode(behaviorObject.InstanceId, "Type", activity)
-- r2.requestSetNode(behaviorObject.InstanceId, "ZoneId", zoneInstanceId)
--end
------------------------------------------------------------------------------------
--function r2:affectZoneToNPC(npcInstanceId, zoneInstanceId)
-- local npcInstance = r2:getInstanceFromId(npcInstanceId)
-- if npcInstance == nil then
-- debugInfo("No target npc")
-- return
-- end
-- local behavior = r2:getBehavior(npcInstance)
-- local activity
-- local targetInstance = r2:getInstanceFromId(zoneInstanceId)
-- if targetInstance:isKindOf("WayPoint") or targetInstance:isKindOf("RegionVertex") then
-- debugInfo(colorTag(255, 255, 0) .. "Selecting parent from WayPoint of RegionVertex")
-- targetInstance = targetInstance.ParentInstance
-- end
-- r2:blink(targetInstance)
-- if targetInstance:isKindOf("Road") then
-- activity = "follow_route"
-- else
-- activity = "wander"
-- end
-- local act = npcInstance:getParentAct()
-- if act~= nil
-- then
--debugInfo(colorTag(255,0,0).."act class: "..act.Class)
-- else
-- debugInfo(colorTag(255,0,0).."act niiil!! ")
-- end
-- r2:setActivityType(behavior,activity,targetInstance.InstanceId)
--end
function r2:affectZoneToSelectedNPC(zoneInstanceId)
local npcInstanceId = r2:getLeader(r2:getSelectedInstance()).InstanceId
--r2:affectZoneToNPC(npcInstanceId, zoneInstanceId)
r2:setBehaviorToNPC(npcInstanceId, zoneInstanceId, "Wander")
end
function r2:affectRestZoneToSelectedNPC(zoneInstanceId)
local npcInstanceId = r2:getLeader(r2:getSelectedInstance()).InstanceId
--r2:affectZoneToNPC(npcInstanceId, zoneInstanceId)
r2:setBehaviorToNPC(npcInstanceId, zoneInstanceId, "Rest In Zone")
end
function r2:affectFeedZoneToSelectedNPC(zoneInstanceId)
local npcInstanceId = r2:getLeader(r2:getSelectedInstance()).InstanceId
--r2:affectZoneToNPC(npcInstanceId, zoneInstanceId)
r2:setBehaviorToNPC(npcInstanceId, zoneInstanceId, "Feed In Zone")
end
function r2:affectHuntZoneToSelectedNPC(zoneInstanceId)
local npcInstanceId = r2:getLeader(r2:getSelectedInstance()).InstanceId
--r2:affectZoneToNPC(npcInstanceId, zoneInstanceId)
r2:setBehaviorToNPC(npcInstanceId, zoneInstanceId, "Hunt In Zone")
end
function r2:affectGuardZoneToSelectedNPC(zoneInstanceId)
local npcInstanceId = r2:getLeader(r2:getSelectedInstance()).InstanceId
--r2:affectZoneToNPC(npcInstanceId, zoneInstanceId)
r2:setBehaviorToNPC(npcInstanceId, zoneInstanceId, "Guard Zone")
end
------------------------------------------------------------------------------------
function r2:createZoneAndAffectBehaviorToNPC(startX, startY, startZ, primLook, primInvalidLook, primCanCloseLook, affectFunction, activity)
if affectFunction == nil then
dumpCallStack(1)
assert(false)
end
r2.requestNewPendingMultiAction(i18n.get("uiR2EDAddActivityAction"), 3)
-- TODO nico : use co-routine here instead of the callback stuff...
local npcInstanceId = r2:getSelectedInstance().InstanceId
local selectInstance = false
local params =
{
Look = primLook,
InvalidLook = primInvalidLook,
CanCloseLook = primCanCloseLook,
Vertices = { { x = startX, y = startY, z = startZ } }, -- start with single vertex where the user clicked
CookieKey = "CreateFunc", -- special function called by the road/region displayer when it is created
CookieValue = function(primitive)
-- this is a pending action with 3 steps here
affectFunction(r2, npcInstanceId, primitive.InstanceId, activity)
end,
SelectInstance = selectInstance, -- when road or place is created to associate new activity to a NPC,
-- it must not be selected
OnCancel = function()
r2.requestCancelAction()
end
}
if params.CanCloseLook and params.CanCloseLook.Shape == r2.PrimRender.Shape.ClosedPolyLine then
params.Type = "Region"
else
params.Type = "Route"
end
r2:setCurrentTool("R2::CToolDrawPrim", params)
--debugInfo("createZoneAndAffectBehaviorToNPC END")
end
------------------------------------------------------------------------------------
function r2:createZoneAndAffectZoneToNPC(startX, startY, startZ)
r2:createZoneAndAffectBehaviorToNPC(startX, startY, startZ, r2.PrimRender.RegionCreateLook, r2.PrimRender.RegionCreateInvalidLook, r2.PrimRender.RegionCreateCanCloseLook, r2.setBehaviorToNPC, "Wander")
end
function r2:createRestZoneAndAffectZoneToNPC(startX, startY, startZ)
r2:createZoneAndAffectBehaviorToNPC(startX, startY, startZ, r2.PrimRender.RegionCreateLook, r2.PrimRender.RegionCreateInvalidLook, r2.PrimRender.RegionCreateCanCloseLook, r2.setBehaviorToNPC, "Rest In Zone")
end
function r2:createFeedZoneAndAffectZoneToNPC(startX, startY, startZ)
r2:createZoneAndAffectBehaviorToNPC(startX, startY, startZ, r2.PrimRender.RegionCreateLook, r2.PrimRender.RegionCreateInvalidLook, r2.PrimRender.RegionCreateCanCloseLook, r2.setBehaviorToNPC, "Feed In Zone")
end
function r2:createHuntZoneAndAffectZoneToNPC(startX, startY, startZ)
r2:createZoneAndAffectBehaviorToNPC(startX, startY, startZ, r2.PrimRender.RegionCreateLook, r2.PrimRender.RegionCreateInvalidLook, r2.PrimRender.RegionCreateCanCloseLook, r2.setBehaviorToNPC, "Hunt In Zone")
end
function r2:createGuardZoneAndAffectZoneToNPC(startX, startY, startZ)
r2:createZoneAndAffectBehaviorToNPC(startX, startY, startZ, r2.PrimRender.RegionCreateLook, r2.PrimRender.RegionCreateInvalidLook, r2.PrimRender.RegionCreateCanCloseLook, r2.setBehaviorToNPC, "Guard Zone")
end
------------------------------------------------------------------------------------
function r2:setBehaviorToNPC(npcInstanceId, zoneInstanceId, activity)
local npcInstance = r2:getInstanceFromId(npcInstanceId)
if npcInstance == nil then return end
local behavior = r2:getBehavior(npcInstance)
local targetInstance = r2:getInstanceFromId(zoneInstanceId)
if targetInstance:isKindOf("WayPoint") or targetInstance:isKindOf("RegionVertex") then
zoneInstanceId = targetInstance.ParentInstance.InstanceId
end
local act = npcInstance:getParentAct()
if act~= nil
then
--debugInfo(colorTag(255,0,0).."act class: "..act.Class)
else
debugInfo(colorTag(255,0,0).."act niiil!! ")
end
r2.activities:initEditorAfterFirstCall()
local tableInit = {}
tableInit.Activity = activity
tableInit.ActivityZoneId = zoneInstanceId
if activity=="Follow Route" or activity=="Repeat Road" or activity=="Patrol" then
tableInit.TimeLimit = "No Limit"
tableInit.TimeLimitValue = "0"
else
tableInit.TimeLimit = "Few Sec"
tableInit.TimeLimitValue = "20"
end
if activity=="Repeat Road" or activity=="Patrol" then
tableInit.RoadCountLimit = "2"
end
-- if current display mode for prim is 'hide all', then force to 'contextual' at
if r2.PrimDisplayVisible == false then
r2:primDisplayShowContextual()
displaySystemInfo(i18n.get("uiR2EDPrimDisplayModeChangedToContextual"), 'BC')
end
if not r2.activities:newElementInst(tableInit) then return end
end
function r2:setBehaviorToSelectedNPC(zoneInstanceId, activity)
local npcInstanceId = r2:getLeader(r2:getSelectedInstance()).InstanceId
r2:setBehaviorToNPC(npcInstanceId, zoneInstanceId, activity)
end
------------------------------------------------------------------------------------
function r2:setBehaviorFollowRouteToNPC(roadInstanceId)
r2:setBehaviorToSelectedNPC(roadInstanceId, "Follow Route")
end
function r2:createRouteAndSetBehaviorFollowRouteToNPC(startX, startY, startZ)
r2:createZoneAndAffectBehaviorToNPC(startX, startY, startZ, r2.PrimRender.RoadCreateLook, r2.PrimRender.RoadCreateInvalidLook, nil, self.setBehaviorToNPC, "Follow Route")
end
------------------------------------------------------------------------------------
function r2:setBehaviorPatrolRouteToNPC(roadInstanceId)
r2:setBehaviorToSelectedNPC(roadInstanceId, "Patrol")
end
function r2:createRouteAndSetBehaviorPatrolRouteToNPC(startX, startY, startZ)
r2:createZoneAndAffectBehaviorToNPC(startX, startY, startZ, r2.PrimRender.RoadCreateLook, r2.PrimRender.RoadCreateInvalidLook, nil, self.setBehaviorToNPC, "Patrol")
end
------------------------------------------------------------------------------------
function r2:setBehaviorRepeatRoadToNPC(roadInstanceId)
r2:setBehaviorToSelectedNPC(roadInstanceId, "Repeat Road")
end
function r2:createRoadAndSetBehaviorRepeatRoadToNPC(startX, startY, startZ)
r2:createZoneAndAffectBehaviorToNPC(startX, startY, startZ, r2.PrimRender.RoadCreateLook, r2.PrimRender.RoadCreateInvalidLook, nil, self.setBehaviorToNPC, "Repeat Road")
end
------------------------------------------------------------------------------------
function r2:setNPCStandInPlace(instance)
r2.activities:initEditorAfterFirstCall()
local tableInit = {}
tableInit.Activity = "Stand Still"
tableInit.ActivityZoneId = ""
tableInit.TimeLimit = "Few Sec"
tableInit.TimeLimitValue = "20"
if not r2.activities:newElementInst(tableInit) then return end
end
----------------
----------------
-- MENU SETUP --
----------------
----------------
------------------------------------------------------------------------------------
function r2:selectParent(index)
local parent = r2:getSelectedInstance()
for i = 1,index do
parent = parent.ParentInstance
if parent == nil then return end
end
r2:setSelectedInstanceId(parent.InstanceId)
end
-------------------
-------------------
-- COPY AND PASTE --
-------------------
-------------------
------------------------------------------------------------------------------------
-- called by C++ when copy has been pressed (key or ui button)
function r2:copy()
local selection = r2:getSelectedInstance()
if selection == nil then
displaySystemInfo(i18n.get("uiR2EDCantCopyEmptySelection"), "BC")
return
end
if not selection:isCopyable() then
displaySystemInfo(i18n.get("uiR2EDSelectionDoNotSupportCopy"), "BC")
return
end
r2.ClipBoard = selection:copy()
r2.ClipBoardSrcInstanceId = selection.InstanceId
r2.ClipBoardDisplayName = selection:getDisplayName()
displaySystemInfo(concatUCString(selection:getDisplayName(), i18n.get("uiR2EDSelectionCopied")), "BC")
end
------------------------------------------------------------------------------------
-- called by C++ when copy has been pressed (key or ui button)
function r2:paste()
r2.requestNewAction(concatUCString(i18n.get("uiR2EDPasteAction"), r2.ClipBoardDisplayName))
if r2.ClipBoard == nil then
displaySystemInfo(i18n.get("uiR2EDEmptyClipBoard"), "BC")
return
end
-- call the paste function (not a method here)
local newCopy = r2.Classes[r2.ClipBoard.Class].newCopy(r2.ClipBoard)
r2.requestNewAction(concatUCString(i18n.get("uiR2EDPasteAction"), r2.ClipBoardDisplayName))
r2.Classes[r2.ClipBoard.Class].paste(newCopy, true, r2.ClipBoardSrcInstanceId)
r2.requestEndAction()
end
-- tmp tmp for debug
function doublePaste()
debugInfo("1 " .. tostring(r2.UIMainLoop.LeftQuotaModified))
r2:paste()
debugInfo("2 " .. tostring(r2.UIMainLoop.LeftQuotaModified))
r2:paste()
debugInfo("3 " .. tostring(r2.UIMainLoop.LeftQuotaModified))
end
-- tmp tmp for debug
function testPaste()
for k = 1, 99 do
r2:paste()
end
end
--r2.CuttedSelectionId = "" -- instance id for the selection to be cutted
--
------------------------------------------------------------------------------------
--function r2:getCuttedSelection()
-- return r2:getInstanceFromId(self.CuttedSelectionId)
-- end
--------------------------------------------------------------------------------------
--function r2:cut(instance)
-- local oldSelection = r2:getCuttedSelection()
-- if oldSelection and oldSelection.DisplayerUI then
-- oldSelection.DisplayerUI:onCut(oldSelection, false) -- not cut anymore
-- end
-- if instance then
-- r2.CuttedSelectionId = instance.InstanceId
-- else
-- r2.CuttedSelectionId = ""
-- end
-- local newSelection = r2:getCuttedSelection()
-- if newSelection and newSelection.DisplayerUI then
-- newSelection.DisplayerUI:onCut(newSelection, true) -- new instance being cut
-- end
--end
--
--------------------------------------------------------------------------------------
--function r2:paste()
-- local target = r2:getSelectedInstance()
-- local src = r2:getCuttedSelection()
-- assert(target)
-- assert(src)
-- r2:cut(nil)
-- -- check that target accept the paste
-- assert(target.accept)
-- local destArray = target:accept(src)
-- -- do the move
-- if target.insert ~= nil then
-- -- if an 'insert' then use it to do the insertion
-- target:insert(src)
-- else
-- -- else just move the node at the end of the array
-- assert(type(destArray) == "string")
-- r2.requestMoveNode(src.InstanceId, "", -1, target.InstanceId, destArray, -1)
-- end
--end
------------------------
------------------------
-- INSTANCE SELECTION --
------------------------
------------------------
------------------------------------------------------------------------------------
-- called by the framework when an instance is selected
function r2:onSelectInstance(instance)
--local st = nltime.getPreciseLocalTime()
if instance then
instance:onSelect()
-- reset slect bar type
r2.SelectBar.InstancesType = instance:getSelectBarType()
end
if r2.isPropertyWindowVisible() then
r2:showProperties(instance)
end
r2.CustomBBox:updateUI()
r2.ContextualCommands:setupToolbar(instance)
r2.MiniToolbar:setupToolbar(instance)
if not instance then
r2.ContextualCommands:setupMenu(nil)
end
r2.SelectBar:touch()
r2.SelectBar:getBar().active = (instance ~= nil and not instance.Ghost)-- ensure that 'active' is properly set for the select bar
-- (do not wait per frame update)
getUI("ui:interface:r2ed_select_bar"):updateCoords()
getUI("ui:interface:r2ed_contextual_toolbar_new"):updateCoords()
getUI("ui:interface:r2ed_mini_activity_view"):updateCoords()
--local et = nltime.getPreciseLocalTime()
--debugInfo("select instance : " .. tostring(1000 * (et - st)) .." ms")
end
------------------------------------------------------------------------------------
--function r2:showEditOrTestButton(isEdition)
-- if isEdition == nil then
-- debugInfo(debug.traceback())
-- end
-- local editButton = getUI("ui:interface:r2ed_toolbar_admin:r2ed_tool_go_edition")
-- if editButton then editButton.active = not isEdition end
-- local testButton = getUI("ui:interface:r2ed_toolbar_admin:r2ed_tool_go_test")
-- if testButton then testButton.active = isEdition end
--end
------------------------------------------------------------------------------------
--function r2:setEditorMode(isEdition)
-- -- tmp hack here to know what is the current mode
-- if isEdition then
-- r2:setupUIForEdition()
-- else
-- r2:setupUIForTest()
-- end
-- --r2:showEditOrTestButton(isEdition)
-- r2.IsEdition = isEdition
--end
---------------
---------------
-- MAIN MENU --
---------------
---------------
------------------------------------------------------------------------------------
function r2:popMainMenu()
r2:setCurrentTool('')
local menuName = select(r2.Mode == "Edit", "ui:interface:r2ed_main_menu", "ui:interface:r2ed_main_menu_animation")
--runAH(nil, "active_menu", "menu=" .. menuName)
launchContextMenuInGame(menuName)
local menu = getUI(menuName)
assert(menu)
local function buildKeyName(action, params)
local keyName = ucstring(runExpr(string.format("getKey('%s', '%s')", action, params)))
assert(isUCString(keyName))
if keyName == i18n.get("uiNotAssigned") then
-- no associated key...
return keyName
else
local result = concatUCString(ucstring("(") , keyName, ucstring(")"))
--debugInfo(result:toUtf8())
return result
end
end
-- fill menu entries common to edition & test
menu:find("preferences_key").t.uc_hardtext = buildKeyName("show_hide", "game_config")
menu:find("keys_key").t.uc_hardtext = buildKeyName("show_hide", "keys")
--menu:find("debug_console_key").t.uc_hardtext = buildKeyName("show_hide", "debug_info")
menu:find("chat_window_key").t.uc_hardtext = buildKeyName("show_hide", "main_chat")
menu:find("quit_key").t.uc_hardtext = buildKeyName("quit_ryzom_now", "")
menu:find("mail_box_key").t.uc_hardtext = buildKeyName("show_hide", "mailbox")
menu:find("guild_forum_key").t.uc_hardtext = buildKeyName("show_hide", "guild_forum")
-- fill name of key for each menu entry
if r2.Mode == "Edit" then
menu:find("go_test_key").t.uc_hardtext = buildKeyName("r2ed_try_go_test", "")
menu:find("palette_key").t.uc_hardtext = buildKeyName("show_hide", "r2ed_palette")
menu:find("scenario_key").t.uc_hardtext = buildKeyName("show_hide", "r2ed_scenario")
menu:find("cust_bbox").active = (config.R2EDExtendedDebug == 1)
else
menu:find("stop_test_key").t.uc_hardtext = buildKeyName("r2ed_stop_test", "")
menu:find("stop_test").active = (r2.AccessMode == "Editor")
end
if r2.Mode ~= "Edit" then
menu:find("live").active = not (r2.AccessMode == "Editor")
menu:find("stop_live").active = not (r2.AccessMode == "Editor")
menu:find("player_admin").active = not (r2.AccessMode == "Editor")
end
menu:find("map_key").t.uc_hardtext = buildKeyName("show_hide", "map")
menu:find("debug_console").active = (config.R2EDExtendedDebug == 1)
menu:find("fixed_lighting_bm").bm.texture = select(r2:getFixedLighting(), "r2_icon_light_on_small.tga", "r2_icon_light_off_small.tga")
menu:find("toggle_fixed_lighting").hardtext = select(r2:getFixedLighting(), "uiR2EDTurnLightOff", "uiR2EDTurnLightOn")
-- setup position
local scrW, scrH = getWindowSize()
menu:updateCoords()
menu.x = scrW - menu.w_real
menu.y = 28
menu:updateCoords()
end
-- called by main menu in edition mode to go to test mode
function r2:tryGoTest()
-- remove any keyboard focus so that any editbox properties will be updated correctly
resetCaptureKeyboard()
r2.acts.deleteOldScenario = true
r2:defaultUIDisplayer():storeClosedTreeNodes()
if r2:getLeftQuota() < 0 then
messageBox(i18n.get("uiR2EDCantGoTest"))
return
end
-- freeze go test in menu & button
local goTestButton = getUI("ui:interface:r2ed_toolbar"):find("r2ed_tool_start").unselected.button
local goTestMenu = getUI("ui:interface:r2ed_main_menu"):find("go_test")
if not goTestButton.frozen or goTest.grayed then
goTestButton.frozen = true
goTestMenu.grayed = true
runAH(nil, "r2ed_go_test", "")
end
end
------------------------------
------------------------------
-- NO MORE ROOM IN SCENARIO --
------------------------------
------------------------------
-- called by C++ (or lua) to signal that there's no more room to create new entities
function r2:makeRoomMsg()
displaySystemInfo(i18n.get("uiR2EDMakeRoom"), "BC")
end
local instanceTrees = nil -- private to r2:getInstanceFromUIUnderMouse
-- test if mouse is currently over a widget in the ui that represents an entity in the scenario (used by the pick tool)
-- TODO nico : put this in the cpp API !!!
function r2:getInstanceIdFromUIUnderMouse()
-- implemented for the scenario tree only for now ...
-- build pointers on all tree if not already done
if instanceTrees == nil then
instanceTrees = {}
table.insert(instanceTrees, getUI("ui:interface:r2ed_scenario"):find("content_tree_list"))
local maxNumActs = getDefine('r2ed_max_num_additionnal_acts')
for i = 0, r2:getMaxNumberOfAdditionnalActs() - 1 do
table.insert(instanceTrees, getUI("ui:interface:r2ed_scenario"):find("act_tree_" .. tostring(i)))
table.insert(instanceTrees, getUI("ui:interface:r2ed_scenario"):find("macro_act_tree_" .. tostring(i)))
end
end
-- look in all scenario trees ....
for k, v in pairs(instanceTrees) do
if v and v.active then
local node = v:getNodeUnderMouse()
if node then
return node.Id
end
end
end
return nil
end
----------
----------
-- ACTS --
----------
----------
-- called by C++ when a new act has been selected
function r2:onActChanged(previousAct, currentAct)
-- update the scenario window
r2.ScenarioWindow:updateUIFromCurrentAct()
-- update the select bar
r2.SelectBar:touch()
if r2:isScenarioUpdating() == 1 then
return
end
-- tp if change location
local baseAct = r2.Scenario:getBaseAct()
-- change position
if previousAct and currentAct and baseAct then
local previousActInstanceId = tostring(r2.Scenario.User.SelectedActInstanceId);
local previousLocationInstanceId = tostring(r2.Scenario.User.SelectedLocationInstanceId)
local currentActInstanceId = tostring(currentAct.InstanceId)
local currentActLocationInstanceId = tostring(currentAct.LocationId)
if currentAct.InstanceId ~= tostring(baseAct.InstanceId) then
r2.Scenario.User.SelectedActInstanceId = tostring(currentAct.InstanceId)
r2.Scenario.User.SelectedLocationInstanceId = tostring(currentAct.LocationId)
debugInfo("Location id: " ..tostring(currentAct.LocationId))
if ( r2.Scenario.User.SelectedLocationInstanceId ~= previousLocationInstanceId) then
local actIndex = 0
local k,v = next(r2.Scenario.Acts)
while k do
if tostring(v.InstanceId) == tostring(currentAct.InstanceId) then
debugInfo("::BEFORE TP::Location id: " ..tostring(currentAct.LocationId))
r2.requestSetStartingAct(actIndex)
r2.requestTpToEntryPoint(actIndex)
end
actIndex = actIndex + 1
k,v = next(r2.Scenario.Acts, k)
end
end
end
end
end
------------------
------------------
-- EDITOR MODES --
------------------
------------------
-- called by C ++ : cleanup tasks when the editor is released
function r2:onFinalRelease()
r2.DMGift:cancel() -- if a dm gift was current, cancel it
end
-- called by C ++ when current mode of the editor is about to change
function r2:onPreModeChanged()
r2.DMGift:cancel() -- if a dm gift was current, cancel it
end
-- Called by the C++ framework when there's a mode change between editing, dm etc
-- the r2.Mode variable is changed to one of the following:
-- 'Edit' for editing mode
-- 'Test' for test mode
-- 'DM' for DM mode
-- 'GoingToDM' when switching from editing to dm mode (when the transition screen is displayed)
-- 'BackToEditing' when switching from dm/test mode to editing (when the transition screen is displayed)
function r2:onModeChanged()
-- if a form was displayed, just cancel it
if r2.CurrentForm and r2.CurrentForm.active then
r2:cancelForm(r2.CurrentForm)
end
-- if we're back in edition and there's an error message left, display it now
if r2.Mode == "Edit" and r2.LastTranslationErrorMsg ~= nil then
local str = r2.LastTranslationErrorMsg
assert(type(str) == "string")
local ucStringMsg = ucstring("Translation Error")
--ucStringMsg:fromUtf8(r2.LastTranslationErrorMsg)
displaySystemInfo(ucStringMsg, "BC")
messageBox(str)
r2.LastTranslationErrorMsg = nil
end
-- reset the list af acts and triggers until they are received
if mode == "Edit" then
r2.AnimGlobals:reset()
end
end
---------------------------------------------------------------------------------------------
-------------
-------------
-- WEATHER --
-------------
-------------
-- pop the weather edition dialog in test mode
function r2:changeWeatherDialog()
local function onOk(form)
setWeatherValue(true) -- back to auto weather
-- send request message to dss
local newWeatherValue = select(form.ManualWeather == 1, form.WeatherValue + 1, 0)
if newWeatherValue ~= getDbProp("SERVER:WEATHER:VALUE") then
r2.requestSetWeather(newWeatherValue) -- predicted weather
end
--if form.Season ~= getDbProp("SERVER:SEASON:VALUE") then
-- r2.requestSetSeason(form.Season)
--end
end
local function onCancel(form)
setWeatherValue(true) -- back to auto weather
end
local params =
{
WeatherValue = getWeatherValue() * 1022,
ManualWeather = select(getDbProp("SERVER:WEATHER:VALUE") ~= 0, 1, 0),
--Season = getServerSeason()
}
r2:doForm("ChangeWeatherForm", params, onOk, onCancel)
end
-- teleport
-- tpPosition when player click on the minimap
-- Whe can not use the /a Position command because we need admin power to do it
function r2:activeTeleportTool()
local function posOk(x, y, z)
if config.Local == 1 then
runCommand("pos", x, y, z);
else
r2.requestTpPosition(x, y, z)
end
end
local function posCancel()
debugInfo("Cancel teleport pos")
end
r2:choosePos("", posOk, posCancel, "teleport", "r2ed_tool_can_pick.tga", "r2ed_tool_pick.tga")
end
-- teleport
-- tpPosition when player click on the minimap
-- Whe can not use the /a Position command because we need admin power to do it
function r2:teleportToTarget()
local target = r2:getSelectedInstance()
if not target then return end
local pos = r2.getWorldPos(target)
local x = pos.x
local y = pos.y
local z = pos.z
local x2, y2, z2 = r2:getUserEntityPosition()
local dx = (x - x2)^2 --* (npcPosX - leaderPosX)
local dy = (y - y2)^2 --* (npcPosY - leaderPosY)
local dist = math.sqrt(dy + dx)
if dist > 300 then return end
if config.Local == 1 then
runCommand("pos", x, y, z);
else
r2.requestTpPosition(x, y, z)
end
end
---------------------------------------------------------------------------------------------
------------------------
------------------------
-- TOOLS CONTEXT HELP --
------------------------
------------------------
-- call by C++ to change the context help that is displayed for the current tool
function r2:setToolContextHelp(ucHelp)
--debugInfo(tostring(ucHelp))
local helpGroup = getUI("ui:interface:r2ed_tool_context_help")
helpGroup.t.uc_hardtext = ucHelp
helpGroup.t:updateCoords()
helpGroup.w = helpGroup.t.w
helpGroup:invalidateCoords()
end
-- call by C++ to display a message saying that the max number of points has been reached
-- while drawing a primitive
function r2:noMoreRoomLeftInPrimitveMsg()
local keyName = ucstring(runExpr("getKey('r2ed_context_command', 'commandId=delete')"))
local msg = concatUCString(i18n.get("uiR2EDNoMoreRoomInPrimStart"),
keyName,
i18n.get("uiR2EDNoMoreRoomInPrimEnd"))
displaySystemInfo(msg, "TAGBC")
end
---------------------------------------------------------------------------------------------
--------------------
--------------------
-- ANIMATION TIME --
--------------------
--------------------
function r2:updateAnimBarActions(...)
-- forward to the real anim bar
r2.ui.AnimBar:updateActions(arg)
end
---------------------------------------------------------------------------------------------
------------------------
------------------------
-- UNDO / REDO EVENTS --
------------------------
------------------------
-- called by C++ when an action has been undone
function r2:onUndo(actionUCName)
debugInfo("*undo*")
displaySystemInfo(concatUCString(i18n.get("uiR2EDUndoing"), actionUCName), "BC")
r2.ToolUI:updateUndoRedo()
end
-- called by C++ when an action has been redone
function r2:onRedo(actionUCName)
debugInfo("*redo*")
displaySystemInfo(concatUCString(i18n.get("uiR2EDRedoing"), actionUCName), "BC")
r2.ToolUI:updateUndoRedo()
end
-- called by C++ when a new action has been added in the action historic
function r2:onNewActionAddedInHistoric()
debugInfo("*new action*")
r2.ToolUI:updateUndoRedo()
end
-- called by C++ when an action new pending action has begun
function r2:onCancelActionInHistoric()
r2.ToolUI:updateUndoRedo()
end
-- called by C++ when a new pending action has begun
function r2:onPendingActionBegin()
r2.ToolUI:updateUndoRedo()
end
-- called by C++ when an undo was attempted but failed because there were no actions left in the historic
function r2:onCantUndo(actionUCName)
displaySystemInfo(i18n.get("uiR2EDCantUndo"), "BC")
end
-- called by C++ when a redo was attempted but failed because there are no more actions at the end of the historic
function r2:onCantRedo(actionUCName)
displaySystemInfo(i18n.get("uiR2EDCantRedo"), "BC")
end
-- called by C++ when the action historic has been cleared
-- this may happen after scenario version update
function r2:onClearActionHistoric()
r2.ToolUI:updateUndoRedo()
end
---------------------------------------------------------------------------------------------
------------------------
------------------------
-- DISPLAY MODE MENU --
------------------------
------------------------
-- call by the toolabr button to pop the 'display mode' menu
function r2:popDisplayModeMenu()
local menuName = "ui:interface:r2ed_primitive_display"
local menu = getUI(menuName)
launchContextMenuInGame(menuName)
-- setup position
local parentIcon = getUI("ui:interface:r2ed_toolbar:r2ed_tool_display_mode")
local scrW, scrH = getWindowSize()
--
menu:find("freeze_all").grayed = not r2.PrimDisplayVisible
menu:find("freeze_all").checked = r2.PrimDisplayFrozen
menu:find("unfreeze_all").grayed = not r2.PrimDisplayVisible
menu:find("unfreeze_all").checked = not r2.PrimDisplayFrozen
menu:find("hide_all").checked = not r2.PrimDisplayVisible
menu:find("show_contextual").checked = (r2.PrimDisplayVisible and r2.PrimDisplayContextualVisibility)
menu:find("show_all").checked = (r2.PrimDisplayVisible and not r2.PrimDisplayContextualVisibility)
--
menu:setPosRef("TL TL")
menu:updateCoords()
menu.x = parentIcon.x_real
menu.y = parentIcon.y_real - scrH
menu:updateCoords()
end
local scratchDisplayModeTableSrc = {} -- 'reserve' for list of objects we want to change display mode
local scratchDisplayModeTableDest = {}
-- reset the display mode to 'Visibl' for all objects in all acts
function r2:resetDisplayModeForAllObjects()
local objList = scratchDisplayModeTableSrc -- alias
table.clear(objList)
r2.Scenario:getSons(objList)
r2.requestNewAction(i18n.get("uiR2EDChangeDisplayAction"))
for k, v in pairs(objList) do
if v:isKindOf("WorldObject") then
v.DisplayerVisual.DisplayMode = 0
end
end
end
----------------------------------------------------------------
-- primitive display mode menu handlers
-- current display mode for primitive
-- NB : these flags may be accessed by C++ when a new route or zone is drawn, to switch 'to display all' mode
-- which isdone by calling 'r2:primDisplayShowAll'
r2.PrimDisplayMode = 0 -- Visible
-- is contextual selection on for primitives ?
r2.PrimDisplayFrozen = false
r2.PrimDisplayVisible = true
r2.PrimDisplayContextualVisibility = false
-- 0, Visible
-- 1, Hidden
-- 2, Frozen
-- 3, Locked
function r2:getPrimDisplayMode()
if not r2.PrimDisplayVisible then
return 1
else
if r2.PrimDisplayFrozen then
return 2
else
return 0
end
end
end
function r2:updatePrimDisplayMode()
r2:setDisplayMode("places", r2:getPrimDisplayMode(), r2.PrimDisplayContextualVisibility)
end
function r2:primDisplayFreezeAll()
r2.PrimDisplayFrozen = true
r2:updatePrimDisplayMode()
end
--
function r2:primDisplayUnfreezeAll()
r2.PrimDisplayFrozen = false
r2:updatePrimDisplayMode()
end
--
function r2:primDisplayHideAll()
r2.PrimDisplayVisible = false
r2.PrimDisplayContextualVisibility = false
r2:updatePrimDisplayMode()
end
--
function r2:primDisplayShowContextual()
r2.PrimDisplayVisible = true
r2.PrimDisplayContextualVisibility = true
r2:updatePrimDisplayMode()
end
--
function r2:primDisplayShowAll()
r2.PrimDisplayVisible = true
r2.PrimDisplayContextualVisibility = false
r2:updatePrimDisplayMode()
end
--
function r2:notifyPrimDisplayShowAll()
displaySystemInfo(i18n.get("uiR2EDPrimDisplayModeChangedToShowAll"), 'BC')
r2:primDisplayShowAll()
end
-- change display mode for all objects of the given category
-- filter may be :
-- 'all'
-- 'places' : roads and regions
-- "bot_objects' : pieces of furniture, buildings ...
function r2:setDisplayMode(filter, displayMode, contextualVisibility)
local objList = scratchDisplayModeTableSrc -- alias
local finalObjList = scratchDisplayModeTableDest -- alias
table.clear(objList)
table.clear(finalObjList)
-- operate in all acts
for k, v in specPairs(r2.Scenario.Acts) do
v:getSons(objList)
end
for k,v in pairs(objList) do
if v:isKindOf("WorldObject") then
if filter == "all" then
if v:canChangeDisplayMode() then
table.insert(finalObjList, v)
end
elseif filter == "places" then
if v:isKindOf("Region") or v:isKindOf("Road") then
table.insert(finalObjList, v)
end
elseif filter == "bot_objects" then
if v:isKindOf("Npc") and v:isBotObject() then
table.insert(finalObjList, v)
end
end
end
end
if table.getn(finalObjList) == 0 then
--displaySystemInfo(i18n.get("uiR2EDNoTargetForDisplayModeChange"), "BC")
return
end
-- change display mode only for object that need it
--r2.requestNewAction(i18n.get("uiR2EDChangeDisplayAction"))
for k, v in pairs(finalObjList) do
v.DisplayerVisual.DisplayMode = displayMode
if v:isKindOf("BasePrimitive") then
v.DisplayerVisual.ContextualVisibilityActive = contextualVisibility
end
--if v.DisplayMode == srcDisplayMode or (srcDisplayMode == nil and v.DisplayMode ~= displayModeValue) then
--debugInfo("Changing display mode for " .. v:getDisplayName():toUtf8())
--r2.requestSetNode(v.InstanceId, "DisplayMode", displayModeValue)
-- now a local property
--end
end
end
r2.BotObjectsFrozen = false
function r2:setupFreezeBotObjectButton()
getUI("ui:interface:r2ed_toolbar"):find("r2ed_freeze_bot_objects").active = not r2.BotObjectsFrozen
getUI("ui:interface:r2ed_toolbar"):find("r2ed_unfreeze_bot_objects").active = r2.BotObjectsFrozen
end
function r2:freezeUnfreezeBotObjects()
if r2.BotObjectsFrozen then
r2:unfreezeBotObjects()
else
r2:freezeBotObjects()
end
end
function r2:freezeBotObjects()
r2.BotObjectsFrozen = true
r2:setDisplayMode("bot_objects", 2, false)
r2:setupFreezeBotObjectButton()
end
function r2:unfreezeBotObjects()
r2.BotObjectsFrozen = false
r2:setDisplayMode("bot_objects", 0, false)
r2:setupFreezeBotObjectButton()
end
function r2:setUndoRedoInstances(instanceId)
if instanceId then
r2.logicComponents.undoRedoInstances[tostring(instanceId)] = true
end
end
---
--Called by C++ when upload message are send
--
local lastMessage = {MessageName="", Size=0, Received=0 }
function r2:onMessageSendingStart(messageName, nbPacket, size)
debugInfo("--- Start "..messageName.. " " .. tostring(nbPacket).. " " ..tostring(size))
lastMessage.MessageName = messageName
lastMessage.Size = size
lastMessage.Received =0
local msg = nil
if lastMessage.MessageName == "SSUA" then
msg = i18n.get("uiR2EDUploadScenario"):toUtf8()
elseif lastMessage.MessageName == "RSS2" then
msg = i18n.get("uimR2EDGoToDMMode"):toUtf8()
else
return
end
local ui = getUI("ui:interface:r2ed_uploading_bar")
if not ui then
return
end
local bar = ui:find("uploading_bar")
if not bar then
return
end
bar.hardtext = msg .. " 0%"
ui.active = true
end
function r2:onMessageSendingUpdate(packetId, packetSize)
debugInfo("--- Update " .. tostring(packetId) .. " " .. tostring(packetSize) )
lastMessage.Received = lastMessage.Received + packetSize
if lastMessage.Size and lastMessage.Size ~= 0 then
debugInfo("--- " .. tostring( math.floor( (100*lastMessage.Received) / lastMessage.Size) ) )
if lastMessage.MessageName == "SSUA" then
msg = i18n.get("uiR2EDUploadScenario"):toUtf8()
elseif lastMessage.MessageName == "RSS2" then
msg = i18n.get("uimR2EDGoToDMMode"):toUtf8()
else
return
end
local ui = getUI("ui:interface:r2ed_uploading_bar")
if not ui then
return
end
local bar = ui:find("uploading_bar")
if not bar then
return
end
bar.hardtext = msg .. " ".. tostring( math.floor( (100*lastMessage.Received) / lastMessage.Size ) ) .."%"
ui.active = true
end
end
function r2:onMessageSendingFinish()
debugInfo("--- Finish ")
debugInfo(lastMessage.MessageName)
local ui = getUI("ui:interface:r2ed_uploading_bar")
if not ui then
return
end
local bar = ui:find("uploading_bar")
if not bar then
return
end
bar.hardtext = ""
ui.active = true
end
-- called by C++ to signal that there are too many entity displayed simultaneously
function r2:setMaxVisibleEntityExceededFlag(tooMany)
local msgGroup = getUI("ui:interface:r2ed_max_visible_entity_count_exceeded")
msgGroup.active = tooMany
if tooMany then
msgGroup.t:updateCoords()
msgGroup.w = msgGroup.t.w
msgGroup:invalidateCoords()
end
end