transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / radiant / brushscript.cpp
1 /*\r
2 Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.\r
4 \r
5 This file is part of GtkRadiant.\r
6 \r
7 GtkRadiant is free software; you can redistribute it and/or modify\r
8 it under the terms of the GNU General Public License as published by\r
9 the Free Software Foundation; either version 2 of the License, or\r
10 (at your option) any later version.\r
11 \r
12 GtkRadiant is distributed in the hope that it will be useful,\r
13 but WITHOUT ANY WARRANTY; without even the implied warranty of\r
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
15 GNU General Public License for more details.\r
16 \r
17 You should have received a copy of the GNU General Public License\r
18 along with GtkRadiant; if not, write to the Free Software\r
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
20 */\r
21 \r
22 // BrushScript stuff\r
23 //\r
24 \r
25 /*!\r
26 \todo is this still used / in working state?\r
27 should we cleanup and remove it for good\r
28 */\r
29 \r
30 #include "stdafx.h"\r
31 #include "gtkmisc.h"\r
32 \r
33 //\r
34 struct SVariableDef\r
35 {\r
36   CString m_strName;\r
37   CString m_strInput;\r
38   float m_fValue;\r
39 };\r
40 \r
41 struct SVecVariableDef\r
42 {\r
43   CString m_strName;\r
44   CString m_strInput;\r
45   vec3_t m_vValue;\r
46 };\r
47 \r
48 \r
49 \r
50 const int MAX_VARIABLES = 64;\r
51 \r
52 brush_t* g_pHold1 = NULL;\r
53 brush_t* g_pHold2 = NULL;\r
54 brush_t* g_pHold3 = NULL;\r
55 bool g_bRotateAroundSelection;\r
56 int g_nVariableCount;\r
57 int g_nVecVariableCount;\r
58 int g_nLoopCounter;\r
59 float g_fDefault = 9999.9f;\r
60 vec3_t g_vDefault;\r
61 bool g_bStartLoop;\r
62 char* g_pLooper;\r
63 bool g_bKeepGoing;\r
64 \r
65 SVariableDef g_Variables[MAX_VARIABLES];\r
66 SVecVariableDef g_VecVariables[MAX_VARIABLES];\r
67 \r
68 void InitForScriptRun()\r
69 {\r
70   g_pHold1 = NULL;\r
71   g_pHold2 = NULL;\r
72   g_pHold3 = NULL;\r
73   g_bRotateAroundSelection = true;\r
74   g_nVariableCount = 0;\r
75   g_nVecVariableCount = 0;\r
76   g_nLoopCounter = 0;\r
77   g_bStartLoop = false;\r
78   g_pLooper = NULL;\r
79   g_bKeepGoing = true;\r
80 }\r
81 \r
82 void AddVariable(const char* pName, float fValue, const char* pInput = NULL)\r
83 {\r
84   if (g_nVariableCount < MAX_VARIABLES)\r
85   {\r
86     g_Variables[g_nVariableCount].m_strName = pName;\r
87     g_Variables[g_nVariableCount].m_strName.MakeLower();\r
88     g_Variables[g_nVariableCount].m_fValue = fValue;\r
89     if (pInput)\r
90       g_Variables[g_nVariableCount].m_strInput = pInput;\r
91     g_nVariableCount++;\r
92   }\r
93   else\r
94     gtk_MessageBox(g_pParentWnd->m_pWidget, "Maximum script variable limit reached!");\r
95 }\r
96 \r
97 float VariableValue(const char* pName)\r
98 {\r
99   CString strName = pName;\r
100   strName.MakeLower();\r
101   for (int n = 0; n < g_nVariableCount; n++)\r
102   {\r
103     if (strName == g_Variables[n].m_strName)\r
104       return g_Variables[n].m_fValue;\r
105   }\r
106   //strName.Format("Reference to non-existant varirable %s", pName);\r
107   //g_pParentWnd->MessageBox(strName);\r
108   return g_fDefault;\r
109 }\r
110 \r
111 void SetVariableValue(const char* pName, float fValue)\r
112 {\r
113   CString strName = pName;\r
114   strName.MakeLower();\r
115   for (int n = 0; n < g_nVariableCount; n++)\r
116   {\r
117     if (strName == g_Variables[n].m_strName)\r
118       g_Variables[n].m_fValue = fValue;\r
119   }\r
120 }\r
121 \r
122 \r
123 \r
124 void AddVectorVariable(const char* pName, const char* pInput = NULL)\r
125 {\r
126   if (g_nVecVariableCount < MAX_VARIABLES)\r
127   {\r
128     g_VecVariables[g_nVecVariableCount].m_strName = pName;\r
129     g_VecVariables[g_nVecVariableCount].m_strName.MakeLower();\r
130     if (pInput)\r
131       g_VecVariables[g_nVariableCount].m_strInput = pInput;\r
132     g_nVecVariableCount++;\r
133   }\r
134   else\r
135     gtk_MessageBox(g_pParentWnd->m_pWidget, "Maximum script variable limit reached!");\r
136 }\r
137 \r
138 void VectorVariableValue(const char* pName, vec3_t& v)\r
139 {\r
140   CString strName = pName;\r
141   strName.MakeLower();\r
142   for (int n = 0; n < g_nVecVariableCount; n++)\r
143   {\r
144     if (strName == g_VecVariables[n].m_strName)\r
145     {\r
146       VectorCopy(g_VecVariables[n].m_vValue, v);\r
147       return;\r
148     }\r
149   }\r
150   strName.Format("Reference to non-existant variable %s", pName);\r
151   gtk_MessageBox(g_pParentWnd->m_pWidget, strName);\r
152 }\r
153 \r
154 void SetVectorVariableValue(const char* pName, vec3_t v)\r
155 {\r
156   CString strName = pName;\r
157   strName.MakeLower();\r
158   for (int n = 0; n < g_nVecVariableCount; n++)\r
159   {\r
160     if (strName == g_VecVariables[n].m_strName)\r
161       VectorCopy(v, g_VecVariables[n].m_vValue);\r
162   }\r
163 }\r
164 \r
165 \r
166 \r
167 \r
168 \r
169 // commands\r
170 //\r
171 // _CopySelected(nHoldPos)  \r
172 // copies selected brush to hold spot 1, 2 or 3\r
173 //\r
174 // _MoveSelected(x, y, z)\r
175 // moves selected brush by coords provided\r
176 //\r
177 // _RotateSelected(x, y, z)\r
178 // rotates selected brush by coords provided\r
179 //\r
180 // _MoveHold(nHoldPos, x, y, z)\r
181 // moves brush in hold pos by coords provided\r
182 //\r
183 // _RotateHold(nHoldPos, x, y, z)\r
184 // rotates brush in hold pos by coords provided\r
185 //\r
186 // _CopyToMap(nHoldPos)\r
187 // copies hold brush to map\r
188 //\r
189 // _CopyAndSelect(nHoldPos)\r
190 // copies hold brush to map and selects it\r
191 //\r
192 // _Input(VarName1, ... VarNamennn)\r
193 // inputs a list of values from the user\r
194 //\r
195 \r
196 typedef void (PFNScript)(char*&);\r
197 \r
198 \r
199 struct SBrushScript\r
200 {\r
201   const char* m_pName;\r
202   PFNScript* m_pProc;\r
203 };\r
204 \r
205 \r
206 const char* GetParam(char*& pBuffer)\r
207 {\r
208   static CString strParam;\r
209   bool bStringMode = false;\r
210 \r
211   while (*pBuffer != (char)NULL && isspace(*pBuffer))   // skip and whitespace\r
212     pBuffer++;\r
213 \r
214   if (*pBuffer == '(') // if it's an opening paren, skip it\r
215     pBuffer++;\r
216 \r
217   if (*pBuffer == '\"') // string ?\r
218   {\r
219     pBuffer++;\r
220     bStringMode = true;\r
221   }\r
222 \r
223   strParam = "";\r
224 \r
225   if (bStringMode)\r
226   {\r
227     while (*pBuffer != (char)NULL && *pBuffer != '\"')\r
228       strParam += *pBuffer++;\r
229   }\r
230   else\r
231   {\r
232     while (*pBuffer != (char)NULL && *pBuffer != ' ' && *pBuffer != ')' && *pBuffer != ',')\r
233       strParam += *pBuffer++;\r
234   }\r
235 \r
236   if (*pBuffer != (char)NULL)   // skip last char\r
237     pBuffer++;\r
238 \r
239   if (strParam.GetLength() > 0)\r
240   {\r
241     if (strParam.GetAt(0) == '$') // ? variable name\r
242     {\r
243       float f = VariableValue(strParam);\r
244       if (f != g_fDefault)\r
245         strParam.Format("%f", f);\r
246     }\r
247   }\r
248 \r
249   return strParam;\r
250 }\r
251 \r
252 brush_t* CopyBrush(brush_t* p)\r
253 {                            \r
254   brush_t* pCopy = Brush_Clone(p);\r
255         //Brush_AddToList (pCopy, &active_brushes);\r
256         //Entity_LinkBrush (world_entity, pCopy);\r
257         Brush_Build(pCopy, false);\r
258 \r
259   return pCopy;\r
260 }\r
261 \r
262 \r
263 void CopySelected(char*& pBuffer)\r
264 {\r
265   // expects one param\r
266   CString strParam = GetParam(pBuffer);\r
267   int n = atoi(strParam);\r
268 \r
269   brush_t* pCopy = NULL;\r
270   if (selected_brushes.next != &selected_brushes && \r
271       selected_brushes.next->next == &selected_brushes)\r
272     pCopy = selected_brushes.next;\r
273 \r
274   if (pCopy)\r
275   {\r
276     if (n == 1)\r
277     {\r
278       //if (g_pHold1)\r
279         //Brush_Free(g_pHold1);\r
280       g_pHold1 = CopyBrush(pCopy);\r
281     }\r
282     else if (n == 2)\r
283     {\r
284       //if (g_pHold2)\r
285         //Brush_Free(g_pHold2);\r
286       g_pHold2 = CopyBrush(pCopy);\r
287     }\r
288     else\r
289     {\r
290       //if (g_pHold3)\r
291         //Brush_Free(g_pHold3);\r
292       g_pHold3 = CopyBrush(pCopy);\r
293     }\r
294   }\r
295 }\r
296 \r
297 void MoveSelected(char*& pBuffer)\r
298 {\r
299   vec3_t v;\r
300   CString strParam = GetParam(pBuffer);\r
301   v[0] = atof(strParam);\r
302   strParam = GetParam(pBuffer);\r
303   v[1] = atof(strParam);\r
304   strParam = GetParam(pBuffer);\r
305   v[2] = atof(strParam);\r
306   Select_Move(v, false);\r
307   Sys_UpdateWindows(W_ALL);\r
308 }\r
309 \r
310 void RotateSelected(char*& pBuffer)\r
311 {\r
312   vec3_t v;\r
313 \r
314   if (g_bRotateAroundSelection)\r
315   {\r
316     Select_GetTrueMid(v);\r
317     VectorCopy(v, g_pParentWnd->ActiveXY()->RotateOrigin());\r
318   }\r
319 \r
320   CString strParam = GetParam(pBuffer);\r
321   v[0] = atof(strParam);\r
322   strParam = GetParam(pBuffer);\r
323   v[1] = atof(strParam);\r
324   strParam = GetParam(pBuffer);\r
325   v[2] = atof(strParam);\r
326   for (int i = 0; i < 3; i++)\r
327     if (v[i] != 0.0)\r
328       Select_RotateAxis(i, v[i], false , true);\r
329   Sys_UpdateWindows(W_ALL);\r
330 }\r
331 \r
332 void MoveHold(char*& pBuffer)\r
333 {\r
334   CString strParam = GetParam(pBuffer);\r
335   brush_t* pBrush = NULL;\r
336   int nHold = atoi(strParam);\r
337   if (nHold == 1)\r
338     pBrush = g_pHold1;\r
339   else if (nHold == 2)\r
340     pBrush = g_pHold2;\r
341   else \r
342     pBrush = g_pHold3;\r
343 \r
344   if (pBrush)\r
345   {\r
346     vec3_t v;\r
347     strParam = GetParam(pBuffer);\r
348     v[0] = atof(strParam);\r
349     strParam = GetParam(pBuffer);\r
350     v[1] = atof(strParam);\r
351     strParam = GetParam(pBuffer);\r
352     v[2] = atof(strParam);\r
353                 Brush_Move (pBrush, v, false);\r
354   }\r
355 }\r
356 \r
357 void RotateHold(char*& pBuffer)\r
358 {\r
359   CString strParam = GetParam(pBuffer);\r
360   brush_t* pBrush = NULL;\r
361   int nHold = atoi(strParam);\r
362   if (nHold == 1)\r
363     pBrush = g_pHold1;\r
364   else if (nHold == 2)\r
365     pBrush = g_pHold2;\r
366   else \r
367     pBrush = g_pHold3;\r
368 \r
369   if (pBrush)\r
370   {\r
371     vec3_t v;\r
372     strParam = GetParam(pBuffer);\r
373     v[0] = atof(strParam);\r
374     strParam = GetParam(pBuffer);\r
375     v[1] = atof(strParam);\r
376     strParam = GetParam(pBuffer);\r
377     v[2] = atof(strParam);\r
378     for (int i = 0; i < 3; i++)\r
379       if (v[i] != 0.0)\r
380         Select_RotateAxis(i, v[i]);\r
381   }\r
382 }\r
383 \r
384 void CopyToMap(char*& pBuffer)\r
385 {\r
386   CString strParam = GetParam(pBuffer);\r
387   brush_t* pBrush = NULL;\r
388   int nHold = atoi(strParam);\r
389   if (nHold == 1)\r
390     pBrush = g_pHold1;\r
391   else if (nHold == 2)\r
392     pBrush = g_pHold2;\r
393   else \r
394     pBrush = g_pHold3;\r
395 \r
396   if (pBrush)\r
397   {\r
398     Brush_AddToList(pBrush, &active_brushes);\r
399                 Entity_LinkBrush (world_entity, pBrush);\r
400                 Brush_Build(pBrush, false);\r
401                 \r
402     Sys_UpdateWindows(W_ALL);\r
403   }\r
404 }\r
405 \r
406 void CopyAndSelect(char*& pBuffer)\r
407 {\r
408   CString strParam = GetParam(pBuffer);\r
409   brush_t* pBrush = NULL;\r
410   int nHold = atoi(strParam);\r
411   if (nHold == 1)\r
412     pBrush = g_pHold1;\r
413   else if (nHold == 2)\r
414     pBrush = g_pHold2;\r
415   else \r
416     pBrush = g_pHold3;\r
417 \r
418   if (pBrush)\r
419   {\r
420     Select_Deselect();\r
421     Brush_AddToList(pBrush, &active_brushes);\r
422                 Entity_LinkBrush (world_entity, pBrush);\r
423                 Brush_Build(pBrush, false);\r
424 \r
425         Select_Brush(pBrush);\r
426     Sys_UpdateWindows(W_ALL);\r
427   }\r
428 }\r
429 \r
430 void Input(char*& pBuffer)\r
431 {\r
432   bool bGo = false;\r
433   const char *fields[5] = { "", "", "", "", "" };\r
434   float values[5];\r
435 \r
436   for (int n = 0; n < g_nVariableCount; n++)\r
437   {\r
438     if (g_Variables[n].m_strInput.GetLength() > 0)\r
439     {\r
440       bGo = true;\r
441       if (n < 5)\r
442       {\r
443         switch (n)\r
444         {\r
445           case 0 : fields[1] = g_Variables[n].m_strInput.GetBuffer (); break;\r
446           case 1 : fields[2] = g_Variables[n].m_strInput.GetBuffer (); break;\r
447           case 2 : fields[3] = g_Variables[n].m_strInput.GetBuffer (); break;\r
448           case 3 : fields[4] = g_Variables[n].m_strInput.GetBuffer (); break;\r
449           case 4 : fields[5] = g_Variables[n].m_strInput.GetBuffer (); break;\r
450         }\r
451       }\r
452     }\r
453   }\r
454 \r
455   if (bGo)\r
456   {\r
457     if (DoBSInputDlg (fields, values) == IDOK)\r
458     {\r
459       for (int n = 0; n < g_nVariableCount; n++)\r
460       {\r
461         if (g_Variables[n].m_strInput.GetLength() > 0)\r
462         {\r
463           if (n < 5)\r
464           {\r
465             switch (n)\r
466             {\r
467               case 0 : g_Variables[n].m_fValue = values[1]; break;\r
468               case 1 : g_Variables[n].m_fValue = values[2]; break;\r
469               case 2 : g_Variables[n].m_fValue = values[3]; break;\r
470               case 3 : g_Variables[n].m_fValue = values[4]; break;\r
471               case 4 : g_Variables[n].m_fValue = values[5]; break;\r
472             }\r
473           }\r
474         }\r
475       }\r
476     }\r
477     else g_bKeepGoing = false;\r
478   }\r
479 }\r
480 \r
481 bool g_bWaiting;\r
482 void _3DPointDone(bool b, int n)\r
483 {\r
484   g_bWaiting = false;\r
485 }\r
486 \r
487 void _3DPointInput(char*& pBuffer)\r
488 {\r
489   CString strParam = GetParam(pBuffer);\r
490   CString strParam2 = GetParam(pBuffer);\r
491   ShowInfoDialog(strParam2);\r
492   AddVectorVariable(strParam, strParam2);\r
493   g_bWaiting = true;\r
494   AcquirePath(2, &_3DPointDone);\r
495   while (g_bWaiting)\r
496     gtk_main_iteration ();\r
497   HideInfoDialog();\r
498   SetVectorVariableValue(strParam, g_PathPoints[0]);\r
499 }\r
500 \r
501 void SetRotateOrigin(char*& pBuffer)\r
502 {\r
503   vec3_t v;\r
504   CString strParam = GetParam(pBuffer);\r
505   VectorVariableValue(strParam, v);\r
506   VectorCopy(v, g_pParentWnd->ActiveXY()->RotateOrigin());\r
507   g_bRotateAroundSelection = false;\r
508 }\r
509 \r
510 void InputVar(char*& pBuffer)\r
511 {\r
512   CString strParam = GetParam(pBuffer);\r
513   CString strParam2 = GetParam(pBuffer);\r
514   AddVariable(strParam, 0.0, strParam2);\r
515 }\r
516 \r
517 void LoopCount(char*& pBuffer)\r
518 {\r
519   CString strParam = GetParam(pBuffer);\r
520   g_nLoopCounter = atoi(strParam);\r
521   if (g_nLoopCounter == 0)\r
522     g_nLoopCounter = (int)VariableValue(strParam);\r
523   if (g_nLoopCounter > 0)\r
524     g_pLooper = pBuffer;\r
525 }\r
526 \r
527 void LoopRun(char*& pBuffer)\r
528 {\r
529   if (g_bStartLoop == true)\r
530   {\r
531     g_nLoopCounter--;\r
532     if (g_nLoopCounter == 0)\r
533     {\r
534       g_bStartLoop = false;\r
535       GetParam(pBuffer);\r
536     }\r
537     else\r
538       pBuffer = g_pLooper;\r
539   }\r
540   else\r
541   {\r
542     if (g_pLooper && g_nLoopCounter > 0)\r
543     {\r
544       g_bStartLoop = true;\r
545       pBuffer = g_pLooper;\r
546     }\r
547     else\r
548     {\r
549       GetParam(pBuffer);\r
550     }\r
551   }\r
552 }\r
553 \r
554 \r
555 void ConfirmMessage(char*& pBuffer)\r
556 {\r
557   CString strParam = GetParam(pBuffer);\r
558   if (gtk_MessageBox(g_pParentWnd->m_pWidget, strParam, "Script Info", MB_OKCANCEL) == IDCANCEL)\r
559     g_bKeepGoing = false;\r
560 }\r
561 \r
562 void Spherize(char*& pBuffer)\r
563 {\r
564   g_bScreenUpdates = false;\r
565   for (int n = 0; n < 120; n += 36)\r
566   {\r
567     for (int i = 0; i < 360; i += 36)\r
568     {\r
569       Select_RotateAxis(0, i, false , true);\r
570       CSG_Subtract();\r
571     }\r
572     Select_RotateAxis(2, n, false , true);\r
573   }\r
574   g_bScreenUpdates = true;\r
575 }\r
576 \r
577 void RunIt(char*& pBuffer);\r
578 SBrushScript g_ScriptCmds[] =\r
579 {\r
580   {"_CopySelected", &CopySelected},\r
581   {"_MoveSelected", &MoveSelected},\r
582   {"_RotateSelected", &RotateSelected},\r
583   {"_MoveHold", &MoveHold},\r
584   {"_RotateHold", &RotateHold},\r
585   {"_CopyToMap", &CopyToMap},\r
586   {"_CopyAndSelect", &CopyAndSelect},\r
587   {"_Input", &Input},\r
588   {"_3DPointInput", &_3DPointInput},\r
589   {"_SetRotateOrigin", &SetRotateOrigin},\r
590   {"_InputVar", &InputVar},\r
591   {"_LoopCount", &LoopCount},\r
592   {"_LoopRun", &LoopRun},\r
593   {"_ConfirmMessage", &ConfirmMessage},\r
594   {"_Spherize", &Spherize},\r
595   {"_RunScript", RunIt}\r
596 };\r
597 \r
598 const int g_nScriptCmdCount = sizeof(g_ScriptCmds) / sizeof(SBrushScript);\r
599 \r
600 void RunScript(char* pBuffer)\r
601 {\r
602   g_pHold1 = NULL;\r
603   g_pHold2 = NULL;\r
604   g_pHold3 = NULL;\r
605 \r
606   while (g_bKeepGoing && pBuffer && *pBuffer)\r
607   {\r
608     while (*pBuffer != (char)NULL && *pBuffer != '_')\r
609       pBuffer++;\r
610 \r
611     char* pTemp = pBuffer;\r
612     int nLen = 0;\r
613     while (*pTemp != (char)NULL && *pTemp != '(')\r
614     {\r
615       pTemp++;\r
616       nLen++;\r
617     }\r
618     if (*pBuffer != (char)NULL)\r
619     {\r
620       bool bFound = false;\r
621       for (int i = 0; i < g_nScriptCmdCount; i++)\r
622       {\r
623         //if (strnicmp(g_ScriptCmds[i].m_pName, pBuffer, strlen(g_ScriptCmds[i].m_pName)) ==  0)\r
624         if (strnicmp(g_ScriptCmds[i].m_pName, pBuffer, nLen) ==  0)\r
625         {\r
626           pBuffer += strlen(g_ScriptCmds[i].m_pName);\r
627           g_ScriptCmds[i].m_pProc(pBuffer);\r
628           if (g_bStartLoop)\r
629           {\r
630           }\r
631           bFound = true;\r
632           break;\r
633         }\r
634       }\r
635       if (!bFound)\r
636         pBuffer++;\r
637     }\r
638   }\r
639 }\r
640 \r
641 \r
642 void RunScriptByName(char* pBuffer, bool bInit)\r
643 {\r
644   if (bInit)\r
645     InitForScriptRun();\r
646   char* pScript = new char[4096];\r
647   CString strINI;\r
648   strINI = g_strGameToolsPath;\r
649   strINI += "/scripts.ini";\r
650   CString strScript;\r
651   FILE *f;\r
652 \r
653   f = fopen (strINI.GetBuffer(), "rt");\r
654   if (f != NULL)\r
655   {\r
656     char line[1024], *ptr;\r
657 \r
658     // read section names\r
659     while (fgets (line, 1024, f) != 0)\r
660     {\r
661       if (line[0] != '[')\r
662         continue;\r
663 \r
664       ptr = strchr (line, ']');\r
665       *ptr = '\0';\r
666 \r
667       if (strcmp (line, pScript) == 0)\r
668       {\r
669         while (fgets (line, 1024, f) != 0)\r
670         {\r
671           if ((strchr (line, '=') == NULL) ||\r
672               strlen (line) == 0)\r
673             break;\r
674           strScript += line;\r
675         }\r
676         break;\r
677       }\r
678     }\r
679     fclose (f);\r
680   }\r
681   RunScript((char*)strScript.GetBuffer());\r
682 }\r
683 \r
684 \r
685 void RunIt(char*& pBuffer)\r
686 {\r
687   brush_t* p1 = g_pHold1;\r
688   brush_t* p2 = g_pHold2;\r
689   brush_t* p3 = g_pHold3;\r
690 \r
691   CString strParam = GetParam(pBuffer);\r
692   RunScriptByName((char*)strParam.GetBuffer(), false);\r
693 \r
694   g_pHold3 = p3;\r
695   g_pHold2 = p2;\r
696   g_pHold1 = p1;\r
697 }\r
698 \r