-- -- File:: pipeline/util/notenodes.ms -- Description:: Common functions and globals for note node editing -- -- Author:: Adam Munson -- Date:: 28/04/11 -- ----------------------------------------------------------------------------- global RsNoteNodeCallbackActive = false unregisterRedrawViewsCallback RSnoteNodeRedrawCallback global RsBugNodeAutoUpdate = true global RsBugNodeAutoWarp = true fn RSnoteNodeRedrawCallback = ( ::RSnoteNodeRedrawFunc() ) struct RsNoteNodeSettingsStruct ( coloursList = #( Blue, -- Blue Green, -- Green (color 128 0 128), -- Purple Yellow, -- Yellow Orange, -- Orange Brown, -- Brown Black -- Black ), typeNames = #( "Point", "Link (Start)", "Box (Start)", "Arrow (Start)", "P_Standing" ), fn getBugColour bugClass = ( case bugClass of ( "A":(red) "B":(yellow) "C":(orange) "D":(brown) "TASK":(blue) "TODO":(green) default:(red + blue) ) ), type_point = 1, type_link = 2, type_box = 3, type_arrow = 4, type_scalejaxx = 5, setTotal = 5, nodeTotal = 90, textLength = 255, -- 255 to allow for /0 in game humanMesh, selPointMesh, bugMesh, defaultNode = (createInstance ::RsNoteNode) ) global RsNoteNodeSettings global RsNoteNodeGroup -- Array to store all the sets and nodes from one notenode xml file global RsNoteNodeSet global RsNoteNodeNum global RsNoteNodeCreateLink global RsNoteNodeImporting -- -- fn: RsNoteNodesResetGlobals -- desc:Resets the globals used, to default values -- fn RsNoteNodesResetGlobals = ( RsNoteNodeSettings = RsNoteNodeSettingsStruct() -- Set defaultNode to allow access to node's default params: RsNoteNodeSettings.defaultNode = createInstance RsNoteNode RsNoteNodeGroup = #() for s = 1 to RsNoteNodeSettings.setTotal do ( append RsNoteNodeGroup #() ) RsNoteNodeSet = 1 RsNoteNodeNum = 1 RsNoteNodeCreateLink = false RsNoteNodeImporting = false ) -- -- fn RsNoteNodeCheckIfGroupExists -- desc Checks to see if a populated node group exists -- fn RsNoteNodeCheckIfGroupExists = ( local retVal = false if (RsNoteNodeGroup != undefined) do ( for grp in RsNoteNodeGroup while not retVal do ( for item in grp where (item != undefined) while not retVal do ( retVal = true ) ) ) return retVal ) -- -- fn: RsNoteNodesDeleteGroup -- desc:Deletes any note nodes currently loaded and resets array -- fn RsNoteNodesDeleteGroup = ( clearSelection() -- Delete the old note node group if one is previously loaded if ( true == RsNoteNodeCheckIfGroupExists() ) do ( for s = RsNoteNodeGroup.count to 1 by -1 do ( for nn = RsNoteNodeGroup[s].count to 1 by -1 do ( if ( RsNoteNodeGroup[s][nn] != undefined ) do ( if ( isValidNode RsNoteNodeGroup[s][nn] ) do ( delete RsNoteNodeGroup[s][nn] ) ) ) ) ) RsNoteNodesResetGlobals() ::RsNoteNodesFilterRoll.RsUpdateBlocksList() ::RsNoteNodesSetRoll.spnSetNumber.value = RsNoteNodeSet ) -------------------------------- -- NOTE-NODE OBJECT -- -------------------------------- plugin helper RsNoteNode name:"RsNoteNode" category:"RS Utils" replaceUI:true extends:dummy classID:#(0x59c69cf3, 0x589eabdc) ( local meshObj, isBugDelegate parameters main rollout:params ( nodeLabel type:#string default:"Node" nodeLink type:#node ui:btnLinkPick nodeType type:#integer default:1 ui:lstNodeType nodeSize type:#worldUnits default:1.65 ui:spnNodeSize colourIdx type:#integer default:1 blockName type:#string default:"" setNumber type:#integer default:-1 nodeNumber type:#integer default:-1 ) rollout params "Note Node" ( editText txtLabel "" labelOnTop:true width:156 offset:[-10,0] dotNetControl txtData "MaxCustomControls.MaxTextBox" width:txtLabel.width height:100 pos:(txtLabel.pos.x + [0, 21]) local clrBtnSize = 19 label clrLbl "Colour:" align:#left offset:[0,-3] checkButton clrBtn1 "" width:clrBtnSize height:clrBtnSize across:7 checkButton clrBtn2 "" width:clrBtnSize height:clrBtnSize checkButton clrBtn3 "" width:clrBtnSize height:clrBtnSize checkButton clrBtn4 "" width:clrBtnSize height:clrBtnSize checkButton clrBtn5 "" width:clrBtnSize height:clrBtnSize checkButton clrBtn6 "" width:clrBtnSize height:clrBtnSize checkButton clrBtn7 "" width:clrBtnSize height:clrBtnSize local clrBtns = #(clrBtn1, clrBtn2, clrBtn3, clrBtn4, clrBtn5, clrBtn6, clrBtn7) listbox lstNodeType "Type:" items:RsNoteNodeSettings.typeNames height:RsNoteNodeSettings.typeNames.count spinner spnNodeSize "Size (Radius):" range:[0.5, 100, 0] fn pickFilter obj = ( -- Don't allow dependency loops: (isKindOf obj RsNoteNode) and (not refs.dependencyLoopTest obj (refs.dependentNodes This)[1]) ) groupBox grpLinks "Node Links:" width:152 height:110 offset:[-8,0] label lblLinkPick "Linked to:" align:#left offset:[0, -grpLinks.height + 13] pickButton btnLinkPick "None" autoDisplay:true width:140 offset:[-1,0] tooltip:"Rightclick to clear" label lblLinkShow "" width:140 height:46 align:#left fn setNoteText newText = ( if (newText == "") then ( txtData.visible = false txtData.pos.y -= 400 -- Move control up out of the way, as it can interfere with clicking even when hidden for n = ((findItem params.controls txtData) + 1) to params.controls.count do ( params.controls[n].pos.y -= txtData.height ) -- Set rollout size, if need be: local lastCtrl = params.controls[params.controls.count] local newHeight = lastCtrl.pos.y + 54 if params.height != newHeight do params.height = newHeight ) else ( if (not txtData.visible) do ( txtData.visible = true for n = ((findItem params.controls txtData) + 1) to params.controls.count do ( params.controls[n].pos.y += txtData.height ) params.height += txtData.height ) -- DotNet textbox needs \r as well as \n for newlines: txtData.text = replace_LF_with_CRLF newText ) ) fn updateCtrls = ( local showText = stringstream "" -- Show any non-default node-settings: if (blockName != "") do ( format "blockName: %\n" blockName to:showText ) if (nodeNumber != -1) do ( format "nodeNumber: %\n" nodeNumber to:showText ) if (setNumber != -1) do ( format "setNumber: %\n" setNumber to:showText ) if (filePos showText != 0) do ( format "\n" nodeText to:showText ) setNoteText (showText as string) -- Update link-label: local newText = "Linked from " local foundLink = false local thisNode = (refs.dependentNodes This)[1] if (thisNode != undefined) do ( local nodeDeps = for obj in (refs.dependentNodes thisNode) where (isKindOf obj RsNoteNode) collect obj if (nodeDeps.count != 0) do ( foundLink = true append newText (nodeDeps.count as string + " nodes:\n") for dep in nodeDeps do ( append newText (dep.name + "\n") ) ) ) if not foundLink do ( append newText "0 nodes" ) lblLinkShow.text = newText ) on lstNodeType selected num do ( meshObj = undefined ) on spnNodeSize changed val do ( meshObj = undefined --delegate.size = nodeSize * 0.5 ) -- Clear link-node on rightclick on btnLinkPick rightclick do ( nodeLink = undefined ) fn pressedClrBtn num = ( undo "change node colour" on ( clrBtns.checked = false clrBtns[num].checked = true colourIdx = num ) ) on clrBtn1 changed state do (pressedClrBtn 1) on clrBtn2 changed state do (pressedClrBtn 2) on clrBtn3 changed state do (pressedClrBtn 3) on clrBtn4 changed state do (pressedClrBtn 4) on clrBtn5 changed state do (pressedClrBtn 5) on clrBtn6 changed state do (pressedClrBtn 6) on clrBtn7 changed state do (pressedClrBtn 7) on txtLabel changed newText do ( if (newText.count > RsNoteNodeSettings.textLength) do ( newText = substring newText 1 RsNoteNodeSettings.textLength txtLabel.text = newText nodeLabel = newText messageBox ("Entered text was bigger than " + (RsNoteNodeSettings.textLength as string) + " characters, the end has been removed") title:"Label too long" ) ) on txtLabel entered newText do ( undo "change node name" on ( nodeLabel = newText ) ) on params open do ( txtLabel.text = nodeLabel -- Set up dotNet textbox (which allows for easy wordwrapping) local textClr = (colorMan.getColor #windowText) * 255 local windowClr = (colorMan.getColor #window) * 255 txtData.foreColor = (dotNetClass "System.Drawing.Color").FromArgb textClr[1] textClr[2] textClr[3] txtData.backColor = (dotNetClass "System.Drawing.Color").FromArgb windowClr[1] windowClr[2] windowClr[3] txtData.readOnly = true txtData.multiline = true txtData.scrollbars = txtData.ScrollBars.vertical txtData.wordWrap = true txtData.SelectionStart = 0 txtData.SelectionLength = 0 txtData.readonly = true -- Paint the colour-buttons: local blankBmp = bitmap clrBtnSize clrBtnSize local clrList = RsNoteNodeSettings.coloursList local clrSize = clrBtnSize - 4 for n = 1 to clrBtns.count do ( clrBtns[n].images = #(bitmap clrSize clrSize color:clrList[n], blankBmp, 1,1,1,1,1) ) clrBtns[colourIdx].checked = true updateCtrls() ) ) -- Don't allow instancing, as it buggers up path-drawing: fn deInstance = ( local deps = refs.dependentNodes This if (deps.count > 1) do ( InstanceMgr.MakeObjectsUnique deps #individual ) ) on update do ( deInstance() meshObj = undefined isBugDelegate = undefined -- Change something in the paramblock to force a redraw nodeLabel = nodeLabel ) on detachedFromNode nodeToDelete do ( -- Callback for when a node is deleted from the scene. We have to -- go through all the nodes to check if there is a link to the node -- and if so, remove that but then also shift down all the indexes to nodes -- after it in the array if ( RsNoteNodeGroup != undefined ) do ( if ( true == RsNoteNodeCheckIfGroupExists() ) do ( nodeIdx = -1 for nn = 1 to RsNoteNodeGroup[setNumber].count do ( if ( RsNoteNodeGroup[setNumber][nn] == nodeToDelete ) do ( nodeIdx = nn ) ) -- Remove node from array, shrinking it Assert ( nodeIdx > 0 ) message:"nodeIdx is not above 0 in RsNoteNodesDeleteNode" deleteitem RsNoteNodeGroup[setNumber] nodeIdx if ( 1 != RsNoteNodeNum ) do ( RsNoteNodeNum -= 1 ) ) ) ) on attachedToNode newNode do ( -- Check global to make sure we're not in middle of importing -- as we don't need to do this with if ((isKindOf newNode RsNoteNode) and (RsNoteNodeImporting == false)) do ( if (RsNoteNodesUtil != undefined) and RsNoteNodesUtil.open and (RsNoteNodeGroup != undefined) and (RsNoteNodeNum != undefined) do ( if ( RsNoteNodeNum <= RsNoteNodeSettings.nodeTotal ) then ( newNode.setNumber = RsNoteNodeSet newNode.nodeNumber = RsNoteNodeNum append RsNoteNodeGroup[RsNoteNodeSet] newNode --format "Set:%\tNodeNum:%\n" newNode.setNumber newNode.nodeNumber RsNoteNodeNum += 1 ) else ( Messagebox ( "This set is full (" + ( RsNoteNodeSettings.nodeTotal as string ) + \ "), you cannot make any more nodes unless you delete some" ) ) ) ) ) on getDisplayMesh do ( deInstance() local thisNode = (refs.dependentNodes this)[1] if (isBugDelegate == undefined) do (isBugDelegate = (isKindOf thisNode ::RsBugNode)) local showMesh local nodeColour = thisNode.wirecolor if (RsNoteNodeSettings.selPointMesh == undefined) do ( local newMesh = (createInstance Plane).mesh local selSize = 0.01 setMesh newMesh vertices:#([-selSize,-selSize,0], [selSize,-selSize,0], [selSize,selSize,0],[-selSize,selSize,0]) faces:#([1,2,3], [2,3,4]) for faceNum = 1 to 2 do ( for edgeNum = 1 to 2 do ( setEdgeVis newMesh faceNum edgeNum false ) ) RsNoteNodeSettings.selPointMesh = newMesh ) if isBugDelegate then ( -- Set bug-colours: nodeColour = RsNoteNodeSettings.getBugColour thisNode.bugClass -- Set bug-mesh: if (RsNoteNodeSettings.bugMesh == undefined) do ( RsNoteNodeSettings.bugMesh = manip.makeSphere [0,0,0] 4.0 4 meshOp.attach RsNoteNodeSettings.bugMesh RsNoteNodeSettings.selPointMesh ) showMesh = RsNoteNodeSettings.bugMesh ) else ( -- Get node-colour by index: nodeColour = RsNoteNodeSettings.coloursList[this.colourIdx] -- Draw appropriate mesh/marker for node-type: case this.nodeType of ( (RsNoteNodeSettings.type_point): ( showMesh = RsNoteNodeSettings.selPointMesh ) (RsNoteNodeSettings.type_link): ( if (meshObj == undefined) do ( meshObj = manip.makeSphere [0,0,0] nodeSize 8 meshOp.attach meshObj RsNoteNodeSettings.selPointMesh ) showMesh = meshObj ) (RsNoteNodeSettings.type_arrow): ( if (meshObj == undefined) do ( meshObj = manip.makeSphere [0,0,0] nodeSize 8 meshOp.attach meshObj RsNoteNodeSettings.selPointMesh ) showMesh = meshObj ) (RsNoteNodeSettings.type_box): ( drawLink = false if (nodeLink != undefined) and (isValidNode nodeLink) then ( local posDiff = nodeLink.pos - thisNode.pos showMesh = manip.makeBox (0.5 * posDiff) posDiff.y posDiff.x posDiff.z 1 1 1 -- Box resists node-rotation: rotate showMesh thisNode.rotation ) else ( showMesh = RsNoteNodeSettings.selPointMesh ) ) (RsNoteNodeSettings.type_scalejaxx): ( if (RsNoteNodeSettings.humanMesh == undefined) do ( RsNoteNodeSettings.humanMesh = (createInstance ::P_Standing).mesh meshOp.attach RsNoteNodeSettings.humanMesh RsNoteNodeSettings.selPointMesh ) showMesh = RsNoteNodeSettings.humanMesh ) default: ( if (meshObj == undefined) do ( meshObj = manip.makeSphere [0,0,0] nodeSize 4 meshOp.attach meshObj RsNoteNodeSettings.selPointMesh ) showMesh = meshObj ) ) ) -- Set node-colours: if (thisNode.wirecolor != nodeColour) do ( thisNode.wirecolor = nodeColour ) -- Start up the redraw callback, if it's not already active: if not RsNoteNodeCallbackActive do ( RsNoteNodeCallbackActive = true unregisterRedrawViewsCallback RSnoteNodeRedrawCallback registerRedrawViewsCallback RSnoteNodeRedrawCallback ) return showMesh ) tool create ( on mousePoint click do ( nodeTM.translation = gridPoint #stop ) ) -- tool create ) ------------------------------ -- BUG-NODE OBJECT -- ------------------------------ plugin helper RsBugNode name:"RsBugNode" category:"RS Utils" classID:#(0x56816126, 0x67f4c0d3) extends:RsNoteNode autoPromoteDelegateProps:true replaceUI:true ( parameters main rollout:params ( nodeText type:#string default:"" bugId type:#string default:"" bugClass type:#string default:"" bugComponent type:#string default:"" bugSummary type:#string default:"" tagList type:#stringTab animatable:false tabSizeVariable:true ) -- Used to store bug-edits, for deferred server-submit: local pendingChanges fn setNodeLabel = ( local newLabel = bugClass + ": " + bugId if (delegate.nodelabel != newLabel) do ( delegate.nodelabel = newLabel ) ) fn selNextPrev prev:true justEdited:false = ( local bugNodes = for obj in helpers where (isKindOf obj RsBugNode) and ((not justEdited) or (obj.pendingChanges != undefined)) collect obj local thisNodeNum = findItem bugNodes $ local increment = if prev then -1 else 1 local selNum = thisNodeNum + increment case of ( (selNum < 1):(selNum = bugNodes.count) (selNum > bugNodes.count):(selNum = 1) ) select bugNodes[selNum] DisableSceneRedraw() HelpersVisibilityState = HideByCategory.Helpers HideByCategory.Helpers = false max tool zoomextents HideByCategory.Helpers = HelpersVisibilityState EnableSceneRedraw() select bugNodes[selNum] ) rollout params "Bug Node" ( local ctrlWidth = 154 local halfWidth = (ctrlWidth / 2 - 2) button btnBugToolkit "Max Bugstar Toolkit" width:ctrlWidth offset:[-1,-2] button btnSelPrevBug "<- Prev Bug" width:halfWidth offset:[-7,0] across:2 button btnSelNextBug "Next Bug ->" width:halfWidth offset:[4,0] --button btnEditBug "Edit Bug" offset:[-10,0] width:halfWidth align:#left across:2 tooltip:"Make changes to bug on server" button btnCommitPending "Submit Edits" enabled:false tooltip:"Submit pending bug-edits to server" width:ctrlWidth checkbutton chkbtnWarpPlayer "Auto Warp Player to location" align:#left offset:[-10,0] height:18 checked:RsBugNodeAutoWarp button btnOpenClient "Show in Bugstar Client" width:ctrlWidth offset:[-2,0] button btnUpdate "Update BugNode" align:#left offset:[-10,0] height:18 across:2 checkbox chkAutoUpdate "Auto" offset:[3,2] align:#right checked:RsBugNodeAutoUpdate dotNetControl txtData "MaxCustomControls.MaxTextBox" width:ctrlWidth height:300 offset:[-10,0] fn setNoteText newText = ( txtData.text = replace_LF_with_CRLF (RsWordWrap newText 130) ) fn updateCtrls = ( btnOpenClient.enabled = (bugId != "") setNoteText nodeText local pendingCount = 0 for obj in helpers where (isKindOf obj RsBugNode) and (obj.pendingChanges != undefined) do ( pendingCount += 1 ) btnCommitPending.enabled = (pendingCount != 0) ) on btnSelPrevBug pressed do ( selNextPrev prev:true ) on btnSelNextBug pressed do ( selNextPrev prev:false ) on btnOpenClient pressed do ( ::bugstar.openBugInClient bugId ) fn updateBug = ( local selObj = $ local rememberPos = selObj.pos if (::bugstar == undefined) do ( fileIn "pipeline/helpers/debug/bugstar.ms" ) local bugdata = bugstar.getBug bugId -- Turn off auto-update if login failed: if not bugstar.hasValidPassword() do ( RsBugNodeAutoUpdate = chkAutoUpdate.checked = false return false ) bugstar.updateBugNode selObj bugdata -- Don't move object if it's over origin, as those will have been raised up on purpose: if (selObj.pos != rememberPos) and (not (rememberPos.x == rememberPos.y == 0)) do ( selObj.pos = rememberPos ) updateCtrls() ) fn warpPlayer = ( local selObj = $ ::RsRagFuncs.setPlayerPos selObj.pos dir:(selObj.rotation as eulerangles).z ) /* on btnEditBug pressed do ( if (::bugstar == undefined) do ( fileIn "pipeline/helpers/debug/bugstar.ms" ) if (::RsBugstarEditDialog == undefined) do ( fileIn "pipeline/helpers/debug/bugstar_edit.ms" ) ::RsBugstarEditDialog.createRoll() ) */ on btnUpdate pressed do ( updateBug() ) on chkAutoUpdate changed val do ( RsBugNodeAutoUpdate = val if val do updateBug() ) on chkbtnWarpPlayer changed val do ( RsBugNodeAutoWarp = val; if val do warpPlayer(); ) on btnBugToolkit pressed do ( filein "pipeline/helpers/debug/bugstar_ui.ms" ) on params open do ( --Lets load in the relevant fileins instead of checking everytime we run a callback on a button press -- if (::RsRagFuncs == undefined) do fileIn "pipeline/util/RAG_funcs.ms"; -- Set up dotNet textbox (which allows for easy wordwrapping) local textClr = (colorMan.getColor #windowText) * 255 local windowClr = (colorMan.getColor #window) * 255 txtData.foreColor = (dotNetClass "System.Drawing.Color").FromArgb textClr[1] textClr[2] textClr[3] txtData.backColor = (dotNetClass "System.Drawing.Color").FromArgb windowClr[1] windowClr[2] windowClr[3] txtData.readOnly = true txtData.multiline = true txtData.scrollbars = txtData.ScrollBars.vertical txtData.wordWrap = true txtData.SelectionStart = 0 txtData.SelectionLength = 0 txtData.readonly = true if RsBugNodeAutoUpdate then updateBug() else updateCtrls() -- If auto Warp on then letes warp if RsBugNodeAutoWarp then warpPlayer() ) on params rolledUp val do ( if val do ( updateCtrls() ) ) ) on update do ( setNodeLabel() ) ) -- This redraw-callback function draws the node-text and links: fn RSnoteNodeRedrawFunc = ( local noteObjs = for obj in objects where (not obj.isHidden) and ((isKindOf obj RsNoteNode) or (isKindOf obj RsBugNode)) collect obj if (noteObjs.count != 0) do ( local lineLenth = 300 local arrowSize = 10 local rndLimits = gw.getRndLimits() local zBufNum = findItem rndLimits #zBuffer if (zBufNum != 0) do (deleteItem rndLimits zBufNum) gw.setRndLimits rndLimits gw.setTransform (Matrix3 1) for obj in noteObjs do ( local isBug = isKindOf obj RsBugNode local nodeLabel = obj.nodeLabel local nodeLink = obj.nodeLink local hasText = (nodeLabel != "") local hasLink = (isValidNode nodeLink) if isBug or hasText or hasLink do ( local manipColour, textColour if obj.isSelected then ( manipColour = textColour = white ) else ( manipColour = obj.wirecolor ) local labelPos = gw.wtransPoint obj.pos local showMesh, showMarker local drawLink = true -- Draw appropriate mesh/marker for node-type: if isBug then ( -- Bugs show asterisk marker: showMarker = #asterisk ) else ( case obj.nodeType of ( (RsNoteNodeSettings.type_point): ( showMarker = #xMarker ) (RsNoteNodeSettings.type_link): ( showMarker = #circle ) (RsNoteNodeSettings.type_arrow): ( directionMesh = true showMarker = #triangle ) (RsNoteNodeSettings.type_box): ( drawLink = false showMarker = #hollowBox ) (RsNoteNodeSettings.type_scalejaxx): ( showMarker = #circle ) default: ( showMarker = #circle ) ) ) -- Draw line to nodeLink node: if drawLink and hasLink do ( gw.setColor #line manipColour local startPoint = labelPos local endPoint = gw.wtransPoint nodeLink.pos local LineVector = endPoint - startPoint LineVector.z = 0 local LineNorm = normalize LineVector local PerpNorm = normalize [LineVector.y,-LineVector.x,0] local scaledLineNorm = (arrowSize * LineNorm) local scaledPerpNorm = (arrowSize * PerpNorm * 0.5) gw.wPolyline #(startPoint, endPoint - scaledLineNorm) false gw.wPolyline #((endPoint - scaledLineNorm - scaledPerpNorm), endPoint, (endPoint - scaledLineNorm + scaledPerpNorm)) true ) if (showMarker != undefined) do ( gw.wMarker labelPos showMarker color:manipColour ) -- Draw node-label text: if hasText do ( -- Make text slightly lighter than mesh: local textColour = manipColour + 70 if (textColour.r > 255) do (textColour.r = 255) if (textColour.g > 255) do (textColour.g = 255) if (textColour.b > 255) do (textColour.b = 255) /* local newText = RsWordWrap nodeLabel lineLenth local labelLines = filterString newText "\n" */ local labelLines = #(nodeLabel) for n = labelLines.count to 1 by -1 do ( gw.wtext labelPos labelLines[n] color:textColour labelPos -= [0, 13, 0] ) ) ) ) ) ) RsNoteNodesResetGlobals() RSnoteNodeRedrawCallback()