-- Rockstar Fragment Tuning -- Rockstar North -- 15/5/2006 -- by Luke Openshaw -- rewritten by Gunnar Droege -- Read and write fragment params -- note: children are bound objects, groups are the actual breaking bits and might have more chidren --filein "../../rockstar/export/settings.ms" filein "string.ms" try CloseRolloutFloater RsFragTuner catch() global RsFragTuner = newRolloutFloater "Rockstar Fragment Tuner" 300 900 50 126 global gFragEditMode = #basic global gReloadFileMode = true struct sRsFragAttribLUT ( ------------------------------------------------------------------------ -- FRAG ATTRIBS ------------------------------------------------------------------------ RsFragAttribLUT = #( #("minMoveForce", "Min force to move me", 0), #("artAssetID", undefined, -1), #("attachBottomEnd", undefined, 0), #("estimatedCacheSize", undefined, 0), #("estimatedArticulatedCacheSize", undefined, 0), #("cloneBoundPartsInCache", undefined, 0), #("unbrokenCGOffset", undefined, #(0,0,0)), #("unbrokenElasticity", "Unbroken elasticity", 0), #("gravityFactor", undefined, 1), #("buoyancyFactor1", undefined, 1), #("breakImpulseSourceTypeFlags", undefined, 0), #("dampingLinearC", undefined, #(0,0,0)), #("dampingLinearV", undefined, #(0,0,0)), #("dampingLinearV2", undefined, #(0,0,0)), #("dampingAngularC", undefined, #(0,0,0)), #("dampingAngularV", undefined, #(0,0,0)), #("dampingAngularV2", undefined, #(0,0,0)) ), ------------------------------------------------------------------------ -- GROUP ATTRIBS ------------------------------------------------------------------------ RsGroupAttribLUT = #( #("strength", "Strength", -1), #("forceTransmissionScaleUp", "Transmission scape UP", 0.25), #("forceTransmissionScaleDown", "Transmission scape DOWN", 0.25), #("jointStiffness", undefined, 0), #("minSoftAngle1", undefined, -1), #("maxSoftAngle1", undefined, 1), #("maxSoftAngle2", undefined, 1), #("maxSoftAngle3", undefined, 1), #("maxSoftAngle4", undefined, 1), #("rotationSpeed", undefined, 0), #("rotationStrength", undefined, 0), #("restoringStrength", undefined, 0), #("restoringMaxTorque", undefined, 0), #("latchStrength", "Resistance of me latch", 0), #("numChildren", undefined, 0), #("numChildGroups", undefined, 0), #("childIndex", undefined, 0), #("parentIndex", undefined, 0), #("childGroupsPointersIndex", undefined, 0), #("flags", undefined, 0), #("minDamageForce", "Min force to damage me", 100), #("damageHealth", "Intial damage health", 100), #("selfCollisionCount", undefined, 0) ), ------------------------------------------------------------------------ -- CHILD ATTRIBS ------------------------------------------------------------------------ RsChildAttribLUT = #( #("pristineMass", "Pristine mass", 0), #("damagedMass", "Damaged mass", 0) ), RsAttribLUT = RsFragAttribLUT + RsGroupAttribLUT + RsChildAttribLUT, -- accessors fn getDefault key = ( for tuple in RsAttribLUT do ( if (tuple[1]==key) then return tuple[3] ) return undefined ), fn getValue key = ( if gFragEditMode==#verbose then return key for tuple in RsAttribLUT do ( if (tuple[1]==key) then return tuple[2] ) return undefined ) ) global gRsFragAttribLUT = sRsFragAttribLUT() ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -- data holding/processing structs ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ struct sFragGroup ( groupName = "fragments", parentGroup = undefined, groupValues = #(), groupArray = #(), childArray = #() ) struct sFragChild ( childName = "dummyname", parentGroup = undefined, childValues = #() ) #typeScalar #typeArray #typeString #typeScope struct sAttrParseResult ( attrName, attrValue, attrType, attrExportFlag = false ) struct sFragTuner ( tuneArray = #(), currFrag = 0.0, currFragName = "initial", currFragRoot = sFragGroup(), currFragGroup = currFragRoot, currObj, childObj, groupObj, fragObj, isFrag, isGroup, currHirarchyErrors = "", -- fn RsTrimAndChopLine idx currLine = ( -- filteredLine = filterstring currLine " " -- -- if filteredLine[idx] != undefined then ( -- --currTrimLine = RsGetTrimParamFromString filteredLine[idx] -- currLine = RsRemoveSpacesFromString filteredLine[idx] -- currLine = RsRemoveTabsFromString currLine -- ) -- -- return currLine -- ), -- fn RsGetTrimParamFromString currLine = ( -- -- currLine = RsRemoveSpacesFromString currLine -- currLine = RsRemoveTabsFromString currLine -- -- return currLine -- ), fn RetrieveIndentation obj = ( indentation = 1 while classof(obj.parent) != UndefinedClass do ( obj = obj.parent indentation = indentation + 1 ) return indentation + 1 ), -------------------------------------------------------------------------- -- Extract hierarchical information from selected object -------------------------------------------------------------------------- fn DetermineObject = ( isFrag = false isGroup = false sel = selection as array obj = sel[1] if obj != Undefined then ( if "GtaObject"==(getattrclass obj) and obj.parent != undefined then ( groupObj = obj isGroup = true groupName = obj.name ) else if "GtaObject"==(getattrclass obj) then ( fragIndex = getattrindex "Gta Object" "Is Fragment" if getattr obj fragIndex == true then ( isFrag = true isGroup = false ) ) -- get the absolute parent while classof(obj.parent) != UndefinedClass do ( obj = obj.parent ) --getting rid of suffix fragObjName = obj.name local fragStringIndex = findString fragObjName "_frag_" if fragStringIndex!=undefined then fragObjName = substring fragObjName 1 (fragStringIndex-1) currFragName = fragObjName ) ), fn dumpGroup theGroup theFile indentDepth isRoot = ( local indent = "" local borderindent = "" local i=0 while i0 then ( if (valueParseStep[1] as number)!=undefined then ( if valueParseStep.count>1 then ( back.attrType = #typeArray back.attrValue = valueParseStep ) else ( back.attrType = #typeScalar back.attrValue = valueParseStep[1] ) ) else ( if valueParseStep.count>1 and valueParseStep[1]=="all" then -- masses on children have the all suffix ( back.attrType = #typeScalar back.attrValue = valueParseStep[2] ) else ( back.attrType = #typeString back.attrValue = valueParseStep[1] ) ) ) ) back.attrExportFlag = true return back ), -------------------------------------------------------------------------- -- Stream in tune data -------------------------------------------------------------------------- fn ReadInAttrib tunePath = ( local currScope = #("root") sel = selection as array currObj = sel[1] tuneFile = openFile tunePath -- print tunePath if tuneFile != undefined then ( while eof tuneFile == false do ( currentLine = readLine tuneFile -- remove trailing spaces local nameAndValueTuple = ParseNameAndValueFromString currentLine prefix = nameAndValueTuple.attrName value = nameAndValueTuple.attrValue if prefix == "}" then ( if currScope.count>0 then ( if currScope[currScope.count]=="groupScope" then currFragGroup = currFragGroup.parentGroup deleteItem currScope currScope.count ) -- try( print ("< "+currScope[currScope.count]+", "+(currScope.count as string)) )catch() continue ) else if prefix == "group" then ( groupName = value append currScope "groupScope" local formerFragGroup = currFragGroup currFragGroup = sFragGroup() currFragGroup.parentGroup = formerFragGroup currFragGroup.groupName = groupName append formerFragGroup.groupArray currFragGroup ) else if prefix == "child" then ( currChild = sFragChild() append currScope "childScope" currentLine = readLine tuneFile local nameAndValueTuple = ParseNameAndValueFromString currentLine append currChild.childValues nameAndValueTuple currentLine = readLine tuneFile local nameAndValueTuple = ParseNameAndValueFromString currentLine append currChild.childValues nameAndValueTuple append currFragGroup.childArray currChild ) else ( -- tis a value append currFragGroup.groupValues nameAndValueTuple ) ) close tuneFile return true ) else return false ), -------------------------------------------------------------------------- -- Create the tune hierarchy to show from actual scene data -------------------------------------------------------------------------- fn ParseNameAndValueFromDefault val = ( back = sAttrParseResult() back.attrName = val[1] back.attrValue = val[3] back.attrType = \ if (superClassOf val[3])==number then #typeScalar \ else if (classof val[3])==array then #typeArray \ else typeString back.attrExportFlag = undefined!=val[2] return back ), fn createGroup sceneNode isRoot = ( local formerFragGroup = undefined if not isRoot then ( local formerFragGroup = currFragGroup currFragGroup = sFragGroup() currFragGroup.parentGroup = formerFragGroup currFragGroup.groupName = sceneNode.name ) else currFragGroup = currFragRoot if undefined!=formerFragGroup then ( append formerFragGroup.groupArray currFragGroup print formerFragGroup.groupName print formerFragGroup.groupArray.count ) -- create attributes if isRoot then -- fragment root data ( for val in gRsFragAttribLUT.RsFragAttribLUT do ( local nameAndValueTuple = ParseNameAndValueFromDefault val append currFragGroup.groupValues nameAndValueTuple ) ) else -- group data ( for val in gRsFragAttribLUT.RsGroupAttribLUT do ( local nameAndValueTuple = ParseNameAndValueFromDefault val append currFragGroup.groupValues nameAndValueTuple ) ) -- bounds data local childBounds = for c in sceneNode.children where "Gta Collision"==(getAttrClass c) collect c for b in childBounds do ( currChild = sFragChild() for val in gRsFragAttribLUT.RsChildAttribLUT do ( local nameAndValueTuple = ParseNameAndValueFromDefault val append currChild.childValues nameAndValueTuple ) append currFragGroup.childArray currChild ) -- child data local childMeshes = for c in sceneNode.children where "Gta Object"==(getAttrClass c) collect c for c in childMeshes do ( createGroup c false ) if undefined!=formerFragGroup then currFragGroup = formerFragGroup return true ), fn CreateHierarachyFromNodes = ( local obj = selection[1] if obj == undefined then ( messagebox "Select an object!" return false ) while undefined!=obj.parent do obj = obj.parent if not (createGroup obj true) then return false gReloadFileMode = false if undefined!=RsButtonRoll then RsButtonRoll.checkReloadFile.checked = false return true ), -------------------------------------------------------------------------- -- Hierarchy manipulation -------------------------------------------------------------------------- fn findGroup root grpName = ( if matchPattern grpName pattern:"*_frag_*" then ( grpName = substring grpName 1 ((findString grpName "_frag_")-1) ) if currFragname == grpName then return currFragRoot if root.groupName==grpName then return root for grp in root.groupArray do ( local back = findGroup grp grpName if back != undefined then return back ) return undefined ), fn setGroupValue grpName valName newVal arrayIndex = ( local grp = findGroup currFragRoot grpName --print (grp) if grp!=undefined then ( for val in grp.groupValues do if val.attrName==valname then ( if arrayindex!=undefined then ( val.attrValue[arrayIndex] = newVal ) else ( val.attrValue = newVal ) ) ) ), fn setChildValue grpName childIndex valName newVal = ( local grp = findGroup currFragRoot grpName if grp!=undefined then ( for val in grp.childArray[(childIndex as number)].childValues do if val.attrName==valname then val.attrValue = newVal ) ), fn checkGroup theGroup sceneNode = ( -- if theGroup.groupName=="fragments" then -- if theGroup.childArray.count != sceneNode then local childMeshes = for c in sceneNode.children where "Gta Object"==(getAttrClass c) collect c local childBounds = for c in sceneNode.children where "Gta Collision"==(getAttrClass c) collect c if theGroup.groupArray.count != childMeshes.count then ( currHirarchyErrors = (sceneNode.name+"'s child mesh count "+(childMeshes.count as string)+" not equal to "+(theGroup.groupName)+"'s group count "+(theGroup.groupArray.count as string)) return false ) if theGroup.childArray.count != childBounds.count then ( currHirarchyErrors = (sceneNode.name+"'s child collision count "+(childBounds.count as string)+" not equal to "+(theGroup.groupName)+"'s child count "+(theGroup.childArray.count as string)) return false ) for grp in theGroup.groupArray do ( if not (checkGroup grp (getNodeByName grp.groupName)) then return false ) return true ), fn checkFragHirarchy = ( return (checkGroup currFragGroup selection[1]) ), fn PopulateFragTuner = ( local isSameFragment = false if undefined!=selection[1] then ( currFragGroup = findGroup currFragRoot selection[1].name isSameFragment = undefined!=currFragGroup ) -- no file update desired if isSameFragment and not gReloadFileMode and undefined!=selection[1] then ( if undefined==currFragGroup then currFragGroup = currFragRoot checkFragHirarchy() ::ResetRollouts currFragGroup.groupName return true ) currHirarchyErrors = undefined currFragRoot = sFragGroup() currFragGroup = currFragRoot currFragname = "" local currGroupName = undefined if $selection.count<=0 then ( ::ResetRollouts undefined return false ) DetermineObject() currFragRoot.groupName = currFragName local tunePath = RsConfigGetCommonDir() + "data/fragments/" + currFragName + ".tune" if ReadInAttrib tunePath then ( currFragGroup = findGroup currFragRoot selection[1].name if undefined==currFragGroup then currFragGroup = currFragRoot checkFragHirarchy() ::ResetRollouts currFragGroup.groupName ) else ( ::ResetRollouts undefined ) ), fn RefreshUI = ( if undefined!=RsTreeViewRoll then RsTreeViewRoll.reset() PopulateFragTuner() if undefined!=RsTreeViewRoll then ( RsTreeViewRoll.updateTV gFragTuner.currFragRoot gFragTuner.currFragGroup RsFragRoll.setFragName gFragTuner.currFragname ) ), fn WriteFragTuneData = ( tunePath = RsConfigGetCommonDir() + "data/fragments/" + currFragName + ".tune" WriteOutAttrib tunePath ) ) -- struct sFragTuner global gFragTuner = sFragTuner() ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -- global UI code ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ rollout RsBasicEditRollout "Quick edit" ( label lblBullt "bullet ped car" align:#right label comboLabel "Only damaged by:" across:2 align:#left slider comboDamagedBy "" range:[0,16,8] type:#integer --items:#("bullet", "ped", "car") label checkboxLabel "Disappear when dead" across:2 align:#left checkbox checkDisappear "" on comboDamagedBy changed val do ( local threshold = 1 for i=1 to val do threshold = threshold * 2 ) ) fn addControlAndHandler creator type varName readableName paramString codeString isExport = ( creator.addControl type varName readableName paramStr:(paramString+" across:2 offset:(point2 110 0)") local checkBoxName = (varName+"_expChck") creator.addControl #checkbox checkBoxName "" paramStr:("width:10 align:#right checked:"+isExport as string) creator.addHandler varName #changed paramStr:"val" codeStr:(codeString+"; "+checkBoxName+".checked = true") ) fn ResetRollouts groupName = ( for r in RsFragTuner.rollouts do ( try( if findString r.name "RsTree"==undefined and findString r.name "RsFrag"==undefined and findString r.name "RsButton"==undefined then ( -- format "Remove rollout: % [%]\n" r r.name removeRollout r RsFragTuner ) )catch() ) if undefined!=gFragTuner.currHirarchyErrors then ( -- local stream = stringstream "" -- theClass = dotNetClass "System.Drawing.Color" -- showMethods theClass to:stream -- messagebox stream -- theClass.fromARGB 255 0 0 rollout ErrorRollout "Hierarchy errors:" ( dotNetControl f1 "System.Windows.Forms.Textbox" text:gFragTuner.currHirarchyErrors readonly:true multiline:true scrollbars:true height:60 --edittext lbl "" text:gFragTuner.currHirarchyErrors readOnly:true height:32 ) addRollout ErrorRollout RsFragTuner ) -- search for groupNode local rootGroup = gFragTuner.currFragGroup if groupName==undefined then ( -- rootGroup = gFragTuner.currFragRoot if undefined!=RsButtonRoll then ::RsButtonRoll.btnCreate.enabled = true ) else ( -- rootGroup = gFragTuner.findGroup gFragTuner.currFragRoot groupName ::RsButtonRoll.btnCreate.enabled = false ) if groupName==undefined then ( return false ) if gFragEditMode==#basic then ( addRollout RsBasicEditRollout RsFragTuner return true ) child = rolloutCreator "RsDynFragRoll" "Frag values" child.begin() local index=0 for c in gFragTuner.currFragRoot.groupValues do ( local varName = "var"+index as string local readableName = gRsFragAttribLUT.getValue c.attrName if (undefined==readableName) then continue case c.attrType of ( #typeScalar: ( addControlAndHandler child #spinner varName readableName (" type:#float range:[-100,100000,"+c.attrValue as string+"]") (" if gFragTuner!=undefined then gFragTuner.setGroupValue @fragments@ @"+c.attrName+"@ val undefined") c.attrExportFlag ) #typeArray: ( arrayVarName = varname+"a0" addControlAndHandler child #label arrayVarName readableName "" "" c.attrExportFlag arrayVarName = varname+"a1" child.addControl #spinner arrayVarName "" paramStr:(" across:3 type:#float range:[-100,100000,"+c.attrValue[1] as string+"]") child.addHandler arrayVarName #changed paramStr:"val" codeStr:("gFragTuner.setGroupValue @fragments@ @"+c.attrName+"@ val 1") arrayVarName = varname+"a2" child.addControl #spinner arrayVarName "" paramStr:(" type:#float range:[-100,100000,"+c.attrValue[2] as string+"]") child.addHandler arrayVarName #changed paramStr:"val" codeStr:("gFragTuner.setGroupValue @fragments@ @"+c.attrName+"@ val 2") arrayVarName = varname+"a3" child.addControl #spinner arrayVarName "" paramStr:(" type:#float range:[-100,100000,"+c.attrValue[3] as string+"]") child.addHandler arrayVarName #changed paramStr:"val" codeStr:("gFragTuner.setGroupValue @fragments@ @"+c.attrName+"@ val 3") ) #default: ( addControlAndHandler child #edittext varName readableName (" align:#right value:"+c.attrValue as string) ("gFragTuner.setGroupValue @fragments@ @"+c.attrName+"@ val 1") c.attrExportFlag ) ) index+=1 ) child.end() addRollout child.def RsFragTuner if groupName==undefined or groupName=="ROOT" or gFragEditMode!=verbose then return true child = rolloutCreator "RsGroupRoll" "Group values" child.begin() local index=0 for c in rootGroup.groupValues do ( local varName = "var"+index as string local readableName = gRsFragAttribLUT.getValue c.attrName if (undefined==readableName) then continue case c.attrType of ( #typeScalar: ( addControlAndHandler child #spinner varName readableName (" type:#float range:[-100,100000,"+c.attrValue as string+"]") ("gFragTuner.setGroupValue @"+groupName+"@ @"+c.attrName+"@ val undefined") c.attrExportFlag ) #typeArray: ( arrayVarName = varname+"a0" addControlAndHandler child #label arrayVarName readableName "" "" c.attrExportFlag arrayVarName = varname+"a1" child.addControl #spinner arrayVarName "" paramStr:(" across:3 type:#float range:[-100,100000,"+c.attrValue[1] as string+"]") child.addHandler arrayVarName #changed paramStr:"val" codeStr:("gFragTuner.setGroupValue @"+groupName+"@ @"+c.attrName+"@ val 1") arrayVarName = varname+"a2" child.addControl #spinner arrayVarName "" paramStr:(" type:#float range:[-100,100000,"+c.attrValue[2] as string+"]") child.addHandler arrayVarName #changed paramStr:"val" codeStr:("gFragTuner.setGroupValue @"+groupName+"@ @"+c.attrName+"@ val 2") arrayVarName = varname+"a3" child.addControl #spinner arrayVarName "" paramStr:(" type:#float range:[-100,100000,"+c.attrValue[3] as string+"]") child.addHandler arrayVarName #changed paramStr:"val" codeStr:("gFragTuner.setGroupValue @"+groupName+"@ @"+c.attrName+"@ val 3") ) #default: ( addControlAndHandler child #edittext varName readableName (" align:#right value:"+c.attrValue as string) ("gFragTuner.setGroupValue @"+groupName+"@ @"+c.attrName+"@ val 1") c.attrExportFlag ) ) index+=1 ) child.end() addRollout child.def RsFragTuner local index=1 for c in rootGroup.childArray do ( local readableName = gRsFragAttribLUT.getValue c.childValues[2].attrName if (undefined==readableName) then continue child = rolloutCreator ("Child"+(index as string)) ("child "+(index as string)) -- Start creating the rollout child.begin() addControlAndHandler child #spinner "myEdit1" readableName (" type:#float range:[-100,100000,"+c.childValues[1].attrValue as string+"]") ("gFragTuner.setChildValue @"+groupName+"@ @"+(index as string)+"@ @"+c.childValues[1].attrName+"@ val") c.attrExportFlag readableName = gRsFragAttribLUT.getValue c.childValues[1].attrName if (undefined==readableName) then continue addControlAndHandler child #spinner "myEdit2" readableName (" type:#float range:[-100,100000,"+c.childValues[2].attrValue as string+"]") ("gFragTuner.setChildValue @"+groupName+"@ @"+(index as string)+"@ @"+c.childValues[2].attrName+"@ val") c.attrExportFlag child.end() index+=1 addRollout child.def RsFragTuner ) ) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -- rollouts ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ rollout RsFragRoll "Fragment" ( editText edtFragName "Frag Name:" on RsFragRoll open do ( callbacks.removeScripts id:#loadFrag local script = "gFragTuner.RefreshUI()" callbacks.addscript #selectionSetChanged script id:#loadFrag ) on RsFragRoll close do ( callbacks.removeScripts id:#loadFrag ) fn setFragName newName = ( edtFragName.text = newName ) ) rollout RsButtonRoll "Update" ( local values = #(#basic, #advanced, #verbose) button btnCreate "Create Tune File" across:2 button btnUpdate "Save Tune File" checkbox checkReloadFile "Reload tune on select" checked:true radiobuttons radioMode "Editing mode" labels:values columns:3 on btnCreate pressed do ( if not gFragTuner.CreateHierarachyFromNodes() then return false ResetRollouts gFragTuner.currFragGroup.groupName if undefined!=RsTreeViewRoll then RsTreeViewRoll.updateTV gFragTuner.currFragRoot gFragTuner.currFragGroup ) on btnUpdate pressed do ( gFragTuner.WriteFragTuneData() ) on checkReloadFile changed val do ( gReloadFileMode = val ) on radioMode changed val do ( gFragEditMode = values[val] gFragTuner.PopulateFragTuner() ) ) rollout RsTreeViewRoll "Node hirarchy" ( button buttLevelUp "select parent node" width:280 align:#center dotNetControl tv "TreeView" width:280 height:200 align:#center local currentSelected = undefined fn addChildren theNode theChildren selected = ( local bgColorClass = (dotNetclass "System.Drawing.Color") local bgColor = bgColorClass.FromArgb 255 0 100 255 local fgColor = bgColorClass.FromArgb 255 255 255 255 for c in theChildren do ( newNode = theNode.Nodes.add c.groupName if selected.groupName==c.groupName then ( newNode.backcolor = bgcolor newNode.foreColor = fgColor ) addChildren newNode c.groupArray selected ) ) fn expandAll = ( tv.ExpandAll() ) fn updateTV root selected = ( currentSelected = undefined local theChildren = root.groupArray if theChildren.count<=0 then return false theRoot = tv.Nodes.add "ROOT" rootNodes = for o in theChildren collect o --where o.parent == undefined addChildren theRoot rootNodes selected expandAll() ) on tv Click arg do ( hitNode = tv.GetNodeAt (dotNetObject "System.Drawing.Point" arg.x arg.y) if hitNode != undefined do try( local theNode = getNodeByName (hitNode.Text+"_frag_") if undefined==fragNode then theNode = getNodeByName hitNode.Text select theNode )catch(print "error selecting node") ) on buttLevelUp pressed do ( if selection[1]!=undefined and selection[1].parent!=undefined then ( select selection[1].parent ) ) fn reset = ( tv.nodes.Clear() ) ) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -- invoking code ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ addRollout RsFragRoll RsFragTuner addRollout RsTreeViewRoll RsFragTuner addRollout RsButtonRoll RsFragTuner gFragTuner.PopulateFragTuner()