--------- Automatic QC file generator -- layout header
--------- version 0.9
-------- known issues:
		--- absolute pathname buttons don't work yet
		--- trying to write to a protected file will crash script
		--- changing text in some fields causes double updates
		--- opening script file can fail if max's last directory is set to something
		---		other than qc location
		-- robustness of errorchecking in pathnames is limited
		--- Compile QC only works if dev tree is set correctly
		-- can choose wrong tex directory if multiple textures are present
		

global Activity_list, At_List, ProjectRoot, Path, EditHack

---------- initializations
struct sequence (smd, name, fps = 30, loop = false, lx = false, act)
edithack = true
at_list = #()
smd_list = #()
Activity_list  = #("","IDLE","WALK","WALK_LOOK_LEFT","WALK_LOOK_RIGHT", \
"RUN","RANGE_ATTACK1","RANGE_ATTACK2","DIESIMPLE","DIEBACKWARD", \
"DIEFORWARD","DIEVIOLENT","FLY","SWIM","HOP","LEAP","LAND", \
"STRAFE_LEFT","STRAFE_RIGHT","ROLL_LEFT","ROLL_RIGHT","TURN_LEFT", \
"TURN_RIGHT","CROUCH","CROUCHIDLE","STAND","USE","SIGNAL1","SIGNAL2", \
"SIGNAL3","LOOKBACK_RIGHT","LOOKBACK_LEFT","COWER","SMALL_FLINCH", \
"BIG_FLINCH","MELEE_ATTACK1","MELEE_ATTACK2","RELOAD","ARM","DISARM", \
"IDLE_ANGRY","WALK_HURT","RUN_HURT","SPECIAL_ATTACK1","SPECIAL_ATTACK2", \
"COMBAT_IDLE","WALK_SCARED","RUN_SCARED","VICTORY_DANCE","DIE_HEADSHOT", \
"DIE_CHESTSHOT","DIE_GUTSHOT","DIE_BACKSHOT","FLINCH_HEAD","FLINCH_CHEST", \
"FLINCH_STOMACH","FLINCH_LEFTARM","FLINCH_RIGHTARM","FLINCH_LEFTLEG", \
"FLINCH_RIGHTLEG","VM_DRAW","VM_HOLSTER","VM_IDLE","VM_FIDGET", \
"VM_PRIMARYATTACK","VM_SECONDARYATTACK","VM_RELOAD","VM_DRYFIRE", \
"VM_HITLEFT","VM_HITRIGHT","VM_HITCENTER","VM_MISSLEFT","VM_MISSRIGHT", \
"VM_MISSCENTER","VM_HAULBACK","VM_SWINGHARD","VM_SWINGMISS","VM_SWINGHIT")

---------------------------------------------------------------------------------

---------------------------------------- General Functions -----------------------------------------
----------------------------------------------------------------------------------------------------	
fn At_update which =
	(
	if At_list[which] == undefined then
		(
		At_dummy = sequence smd:(filenamefrompath smd_list[which]) \
					name:(GetfilenameFile smd_list[which]) \
					fps:"30" \
					loop:false \
					lx:false \
					act:(1 as integer)
		append  At_list at_dummy
		)
	else
		(
		do_string = "" as stringstream
		format "smds.SmdFile%.text = At_list[%].smd\n" which which to:do_string
		format "smds.SmdName%.text = At_list[%].name\n" which which to:do_string
		format "smds.Fps%.text = At_list[%].fps\n" which which to:do_string
		format "smds.loop%.state = At_list[%].loop\n" which which to:do_string
		format "smds.lx%.state = At_list[%].lx\n" which which to:do_string
		format "smds.SmdAct%.selection = AT_List[%].act" which which to:do_string -- why doesn't this work? To Be Added
		seek do_string 0
		execute do_string
		)
	)


fn make_smd_list =
	(
	make_string = "------ generate smd rollout\n" as stringstream
	format "rollout Smds \"Sequences\"(\n" to: make_string	

	format "button SMDGetAll \"Collect .smd files\" across:3\n" to:make_string
	format "button Addone \"Add .smd file\" \n" to:make_string
	format "button ClrSmd \"Clear SMD list\" \n" to:make_string
	format "on SmdGetAll pressed do	collect_smds()\n" to:make_string
	format "on ClrSmd pressed do (\n" to:make_string
	format "Smd_list = #()\n" to:make_string
	format "At_list = #()\n" to:make_string
	format "make_smd_list())\n" to:make_string
	format "on AddOne pressed do Add_smd()\n" to:Make_String
	format "label s0 \"smd file\"  offset:[10,0]across:6\n"  to: make_string
	format "label s1 \"sequence\" offset:[30,0]\n"  to: make_string
	format "label s2 \"FPS\" offset:[33,0]\n"  to: make_string
	format "label s3 \"loop\" offset:[-1,0]\n"  to: make_string
	format "label s4 \"extract\" offset:[-37,0]\n"  to: make_string
	format "label s5 \"Activity tag\" offset:[-50,0]\n" to: make_string
	if smd_list.count > 0 then
		(
		for Q = 1 to smd_list.count do 
			(
			--------- dynamic list items
			format "button SmdDel% \"X\"offset:[-20,-2] across:7\n" Q to: make_string
			format "edittext SmdFile% width:86 offset:[-40,0]\n" Q to: make_string
			format "edittext SmdName% width:86 offset:[-16,0]\n" Q to: make_string 
			format "edittext Fps% width:32 offset:[9,0]\n" Q to: make_string
			format "checkbox Loop% offset:[-1,2]\n" Q to: make_string
			format "checkbox Lx% offset:[-30,2]\n" Q to: make_string
			format "dropdownlist SmdAct% items:Activity_list width:120 offset:[-56,-2]\n" Q to: make_string
			--------- handlers
			format "on SmdDel% pressed do (delete_smd %)\n" Q  Q to: make_string
			format "on SmdFile% entered text do (At_list[%].smd = SmdFile%.text)\n" Q  Q  q to: make_string
			format "on SmdName% entered text do (At_list[%].name = SmdName%.text)\n" Q  Q  q to: make_string
			format "on Fps% entered text do (At_list[%].fps = fps%.text)\n" Q  Q q to: make_string
			format "on loop% changed state do (At_list[%].loop = loop%.state)\n" Q  Q q to: make_string
			format "on lx% changed state do (At_list[%].lx = lx%.state)\n" Q  Q q to: make_string
			format "on SmdAct% selected K do (At_list[%].act = K)\n" Q  Q q to: make_string
			)
		)

	format ")\n\n" to:make_string
	format "AddRollout smds QcWindow \n\n" to: make_string
	RemoveRollout Smds QCWindow
	seek make_string 0
	execute make_string
	for w = 1 to smd_list.count do (At_update W)
	)
	
fn Delete_smd target = 
	(
	if (Querybox "Delete .smd file from disk") then
		(deletefile smd_list[target])		
	deleteItem smd_list target
	deleteItem At_list target
	make_smd_list()
	)
	
fn collect_smds =
	(
	smd_srch = Header.TreePath.text + "\\" + Header.WrkPath.text + "\\"+ "*.smd"
	smd_list = getfiles Smd_srch
	for w = 1 to smd_list.count do
		( At_Update w )	
	make_smd_list()	 
	)
	
fn Add_Smd =
	(
	NewSmd = GetOpenFileName caption:"Add sequence for SMD file:" types:".smd files|*.smd|"
	if NewSmd == undefined then 
	
	 	(return "")
		else
		(
		NewSmD = FileNameFromPath NewSmd
		Append Smd_list NewSmd
		At_Dummy = sequence\
				smd:(FileNameFromPath NewSmd) \
				name:(GetfilenameFile NewSmd) \
				fps:"30" \
				loop:false \
				lx:false \
				act:(1 as integer)
			Append At_list At_dummy	
		make_smd_list()
		)
	)
	
fn Get_sequences = 
	(
	Sequence_getter = "" as stringstream
	for i = 1 to smd_list.count do
		(
		format "$sequence \"%\" \"%\" fps %" \
			At_list[i].name \
			(GetFileNameFile At_list[i].smd) \
			At_list[i].fps \
			to: Sequence_getter
		if At_list[i].Loop do (format " LOOP" to:Sequence_getter)
		if At_list[i].LX do (format " LX" to:Sequence_getter)
		-------- this is a hack to get around wierdness with the droplists by using the object values
			
		doublehack = "" as stringstream
		ampersand = "%" as string
		format "if smds.SmdAct%.selection > 1 do "  i to:doublehack
		format "format \" activity ACT_% \" smds.SmdAct%.selected " ampersand i to:doublehack 
		format "to:Sequence_getter" to:doublehack
		doublehack = doublehack as string		
		execute doublehack
		------------
		format "\n\n" to:Sequence_getter
		)
	return Sequence_getter
	)
	
fn Assemble_Qc =
	(
	---------- make sure header is complete:
	if Header.QcPath.text == "" then
		(
		Messagebox "Specify .Qc file name and paths before exporting"
		return ""
		)
	---------- make sure there's a body
	
	if body.VRMPath.text == "" or body.VRMPath.text == "-- press to choose VRM file --" then
		(
		Messagebox "Cannot export .qc with no body specified"
		return ""
		)
		
	----------- collect SMDs
	if smd_list.count == 0 then
		(
		if Querybox "Export .qc file with no sequences?" then 
			(
			Sequence_list = "//// Add $sequence statements below here" as stringstream
			)
		else
			(
			return ""
			)
		)
	else
		(
		Sequence_list = Get_sequences()
		)
	-------- we've passed all fail conditions
	-------- now get header info
	QC_OUT = createfile (Header.QcPath.text)
	format "//\n// .qc file version 1.0\n" to:QC_OUT
	format "// generated by QcMaker v0.9\n//* %" Header.comment.text to:QC_OUT
	format "\n\n\n" to:QC_out
	
	modelname = Header.MdlPath.text
	if Header.MdlAbs.state then modelname = AbsPath (modelname)
	format "$modelname %\n" modelname to:QC_OUT
	
	CDir = Header.WrkPath.text
	if Header.WrkAbs.state then CDir = AbsPath CDir
	format "$cd %\n" CDir to:QC_OUT
	
	TDir = Header.TexPath.text
	if Header.TexAbs.state then TDir = AbsPath TDir
	format "$cdtexture %\n\n\n"  TDir to:QC_OUT
	
	Scl = Gmod.SCL.text
	if Scl == "" then Scl = "1.0"
	format "$scale %\n" Scl to:QC_OUT
	
	OrX =  Gmod.ORX.text
	OrY = Gmod.ORY.text
	OrZ = Gmod.ORZ.text
	OrR = Gmod.ORR.text
	if ("" + OrX + OrY + OrZ + OrR).count > 0 then -- make sure values are there
		(
		OrX = if (OrX == "") then "0" else OrX
		OrY = if (OrY == "") then "0" else OrY
		OrZ = if (OrZ == "") then "0" else OrZ
		OrR = if (OrR == "") then "0" else OrR
		)
	if OrX != "" do (format "$origin % % % %\n\n\n" OrX OrY OrZ OrR to:QC_OUT)
	
	Bname = Body.Bodyname.text 
	Bname = if Bname == "" then "body" else Bname
	Vrm = getfilenamefile Body.VrmPath.text
	MrmMax = Body.MrmMax.value as integer
	MrmMin = Body.MrmMin.value as integer
	Format "$body \"%\" \"%\" % %" Bname Vrm MrmMax MrmMin to:QC_OUT
	if Body.VrMflip.state do (format" reverse" to:QC_OUT)
	format "\n\n\n" to:QC_OUT
	
	format "// --------- Animation sequences -------\n" to:QC_OUT
	seek Sequence_list 0
	 while not eof Sequence_list do
		(
		nextone = readline Sequence_list
		format "%\n" nextOne to: QC_OUT
		)
	close QC_OUT
	
	Mbox = "" as stringstream	
	format "Saved file as %\n" Header.QcPath.text to:Mbox
	format "Do you want to open it now?" to:Mbox
	
	if Querybox (Mbox as string) then
		(
		doscommand  ("Path "+ GetFileNamePath (Header.QcPath.text))
		edit (FileNameFromPath Header.QcPath.text)
		)
	
	return ""
	)
	
fn nospaces Str = (return Str) --  a dummy func

	
fn GetPath  = -- a dummy func
		(
		path = GetFileNamePath Header.QcPath.text
		if path == "" do return #("",0)

		path = filterstring path "\\"
		trees = #("HL2","hl2","Hl2","Halflife2", \
				"TF2","tf2","Tf2","TeamFortress2", \
				"Quiver","quiver","HL","hl")
		ProjectRoot = 1
		Leaves = path.count
		for i = 1 to Leaves do
			(
			TreeFlag = false
				for j = 1 to trees.count do --- search for tree tag
					(
					if findstring path[i] trees[j] != undefined then  TreeFlag = true
					)
			if Treeflag then 
				(
				ProjectRoot = i
				exit
				)
			)
		return #(Path,ProjectRoot)
		)
		
fn relative path1 = ( -------- returns pathnmame relative to Tree
	Gpath = getpath ()
	path = Gpath[1]
	projectroot = Gpath[2]
	
	compstring = ""
	for i = 1 to ProjectRoot do
		(
		compstring = compstring + path[i] + "\\"
		)
	cut = (compstring.count) as integer

	compstring = (substring (compstring) 1 ( cut - 2 )) + "*"
	if matchpattern path1 pattern:compstring then
		(
		path1 = substring path1 (cut+1) 255
		return path1
		)
	else
		(return path1)
	)
	
	

---------------------------------------- Header Rollout --------------------------------------------
----------------------------------------------------------------------------------------------------	

rollout Header "QC header"
(


group "Dev Tree" -----------------------
	
	(
	label L "save .qc file as:"\
		across:3 offset:[-6,3]
	
	button QcPathSet "SET:" \
		width:32 height:18 offset:[-80,0] \

	edittext QcPath \
		fieldwidth:255 offset:[-130,0]
	
	
	label L0 " developement tree:"\
		across:3 offset:[-15,3]
	
	button TreePathSet "SET:" \
		width:32 height:18 offset:[-80,0] 

	edittext TreePath \
		fieldwidth:255 offset:[-130,0]		
	)
		
	group "Header comment:"
		(
		edittext Comment 
		)
		
	group "Path setup" ---------------------
	(			
	--------- mdl path
	
	label L1 "Save .mdl as:"\
		across:4 offset:[-24,3]
	
	button MdlPathSet "SET:" \
		width:32 height:18 offset:[-76,0] \

	edittext MdlPath\
		text:"-- press to choose .mdl path and name --" \
		fieldwidth:255 offset:[-110,0]
	
	checkbox MdlAbs "Absolute" \
		align:left	checked:false offset:[48,0] enabled:false
	
	------- work path 
	
	label L2 "source Path" \
		across:4 offset:[-25,3]
	
	button WrkPathSet "SET:" \
		width:32 height:18 offset:[-76,0] \
		
	edittext WrkPath \
		text:"-- press to choose work files path --" \
		fieldwidth:255 offset:[-110,0]
	
	checkbox WrkAbs "Absolute" \
		align:left	checked:false offset:[48,0] enabled:false
	
	------- texture path
	
	label L3 "texture Path" \
		across:4 offset:[-24,3]
	
	button TexPathSet "SET:" \
		width:32 height:18 offset:[-76,0]
		
	edittext TexPath \
		text:"-- press to choose texture path --" \
		fieldwidth:255 offset:[-110,0]
	
	checkbox TexAbs "Absolute" \
		align:left	checked:false offset:[48,0] enabled:false
	
		
	) ---------- end of path group -------------
	
	label space2 ""
	
	button upd "Guess Paths" across:2 width:144
	
	button EditQC "Edit .qc from disk" width:144	

	button GoQC "Generate .qc file" across:2 width:144
	
	button build "Compile .mdl"  width:144
	
	label space3 ""
	
	-------- header functions are local to rollout
	
	--------------------------- begin PathInit functions
	-------------------------- body is separated for ease of prechecking
	fn PathInitBody =
		(
		Gpath = getpath ()
		Path = Gpath[1]
		ProjectRoot = Gpath[2]			
		---------------------- default project	
		TrPath = "" as stringstream 
		for k = 1 to ProjectRoot do 
			(
			format "%\\" path[k] to:TrPath
			)
		TrPath = TrPath as string
		edithack = false
		TreePath.text = TrPath 
		
		---------------------- work files
		WorkPath = "" as stringstream
		for m = ProjectRoot + 1 to Leaves do 
			(
			format "%\\" path[m] to:WorkPath
			)
		WorkPath = WorkPath as string
		cut = WorkPath.count
		cut -= 1
		WorkPath = substring WorkPath 1 cut
		edithack = false
		WrkPath.text = WorkPath
		
		--------------------- texture directories
		Texs = #("textures","Textures", \
				"Maps_8bit","tx8","Tx8","TX8", \
				"bitmaps","Bitmaps","Txmaps", \
				"maps","Maps","maps_8bit","TX", \				
				"txmaps","texmaps","Texmaps")
		
		TexPath.text = ""
	 	for L = 1 to Texs.count do
			(
			TestPath = getdirectories (TrPath + WorkPath +"\\"+ Texs[L])
			if TestPath.count > 0 then TexPath.text = WorkPath +"\\"+ Texs[L]
			)
		-------------------- Model directory
		ModelRoot = TreePath.text + Path[ProjectRoot + 1]
		PossPath = ModelRoot + ("\\model*" as string)
		if GetDirectories PossPath  != undefined then
			(
			MdlPath.text = relative ModelRoot+"\models"
			)
		else
			MdlPath.text  = "-- press to choose .mdl path and name --"
		)
		
	fn PathInit =
			(	
			if 	TreePath.text == "" then
				(
			 	PathInitBody()
				)
			else
				(
				if (Querybox "Attempt to update pathnames ?") then
					(
					PathInitBody() 
					)
				)
			)
		
-------------------------- end PathInit function

	on QcPathSet pressed do
		(
		QcPathIn = GetSaveFileName caption:"Save QC file as:" \
			types:".qc files|*.qc|"
		if QcPathIn != undefined then QcPath.text = QcPathIn
		if QcPath.text == QcPathIn do (PathInit())		
		)
	
	on QCPath entered text do
		(
		if edithack then 
			(
			QCPath.text = text
			pathinit()
			)
		else
			(
			edithack = true
			)
		)------ end QCPathSet
		
	on TreePathSet pressed do
		(
		TreePathIn = GetSavePath caption:"Find project (HL, TF, Quiver) directory :" 
		if TreePathIn != undefined then TreePath.text = (TreePathIn + "\\")
		)
		
	on TreePath entered text do
		(
		if edithack then
			(
			if TreePath.text != "" then 
				(
				cut = TreePath.text.count - 1
				Checkpath = (substring (TreePath.text) 1 cut ) + "*"
				if (getdirectories checkpath).count  == 0 then
					(
					messagebox "Could not find specified Root path"	
					TreePath.Text = ""
					)
				else
					(
					if substring TreePath.text (treepath.text.count) (treepath.text.count) != "\\" do
						(treePath.text = TreePath.text + "\\")
					)
				)
			)
		else
			(edithack = true)
		)
	on MdlPathSet pressed do
		(
		MdlPathIn = GetSaveFileName caption:"Save MDL file as:" \
			types:".mdl files|*.mdl|" 
		if MdlPathIn != undefined then 
			(
			MdlPath.text = relative MdlPathIn
			)
		) ---- end TreePath entered	
	
	on WrkPathSet pressed do
		(
		WrkPathIn = GetSavePath caption:"Choose model source directory:"
		if WrkPathIn != undefined then WrkPath.text = relative WrkPathIn
		)
		
	on TexPathSet pressed do
		(
		TexPathIn = GetSavePath caption:"Choose texture directory:"
		if TexPathIn != undefined then TexPath.text = relative TexPathIn
		)	
	
	on EditQc pressed do
		(
		openwho =  GetOpenFileName caption:"Open .QC file:" types:".qc files|*.qc|"
		if openwho != undefined do	edit openwho
		)
	
	on Build pressed do ------ generate a model. Requires batch file in bin directory
		(
		openwho =  GetOpenFileName caption:"Compile .QC file:" types:".qc files|*.qc|"
		if openwho != undefined do
			(
			--------- this is too many lines, because a string op and a math op
			-------- can't seem to coexist in the same line ?!?!
			Toffset = ( Header.treePath.text.count ) - 1
			Tpath = substring Header.Treepath.text  1 Toffset
			cmdstring = "" as stringstream
			format "%\bin\compile_qc.bat %"   tpath openwho to:cmdstring
			cmdstring = cmdstring as string
			doscommand cmdstring
			)
		)---- end build

		
	on upd pressed do
		(
		Pathinit()
		)
	
	on Clr pressed do
		(
		edithack = false
		QCpath.text = ""
		edithack = false
		TreePath.text = ""
		edithack = false
		MdlPath.text = ""
		edithack = false
		WrkPath.text = ""
		edithack = false
		TexPath.text = ""
		edithack = true
		)	
	on GoQC pressed do
		(
		assemble_qc()
		)
	
	---------- function calls ---------
) 
-------------------------------------- Body  Rollout ------------------------------------------
-----------------------------------------------------------------------------------------------	

Rollout Body "Body Structure"
	(
	label BL0 ""
	group "Body Statement:"
		(
		
		label BL1 "VRM file:" \
			across:4 offset:[-29,3]
		
		button VRMPathSet "SET:" \
			width:32 height:18 offset:[-76,0]
			
		edittext VRMPath \
			text:"-- press to choose VRM file --" \
			fieldwidth:255 offset:[-110,0]
		
		checkbox VRMAbs "Absolute" \
			align:left	checked:false offset:[48,0] enabled:false
			
		label BL2 "Body name:" \
			across:7 offset:[0,2]
			
		edittext BodyName text:"Body" offset:[0,0] width:70
		
		label BL3 "MRM Max:" offset:[55,2] width:128
	
		spinner  MRMMax range:[0,9999,6000] scale:100 type:#integer width:45 offset:[-10,0]
		
		label BL4 "MRM Min:" offset:[5,2]
	
		spinner  MRMMin range:[0,9999,500] scale:50 type:#integer width:45 offset:[-20,0]
		
		Checkbox VRMflip "Reverse" offset:[0,0]	
		)
	

	--------------- body section functions
	 
	 fn RemPath VrmPath =
		(
		if Header.TreePath.text == "" or Header.WrkPath.text == "" then
			(
			return VrmPath
			)
		else
			(
			RootPath = Header.TreePath.text
			SrcPath = Header.WrkPath.text
			RootPath = RootPath+SrcPath
			offset = ( RootPath.count + 2 )
			return (substring VrmPath offset 999)
			)	
		)
		
	on VrmPathSet pressed do
		(
		VrmPathIn = GetOpenFileName caption:"Use VRM file:" \
			types:".VRM files|*.vrm|"
		if VrmPathIn != undefined then VrmPath.text = VrmPathIn
		VrmPath.text = (RemPath VrmPath.text)
		)
		
	)-------------- end of Body rollout


-------------------------------------- Global Mod Rollout ------------------------------------------
----------------------------------------------------------------------------------------------------	
Rollout Gmod "Global modifications"
	(
	group "Global modificiations"
		(
		label GM00 "Object origin is at:" across: 6 offset:[5,0]
		edittext ORX "X:" width:34 offset:[20,0]
		edittext ORY "Y:" width:34 offset:[-3,0]
		edittext ORZ "Z:" width:34 offset:[-18,0]
		edittext ORR "Z Rotation:" width:90 offset:[-40,0]
		edittext SCL "Scale" text:"1.0" offset:[0,0]
		)
	)--------------- end of body modification rollout

Rollout Smds "Sequences:"
	(
	button SMDGetAll "Collect .smd files" across:3
	button Addone "Add .smd file"
	on AddOne pressed do Add_smd()		
	button ClrSmd "Clear SMD list"
	on SmdGetAll pressed do	collect_smds()
	on ClrSmd pressed do (
		Smd_list = #()
		At_list = #()
		make_smd_list
		)
		
		
	)---------------- end of SMD Rollout


-------------------------------------- Window declarations -----------------------------------------
----------------------------------------------------------------------------------------------------	
-- logically these should be at the top, but the script seems to compile better
-- if they follow the rollout definitions


QCwindow = NewRolloutFloater "QC file generator" 480 640
AddRollout Header QCwindow
AddRollout Body QCwindow
AddRollout Gmod QCwindow RolledUp:true
AddRollout Smds QCwindow


