1394 lines
46 KiB
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
|
||
|
|