]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/tools/particle/DialogParticleEditor.cpp
hello world
[icculus/iodoom3.git] / neo / tools / particle / DialogParticleEditor.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #include "../../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "../../game/game.h"
33 #include "../../sys/win32/win_local.h"
34 #include "../../sys/win32/rc/common_resource.h"
35 #include "../../sys/win32/rc/Radiant_resource.h"
36 #include "../../sys/win32/rc/ParticleEditor_resource.h"
37 #include "../comafx/DialogName.h"
38 #include "../comafx/VectorCtl.h"
39 #include "../comafx/DialogColorPicker.h"
40 #include "../radiant/GLWidget.h"
41 #include "../radiant/PreviewDlg.h"
42
43 #include "DialogParticleEditor.h"
44
45 #ifdef ID_DEBUG_MEMORY
46 #undef new
47 #undef DEBUG_NEW
48 #define DEBUG_NEW new
49 #endif
50
51 const int StageEnableID[] = {
52         IDC_EDIT_MATERIAL,
53         IDC_BUTTON_BROWSEMATERIAL,
54         IDC_EDIT_ANIMFRAMES,
55         IDC_EDIT_ANIMRATE,
56         IDC_EDIT_COLOR,
57         IDC_BUTTON_BROWSECOLOR,
58         IDC_EDIT_FADECOLOR,
59         IDC_BUTTON_BROWSEFADECOLOR,
60         IDC_EDIT_FADEIN,
61         IDC_SLIDER_FADEIN,
62         IDC_EDIT_FADEOUT,
63         IDC_EDIT_FADEFRACTION,
64         IDC_SLIDER_FADEOUT,
65         IDC_SLIDER_FADEFRACTION,
66         IDC_EDIT_BUNCHING,
67         IDC_SLIDER_BUNCHING,
68         IDC_EDIT_COUNT,
69         IDC_SLIDER_COUNT,
70         IDC_EDIT_TIME,
71         IDC_SLIDER_TIME,
72         IDC_EDIT_CYCLES,
73         IDC_EDIT_TIMEOFFSET,
74         IDC_EDIT_DEADTIME,
75         IDC_CHECK_WORLDGRAVITY,
76         IDC_EDIT_GRAVITY,
77         IDC_SLIDER_GRAVITY,
78         IDC_RADIO_RECT,
79         IDC_RADIO_CYLINDER,
80         IDC_RADIO_SPHERE,
81         IDC_EDIT_OFFSET,
82         IDC_EDIT_XSIZE,
83         IDC_EDIT_YSIZE,
84         IDC_EDIT_ZSIZE,
85         IDC_EDIT_RINGOFFSET,
86         IDC_RADIO_CONE,
87         IDC_RADIO_OUTWARD,
88         IDC_EDIT_DIRECTIONPARM,
89         IDC_RADIO_AIMED,
90         IDC_RADIO_VIEW,
91         IDC_RADIO_X,
92         IDC_RADIO_Y,
93         IDC_RADIO_Z,
94         IDC_EDIT_ORIENTATIONPARM1,
95         IDC_EDIT_ORIENTATIONPARM2,
96         IDC_SLIDER_SPEEDFROM,
97         IDC_EDIT_SPEEDFROM,
98         IDC_EDIT_SPEEDTO,
99         IDC_SLIDER_SPEEDTO,
100         IDC_SLIDER_ROTATIONFROM,
101         IDC_EDIT_ROTATIONFROM,
102         IDC_EDIT_ROTATIONTO,
103         IDC_SLIDER_ROTATIONTO,
104         IDC_SLIDER_SIZEFROM,
105         IDC_EDIT_SIZEFROM,
106         IDC_EDIT_SIZETO,
107         IDC_SLIDER_SIZETO,
108         IDC_SLIDER_ASPECTFROM,
109         IDC_EDIT_ASPECTFROM,
110         IDC_EDIT_ASPECTTO,
111         IDC_SLIDER_ASPECTTO,
112         IDC_COMBO_CUSTOMPATH,
113         IDC_CHECK_ENTITYCOLOR,
114 };
115
116 const int StageIDCount = sizeof( StageEnableID ) / sizeof ( const int );
117
118 const int EditEnableID[] = {
119         IDC_BUTTON_XDN,
120         IDC_BUTTON_XUP,
121         IDC_BUTTON_YUP,
122         IDC_BUTTON_YDN,
123         IDC_BUTTON_ZUP,
124         IDC_BUTTON_ZDN,
125         IDC_BUTTON_DROPEMITTER,
126         IDC_BUTTON_VECTOR,
127         IDC_BUTTON_BROWSECOLOR_ENTITY
128 };
129
130 const int EditIDCount = sizeof ( EditEnableID ) / sizeof ( const int );
131
132
133 CDialogParticleEditor *g_ParticleDialog = NULL;
134
135
136 /*
137 ================
138 ParticleEditorInit
139 ================
140 */
141 void ParticleEditorInit( const idDict *spawnArgs ) {
142
143         if ( renderSystem->IsFullScreen() ) {
144                 common->Printf( "Cannot run the particle editor in fullscreen mode.\n"
145                         "Set r_fullscreen to 0 and vid_restart.\n" );
146                 return;
147         }
148
149         if ( g_ParticleDialog == NULL ) {
150                 InitAfx();
151                 g_ParticleDialog = new CDialogParticleEditor();
152         }
153
154         if ( g_ParticleDialog->GetSafeHwnd() == NULL) {
155                 g_ParticleDialog->Create( IDD_DIALOG_PARTICLE_EDITOR );
156                 /*
157                 // FIXME: restore position
158                 CRect rct;
159                 g_AFDialog->SetWindowPos( NULL, rct.left, rct.top, 0, 0, SWP_NOSIZE );
160                 */
161         }
162
163         idKeyInput::ClearStates();
164
165         g_ParticleDialog->ShowWindow( SW_SHOW );
166         g_ParticleDialog->SetFocus();
167
168         if ( spawnArgs ) {
169                 idStr str = spawnArgs->GetString( "model" );
170                 str.StripFileExtension();
171                 g_ParticleDialog->SelectParticle( str );
172                 g_ParticleDialog->SetParticleVisualization( static_cast<int>( CDialogParticleEditor::SELECTED ) );
173         }
174
175         cvarSystem->SetCVarBool( "r_useCachedDynamicModels", false );
176 }
177
178
179 /*
180 ================
181 ParticleEditorRun
182 ================
183 */
184 void ParticleEditorRun( void ) {
185 #if _MSC_VER >= 1300
186         MSG *msg = AfxGetCurrentMessage();                      // TODO Robert fix me!!
187 #else
188         MSG *msg = &m_msgCur;
189 #endif
190
191         while( ::PeekMessage(msg, NULL, NULL, NULL, PM_NOREMOVE) ) {
192                 // pump message
193                 if ( !AfxGetApp()->PumpMessage() ) {
194                 }
195         }
196 }
197
198 /*
199 ================
200 ParticleEditorShutdown
201 ================
202 */
203 void ParticleEditorShutdown( void ) {
204         delete g_ParticleDialog;
205         g_ParticleDialog = NULL;
206 }
207
208
209 // CDialogParticleEditor dialog
210
211 IMPLEMENT_DYNAMIC(CDialogParticleEditor, CDialog)
212 CDialogParticleEditor::CDialogParticleEditor(CWnd* pParent /*=NULL*/)
213         : CDialog(CDialogParticleEditor::IDD, pParent)
214         , matName(_T(""))
215         , animFrames(_T(""))
216         , animRate(_T(""))
217         , color(_T(""))
218         , fadeColor(_T(""))
219         , fadeIn(_T(""))
220         , fadeOut(_T(""))
221         , fadeFraction(_T(""))
222         , count(_T(""))
223         , time(_T(""))
224         , timeOffset(_T(""))
225         , deadTime(_T(""))
226         , gravity(_T(""))
227         , bunching(_T(""))
228         , offset(_T(""))
229         , xSize(_T(""))
230         , ySize(_T(""))
231         , zSize(_T(""))
232         , ringOffset(_T(""))
233         , directionParm(_T(""))
234         , direction(0)
235         , orientation(0)
236         , distribution(0)
237         , viewOrigin(_T(""))
238         , speedFrom(_T(""))
239         , speedTo(_T(""))
240         , rotationFrom(_T(""))
241         , rotationTo(_T(""))
242         , sizeFrom(_T(""))
243         , sizeTo(_T(""))
244         , aspectFrom(_T(""))
245         , aspectTo(_T(""))
246         , customPath(_T(""))
247         , customParms(_T(""))
248         , trails(_T(""))
249         , trailTime(_T(""))
250         , worldGravity(TRUE)
251         , entityColor(TRUE)
252         , randomDistribution(TRUE)
253         , initialAngle(_T(""))
254         , boundsExpansion(_T(""))
255         , customDesc(_T(""))
256         , particleMode(FALSE)
257 {
258         visualization = TESTMODEL;
259         mapModified = false;
260 }
261
262 CDialogParticleEditor::~CDialogParticleEditor() {
263 }
264
265 void CDialogParticleEditor::DoDataExchange(CDataExchange* pDX) {
266         CDialog::DoDataExchange(pDX);
267         DDX_Text(pDX, IDC_EDIT_DEPTHHACK, depthHack);
268         DDX_Check(pDX, IDC_CHECK_WORLDGRAVITY, worldGravity);
269         DDX_Check(pDX, IDC_CHECK_ENTITYCOLOR, entityColor);
270         DDX_Control(pDX, IDC_COMBO_PARTICLES, comboParticle);
271         DDX_Control(pDX, IDC_LIST_STAGES, listStages);
272         DDX_Text(pDX, IDC_COMBO_CUSTOMPATH, customPath );
273         DDX_Text(pDX, IDC_EDIT_CUSTOMPARMS, customParms );
274         DDX_Text(pDX, IDC_EDIT_MATERIAL, matName);
275         DDX_Text(pDX, IDC_EDIT_ANIMFRAMES, animFrames);
276         DDX_Text(pDX, IDC_EDIT_ANIMRATE, animRate);
277         DDX_Text(pDX, IDC_EDIT_COLOR, color);
278         DDX_Text(pDX, IDC_EDIT_FADECOLOR, fadeColor);
279         DDX_Text(pDX, IDC_EDIT_FADEIN, fadeIn);
280         DDX_Text(pDX, IDC_EDIT_FADEOUT, fadeOut);
281         DDX_Text(pDX, IDC_EDIT_FADEFRACTION, fadeFraction);
282         DDX_Text(pDX, IDC_EDIT_COUNT, count);
283         DDX_Text(pDX, IDC_EDIT_TIME, time);
284         DDX_Text(pDX, IDC_EDIT_TIMEOFFSET, timeOffset);
285         DDX_Text(pDX, IDC_EDIT_DEADTIME, deadTime);
286         DDX_Text(pDX, IDC_EDIT_GRAVITY, gravity);
287         DDX_Text(pDX, IDC_EDIT_BUNCHING, bunching);
288         DDX_Text(pDX, IDC_EDIT_OFFSET, offset);
289         DDX_Text(pDX, IDC_EDIT_XSIZE, xSize);
290         DDX_Text(pDX, IDC_EDIT_YSIZE, ySize);
291         DDX_Text(pDX, IDC_EDIT_ZSIZE, zSize);
292         DDX_Text(pDX, IDC_EDIT_RINGOFFSET, ringOffset);
293         DDX_Text(pDX, IDC_EDIT_CYCLES, cycles);
294         DDX_Control(pDX, IDC_STATIC_DIRPARM, staticDirectionParm);
295         DDX_Text(pDX, IDC_EDIT_DIRECTIONPARM, directionParm);
296         DDX_Text(pDX, IDC_EDIT_SPEEDFROM, speedFrom);
297         DDX_Text(pDX, IDC_EDIT_SPEEDTO, speedTo);
298         DDX_Text(pDX, IDC_EDIT_ROTATIONFROM, rotationFrom);
299         DDX_Text(pDX, IDC_EDIT_ROTATIONTO, rotationTo);
300         DDX_Text(pDX, IDC_EDIT_SIZEFROM, sizeFrom);
301         DDX_Text(pDX, IDC_EDIT_SIZETO, sizeTo);
302         DDX_Text(pDX, IDC_EDIT_ASPECTFROM, aspectFrom);
303         DDX_Text(pDX, IDC_EDIT_ASPECTTO, aspectTo);
304         DDX_Text(pDX, IDC_EDIT_ORIENTATIONPARM1, trails);
305         DDX_Text(pDX, IDC_EDIT_ORIENTATIONPARM2, trailTime);
306         DDX_Check(pDX, IDC_CHECK_RANDOMDISTRIBUTION, randomDistribution);
307         DDX_Text(pDX, IDC_EDIT_INITIALANGLE, initialAngle);
308         DDX_Text(pDX, IDC_EDIT_BOUNDSEXPANSION, boundsExpansion);
309         DDX_Text(pDX, IDC_STATIC_DESC, customDesc);
310         DDX_Control(pDX, IDC_EDIT_RINGOFFSET, editRingOffset);
311         DDX_Radio(pDX, IDC_RADIO_RECT, distribution);
312         DDX_Radio(pDX, IDC_RADIO_CONE, direction);
313         DDX_Radio(pDX, IDC_RADIO_VIEW, orientation);
314         DDX_Control(pDX, IDC_SLIDER_BUNCHING, sliderBunching);
315         DDX_Control(pDX, IDC_SLIDER_FADEIN, sliderFadeIn);
316         DDX_Control(pDX, IDC_SLIDER_FADEOUT, sliderFadeOut);
317         DDX_Control(pDX, IDC_SLIDER_FADEFRACTION, sliderFadeFraction);
318         DDX_Control(pDX, IDC_SLIDER_COUNT, sliderCount);
319         DDX_Control(pDX, IDC_SLIDER_TIME, sliderTime);
320         DDX_Control(pDX, IDC_SLIDER_GRAVITY, sliderGravity);
321         DDX_Control(pDX, IDC_SLIDER_SPEEDFROM, sliderSpeedFrom);
322         DDX_Control(pDX, IDC_SLIDER_SPEEDTO, sliderSpeedTo);
323         DDX_Control(pDX, IDC_SLIDER_ROTATIONFROM, sliderRotationFrom);
324         DDX_Control(pDX, IDC_SLIDER_ROTATIONTO, sliderRotationTo);
325         DDX_Control(pDX, IDC_SLIDER_SIZEFROM, sliderSizeFrom);
326         DDX_Control(pDX, IDC_SLIDER_SIZETO, sliderSizeTo);
327         DDX_Control(pDX, IDC_SLIDER_ASPECTFROM, sliderAspectFrom);
328         DDX_Control(pDX, IDC_SLIDER_ASPECTTO, sliderAspectTo);
329         DDX_Control(pDX, IDC_BUTTON_VECTOR, vectorControl);
330 }
331
332
333 BEGIN_MESSAGE_MAP(CDialogParticleEditor, CDialog)
334         ON_BN_CLICKED(IDC_BUTTON_ADDSTAGE, OnBnClickedButtonAddstage)
335         ON_BN_CLICKED(IDC_BUTTON_REMOVESTAGE, OnBnClickedButtonRemovestage)
336         ON_BN_CLICKED(IDC_BUTTON_BROWSEMATERIAL, OnBnClickedButtonBrowsematerial)
337         ON_BN_CLICKED(IDC_BUTTON_BROWSECOLOR, OnBnClickedButtonBrowsecolor)
338         ON_BN_CLICKED(IDC_BUTTON_BROWSEFADECOLOR, OnBnClickedButtonBrowsefadecolor)
339         ON_BN_CLICKED(IDC_BUTTON_BROWSECOLOR_ENTITY, OnBnClickedButtonBrowseEntitycolor)
340         ON_BN_CLICKED(IDC_BUTTON_UPDATE, OnBnClickedButtonUpdate)
341         ON_CBN_SELCHANGE(IDC_COMBO_PARTICLES, OnCbnSelchangeComboParticles)
342         ON_CBN_SELCHANGE(IDC_COMBO_CUSTOMPATH, OnCbnSelchangeComboPath)
343         ON_BN_CLICKED(IDC_RADIO_RECT, OnBnClickedRadioRect)
344         ON_BN_CLICKED(IDC_RADIO_SPHERE, OnBnClickedRadioSphere)
345         ON_BN_CLICKED(IDC_RADIO_CYLINDER, OnBnClickedRadioCylinder)
346         ON_BN_CLICKED(IDC_RADIO_CONE, OnBnClickedRadioCone)
347         ON_BN_CLICKED(IDC_RADIO_OUTWARD, OnBnClickedRadioOutward)
348         ON_BN_CLICKED(IDC_RADIO_VIEW, OnBnClickedRadioView)
349         ON_BN_CLICKED(IDC_RADIO_AIMED, OnBnClickedRadioAimed)
350         ON_BN_CLICKED(IDC_RADIO_X, OnBnClickedRadioX)
351         ON_BN_CLICKED(IDC_RADIO_Y, OnBnClickedRadioY)
352         ON_BN_CLICKED(IDC_RADIO_Z, OnBnClickedRadioZ)
353         ON_BN_CLICKED(IDC_BUTTON_HIDESTAGE, OnBnClickedButtonHidestage)
354         ON_BN_CLICKED(IDC_BUTTON_SHOWSTAGE, OnBnClickedButtonShowstage)
355         ON_BN_CLICKED(IDC_CHECK_WORLDGRAVITY, OnBnClickedWorldGravity)
356         ON_BN_CLICKED(IDC_CHECK_ENTITYCOLOR, OnBnClickedEntityColor)
357         ON_LBN_SELCHANGE(IDC_LIST_STAGES, OnLbnSelchangeListStages)
358         ON_BN_CLICKED(IDC_BUTTON_NEW, OnBnClickedButtonNew)
359         ON_BN_CLICKED(IDC_BUTTON_SAVE_PARTICLE, OnBnClickedButtonSave)
360         ON_BN_CLICKED(IDC_BUTTON_SAVE_PARTICLE_AS, OnBnClickedButtonSaveAs)
361         ON_BN_CLICKED(IDC_BUTTON_SAVE_PARTICLEENTITIES, OnBnClickedButtonSaveParticles)
362         ON_BN_CLICKED(IDC_BUTTON_TESTMODEL, OnBnClickedTestModel)
363         ON_BN_CLICKED(IDC_BUTTON_IMPACT, OnBnClickedImpact)
364         ON_BN_CLICKED(IDC_BUTTON_MUZZLE, OnBnClickedMuzzle)
365         ON_BN_CLICKED(IDC_BUTTON_FLIGHT, OnBnClickedFlight)
366         ON_BN_CLICKED(IDC_BUTTON_SELECTED, OnBnClickedSelected)
367         ON_BN_CLICKED(IDC_BUTTON_DOOM, OnBnClickedDoom)
368         ON_BN_CLICKED(IDC_CHECK_EDITPARTICLEMODE, OnBnClickedParticleMode)
369         ON_BN_CLICKED(IDC_BUTTON_DROPEMITTER, OnBtnDrop)
370         ON_BN_CLICKED(IDC_BUTTON_YUP, OnBtnYup)
371         ON_BN_CLICKED(IDC_BUTTON_YDN, OnBtnYdn)
372         ON_BN_CLICKED(IDC_BUTTON_XDN, OnBtnXdn)
373         ON_BN_CLICKED(IDC_BUTTON_XUP, OnBtnXup)
374         ON_BN_CLICKED(IDC_BUTTON_ZUP, OnBtnZup)
375         ON_BN_CLICKED(IDC_BUTTON_ZDN, OnBtnZdn)
376         ON_WM_HSCROLL()
377 END_MESSAGE_MAP()
378
379
380 // CDialogParticleEditor message handlers
381
382 void CDialogParticleEditor::OnBnClickedParticleMode() {
383         particleMode = !particleMode;
384         cvarSystem->SetCVarInteger( "g_editEntityMode", ( particleMode ) ? 4 : 0 );
385         EnableEditControls();
386 }
387
388
389 void CDialogParticleEditor::OnBnClickedButtonSaveAs() {
390         idDeclParticle *idp = GetCurParticle();
391         if ( idp == NULL ) {
392                 return;
393         }
394         DialogName dlg("New Particle");
395         if (dlg.DoModal() == IDOK) {
396                 if ( declManager->FindType( DECL_PARTICLE, dlg.m_strName, false ) ) {
397                         MessageBox( "Particle already exists!", "Particle exists", MB_OK );
398                         return;
399                 }
400                 CFileDialog dlgSave( TRUE, "prt", NULL, OFN_CREATEPROMPT, "Particle Files (*.prt)|*.prt||All Files (*.*)|*.*||", AfxGetMainWnd() );
401                 if ( dlgSave.DoModal() == IDOK ) {
402                         idStr fileName;
403                         fileName = fileSystem->OSPathToRelativePath( dlgSave.m_ofn.lpstrFile );
404                         idDeclParticle *decl = dynamic_cast<idDeclParticle*>( declManager->CreateNewDecl( DECL_PARTICLE, dlg.m_strName, fileName ) );
405                         if ( decl ) {
406                                 decl->stages.DeleteContents( true );
407                                 decl->depthHack = idp->depthHack;
408                                 for ( int i = 0; i < idp->stages.Num(); i++ ) {
409                                         idParticleStage *stage = new idParticleStage();
410                                         *stage = *idp->stages[i];
411                                         decl->stages.Append( stage );
412                                 }
413                                 EnumParticles();
414                                 int index = comboParticle.FindStringExact( -1, dlg.m_strName );
415                                 if ( index >= 0 ) {
416                                         comboParticle.SetCurSel( index );
417                                 }
418                                 OnBnClickedButtonSave();
419                                 OnCbnSelchangeComboParticles();
420                                 OnBnClickedButtonUpdate();
421                         }
422                 }
423         }
424 }
425
426
427 void CDialogParticleEditor::OnBnClickedButtonSaveParticles() {
428         cmdSystem->BufferCommandText( CMD_EXEC_NOW, "saveParticles" );
429         CWnd *wnd = GetDlgItem( IDC_BUTTON_SAVE_PARTICLEENTITIES );
430         if ( wnd ) {
431                 wnd->EnableWindow( FALSE );
432         }
433 }
434
435 void CDialogParticleEditor::OnBnClickedButtonAddstage() {
436         AddStage();
437 }
438
439 void CDialogParticleEditor::OnBnClickedButtonRemovestage() {
440         RemoveStage();
441 }
442
443 void CDialogParticleEditor::OnBnClickedButtonBrowsematerial() {
444         CPreviewDlg matDlg( this );
445         matDlg.SetMode(CPreviewDlg::MATERIALS, "particles" );
446         matDlg.SetDisablePreview( true );
447         if ( matDlg.DoModal() == IDOK ) {
448                 matName = matDlg.mediaName;
449                 DlgVarsToCurStage();
450                 CurStageToDlgVars();
451         }
452 }
453
454 void CDialogParticleEditor::OnBnClickedButtonBrowsecolor() {
455         int r, g, b;
456         float ob;
457         idParticleStage *ps = GetCurStage();
458         if ( ps == NULL ) {
459                 return;
460         }
461         r = ps->color.x * 255.0f;
462         g = ps->color.y * 255.0f;
463         b = ps->color.z * 255.0f;
464         ob = 1.0f;
465         if ( DoNewColor( &r, &g, &b, &ob ) ) {
466                 color.Format( "%f %f %f %f", (float)r / 255.0f, (float)g / 255.0f, (float)b / 255.0f, 1.0f );
467                 DlgVarsToCurStage();
468                 CurStageToDlgVars();
469         }
470 }
471
472 void CDialogParticleEditor::OnBnClickedButtonBrowseEntitycolor() {
473         int r, g, b;
474         float ob;
475         idList<idEntity*> list;
476         idDict dict2;
477         idStr str;
478         list.SetNum( 128 );
479         int count = gameEdit->GetSelectedEntities( list.Ptr(), list.Num() );
480         list.SetNum( count );
481
482         if ( count ) {
483                 const idDict *dict = gameEdit->EntityGetSpawnArgs( list[0] );
484                 if ( dict ) {
485                         idVec3 clr = dict->GetVector( "_color", "1 1 1" );
486                         r = clr.x * 255.0f;
487                         g = clr.y * 255.0f;
488                         b = clr.z * 255.0f;
489                         ob = 1.0f;
490                         if ( DoNewColor( &r, &g, &b, &ob ) ) {
491                                 for ( int i = 0; i < count; i++ ) {
492                                         dict = gameEdit->EntityGetSpawnArgs( list[i] );
493                                         const char *name = dict->GetString( "name" );
494                                         idEntity *ent = gameEdit->FindEntity( name );
495                                         if ( ent ) {
496                                                 gameEdit->EntitySetColor( ent, idVec3( (float)r / 255.0f, (float)g / 255.0f, (float)b / 255.0f ) );
497                                                 str = va( "%f %f %f", (float)r / 255.0f, (float)g / 255.0f, (float)b / 255.0f );
498                                                 dict2.Clear();
499                                                 dict2.Set( "_color", str );
500                                                 gameEdit->EntityChangeSpawnArgs( ent, &dict2 );
501                                                 gameEdit->MapSetEntityKeyVal( name, "_color",  str );
502                                         }
503                                 }
504                         }
505                 }
506                 CWnd *wnd = GetDlgItem( IDC_BUTTON_SAVE_PARTICLEENTITIES );
507                 if ( wnd ) {
508                         wnd->EnableWindow( TRUE );
509                 }
510         }
511
512 }
513
514 void CDialogParticleEditor::OnBnClickedButtonBrowsefadecolor() {
515         int r, g, b;
516         float ob;
517         idParticleStage *ps = GetCurStage();
518         if ( ps == NULL ) {
519                 return;
520         }
521         r = ps->fadeColor.x * 255.0f;
522         g = ps->fadeColor.y * 255.0f;
523         b = ps->fadeColor.z * 255.0f;
524         ob = 1.0f;
525         if ( DoNewColor( &r, &g, &b, &ob ) ) {
526                 fadeColor.Format( "%f %f %f %f", (float)r / 255.0f, (float)g / 255.0f, (float)b / 255.0f, 1.0f );
527                 DlgVarsToCurStage();
528                 CurStageToDlgVars();
529         }
530 }
531
532 void CDialogParticleEditor::OnBnClickedButtonUpdate() {
533         UpdateData( TRUE );
534         DlgVarsToCurStage();
535         CurStageToDlgVars();
536 }
537
538 void CDialogParticleEditor::SelectParticle( const char *name ) {
539         int index = comboParticle.FindString( 0, name );
540         if ( index >= 0 ) {
541                 comboParticle.SetCurSel( index );
542                 UpdateParticleData();
543         }
544 }
545
546 idDeclParticle *CDialogParticleEditor::GetCurParticle() {
547         int sel = comboParticle.GetCurSel();
548         if ( sel == CB_ERR ) {
549                 return NULL;
550         }
551         int index = comboParticle.GetItemData( sel );
552         return static_cast<idDeclParticle *>( const_cast<idDecl *>( declManager->DeclByIndex( DECL_PARTICLE, index ) ) );
553 }
554
555 void CDialogParticleEditor::UpdateParticleData() {
556         
557         listStages.ResetContent();
558         idDeclParticle *idp = GetCurParticle();
559         if ( idp == NULL ) {
560                 return;
561         }
562         for ( int i = 0; i < idp->stages.Num(); i++ ) {
563                 int index = listStages.AddString( va( "stage %i", i ) );
564                 if ( index >= 0 ) {
565                         listStages.SetItemData( index, i );
566                 }
567         }
568         listStages.SetCurSel( 0 );
569         OnLbnSelchangeListStages();
570         CWnd *wnd = GetDlgItem( IDC_STATIC_INFILE );
571         if ( wnd ) {
572                 wnd->SetWindowText( va( "Particle file: %s", idp->GetFileName() ) );
573         }
574
575         SetParticleView();      
576 }
577
578 void CDialogParticleEditor::OnCbnSelchangeComboParticles() {
579         UpdateParticleData();
580 }
581
582
583 void CDialogParticleEditor::OnCbnSelchangeComboPath() {
584         DlgVarsToCurStage();
585         CurStageToDlgVars();
586         UpdateControlInfo();
587 }
588
589 void CDialogParticleEditor::UpdateControlInfo() {
590         CWnd *wnd = GetDlgItem( IDC_EDIT_RINGOFFSET );
591         if ( wnd ) {
592                 wnd->EnableWindow( distribution == 2 );
593         }
594         wnd = GetDlgItem( IDC_STATIC_DIRPARM );
595         if ( wnd ) {
596                 wnd->SetWindowText( (direction == 0 ) ? "Angle" : "Upward Bias" );
597         }
598         wnd = GetDlgItem( IDC_EDIT_ORIENTATIONPARM1 );
599         if ( wnd ) {
600                 wnd->EnableWindow( orientation == 1 );
601         }
602         wnd = GetDlgItem( IDC_EDIT_ORIENTATIONPARM2 );
603         if ( wnd ) {
604                 wnd->EnableWindow( orientation == 1 );
605         }
606
607         idParticleStage *ps = GetCurStage();
608         if ( ps == NULL ) {
609                 return;
610         }
611         sliderBunching.SetValuePos( ps->spawnBunching );
612         sliderFadeIn.SetValuePos( ps->fadeInFraction );
613         sliderFadeOut.SetValuePos( ps->fadeOutFraction );
614         sliderFadeFraction.SetValuePos( ps->fadeIndexFraction );
615         sliderCount.SetValuePos( ps->totalParticles );
616         sliderTime.SetValuePos( ps->particleLife );
617         sliderGravity.SetValuePos( ps->gravity );
618         sliderSpeedFrom.SetValuePos( ps->speed.from );
619         sliderSpeedTo.SetValuePos( ps->speed.to );
620         sliderRotationFrom.SetValuePos( ps->rotationSpeed.from );
621         sliderRotationTo.SetValuePos( ps->rotationSpeed.to );
622         sliderSizeFrom.SetValuePos( ps->size.from );
623         sliderSizeTo.SetValuePos( ps->size.to );
624         sliderAspectFrom.SetValuePos( ps->aspect.from );
625         sliderAspectTo.SetValuePos( ps->aspect.to );
626 }
627
628 void CDialogParticleEditor::OnBnClickedRadioRect() {
629         distribution = 0;
630         DlgVarsToCurStage();
631         CurStageToDlgVars();
632         UpdateControlInfo();
633 }
634
635 void CDialogParticleEditor::OnBnClickedRadioSphere() {
636         distribution = 2;
637         DlgVarsToCurStage();
638         CurStageToDlgVars();
639         UpdateControlInfo();
640 }
641
642 void CDialogParticleEditor::OnBnClickedRadioCylinder() {
643         distribution = 1;
644         DlgVarsToCurStage();
645         CurStageToDlgVars();
646         UpdateControlInfo();
647 }
648
649 void CDialogParticleEditor::OnBnClickedRadioCone() {
650         direction = 0;
651         DlgVarsToCurStage();
652         CurStageToDlgVars();
653         UpdateControlInfo();
654 }
655
656 void CDialogParticleEditor::OnBnClickedRadioOutward() {
657         direction = 1;
658         DlgVarsToCurStage();
659         CurStageToDlgVars();
660         UpdateControlInfo();
661 }
662
663 void CDialogParticleEditor::OnBnClickedRadioView() {
664         orientation = 0;
665         DlgVarsToCurStage();
666         CurStageToDlgVars();
667         UpdateControlInfo();
668 }
669
670 void CDialogParticleEditor::OnBnClickedRadioAimed() {
671         orientation = 1;
672         DlgVarsToCurStage();
673         CurStageToDlgVars();
674         UpdateControlInfo();
675 }
676
677 void CDialogParticleEditor::OnBnClickedRadioX() {
678         orientation = 2;
679         DlgVarsToCurStage();
680         CurStageToDlgVars();
681         UpdateControlInfo();
682 }
683
684 void CDialogParticleEditor::OnBnClickedRadioY() {
685         orientation = 3;
686         DlgVarsToCurStage();
687         CurStageToDlgVars();
688         UpdateControlInfo();
689 }
690
691 void CDialogParticleEditor::OnBnClickedRadioZ() {
692         orientation = 4;
693         DlgVarsToCurStage();
694         CurStageToDlgVars();
695         UpdateControlInfo();
696 }
697
698 void CDialogParticleEditor::OnBnClickedDoom() {
699         ::SetFocus(win32.hWnd);
700 }
701
702
703 void CDialogParticleEditor::OnBnClickedTestModel() {
704         visualization = TESTMODEL;
705         SetParticleView();
706 }
707
708 void CDialogParticleEditor::OnBnClickedImpact() {
709         visualization = IMPACT;
710         SetParticleView();
711 }
712
713 void CDialogParticleEditor::OnBnClickedMuzzle(){ 
714         visualization = MUZZLE;
715         SetParticleView();
716 }
717
718 void CDialogParticleEditor::OnBnClickedFlight() {
719         visualization = FLIGHT;
720         SetParticleView();
721 }
722
723 void CDialogParticleEditor::OnBnClickedSelected() {
724         visualization = SELECTED;
725         SetParticleView();
726 }
727
728 void CDialogParticleEditor::SetParticleVisualization( int i ) { 
729         visualization = i; 
730         SetParticleView();
731 }
732
733 void CDialogParticleEditor::SetParticleView() {
734         idDeclParticle *idp = GetCurParticle();
735         if ( idp == NULL ) {
736                 return;
737         }
738         cmdSystem->BufferCommandText( CMD_EXEC_NOW, "testmodel" );
739         idStr str;
740         switch ( visualization ) {
741                 case TESTMODEL : 
742                         str = idp->GetName();
743                         str.SetFileExtension( ".prt" );
744                         cmdSystem->BufferCommandText( CMD_EXEC_NOW, va("testmodel %s\n", str.c_str() ) );
745                 break;
746                 case IMPACT :
747                         str = idp->GetName();
748                         str.SetFileExtension( ".prt" );
749                         cvarSystem->SetCVarInteger( "g_testParticle", TEST_PARTICLE_IMPACT );
750                         cvarSystem->SetCVarString( "g_testParticleName", str );
751                 break;
752                 case MUZZLE :
753                         str = idp->GetName();
754                         str.SetFileExtension( ".prt" );
755                         cvarSystem->SetCVarInteger( "g_testParticle", TEST_PARTICLE_MUZZLE );
756                         cvarSystem->SetCVarString( "g_testParticleName", str );
757                 break;
758                 case FLIGHT :
759                         str = idp->GetName();
760                         str.SetFileExtension( ".prt" );
761                         cvarSystem->SetCVarInteger( "g_testParticle", TEST_PARTICLE_FLIGHT );
762                         cvarSystem->SetCVarString( "g_testParticleName", str );
763                 break;
764                 case SELECTED :
765                         str = idp->GetName();
766                         str.SetFileExtension( ".prt" );
767                         cvarSystem->SetCVarInteger( "g_testParticle", TEST_PARTICLE_FLIGHT );
768                         SetSelectedModel( str );
769                 break;
770         }
771 }
772
773 void CDialogParticleEditor::SetSelectedModel( const char *val ) {
774         idList<idEntity*> list;
775         idMat3 axis;
776
777         list.SetNum( 128 );
778         int count = gameEdit->GetSelectedEntities( list.Ptr(), list.Num() );
779         list.SetNum( count );
780
781         if ( count ) {
782                 for ( int i = 0; i < count; i++ ) {
783                         const idDict *dict = gameEdit->EntityGetSpawnArgs( list[i] );
784                         if ( dict == NULL ) {
785                                 continue;
786                         }
787                         const char *name = dict->GetString( "name" );
788                         gameEdit->EntitySetModel( list[i], val );
789                         gameEdit->EntityUpdateVisuals( list[i] );
790                         gameEdit->EntityGetAxis( list[i], axis );
791                         vectorControl.SetidAxis( axis );
792                         gameEdit->MapSetEntityKeyVal( name, "model", val );
793                 }
794                 CWnd *wnd = GetDlgItem( IDC_BUTTON_SAVE_PARTICLEENTITIES );
795                 if ( wnd ) {
796                         wnd->EnableWindow( TRUE );
797                 }
798         }
799 }
800
801
802 void CDialogParticleEditor::OnBnClickedButtonHidestage() {
803         HideStage();
804 }
805
806 void CDialogParticleEditor::OnBnClickedButtonShowstage() {
807         ShowStage();
808 }
809
810
811 void CDialogParticleEditor::OnBnClickedWorldGravity() {
812         worldGravity = !worldGravity;
813         DlgVarsToCurStage();
814         CurStageToDlgVars();
815 }
816
817 void CDialogParticleEditor::OnBnClickedEntityColor() {
818         entityColor = !entityColor;
819         DlgVarsToCurStage();
820         CurStageToDlgVars();
821 }
822
823
824 void CDialogParticleEditor::AddStage() {
825         
826         idDeclParticle *idp = GetCurParticle();
827         if ( idp == NULL ) {
828                 return;
829         }
830
831         idParticleStage *stage = new idParticleStage;
832         
833         if ((GetAsyncKeyState(VK_CONTROL) & 0x8000)) {
834                 idParticleStage *source = GetCurStage();
835                 if ( source == NULL ) {
836                         delete stage;
837                         return;
838                 }
839                 *stage = *source;
840         } else {
841                 stage->Default();
842         }
843         int newIndex = idp->stages.Append( stage );
844         int index = listStages.AddString( va( "stage %i", newIndex ) );
845         listStages.SetCurSel( index );
846         listStages.SetItemData( index, newIndex );
847         ShowCurrentStage();
848         EnableStageControls();
849 }
850
851 void CDialogParticleEditor::RemoveStage() {
852         idDeclParticle *idp = GetCurParticle();
853         if ( idp == NULL ) {
854                 return;
855         }
856
857         if ( MessageBox( "Are you sure you want to remove this stage?", "Remove Stage", MB_YESNO | MB_ICONQUESTION ) != IDYES ) {
858                 return;
859         }
860
861         int index = listStages.GetCurSel();
862         if ( index >= 0 ) {
863                 int newIndex = listStages.GetItemData( index );
864                 if ( newIndex >= 0 && newIndex < idp->stages.Num() ) {
865                         idp->stages.RemoveIndex( newIndex );
866                         index += ( index >= 1 ) ? -1 : 1;
867                         newIndex = comboParticle.FindStringExact( -1, idp->GetName() );
868                         EnumParticles();
869                         if ( newIndex >= 0 ) {
870                                 comboParticle.SetCurSel( newIndex );
871                         }
872                         OnCbnSelchangeComboParticles();
873                         listStages.SetCurSel( index );
874                         ShowCurrentStage();
875                 }
876         }
877         EnableStageControls();
878 }
879
880 void CDialogParticleEditor::ShowStage() {
881         idParticleStage *ps = GetCurStage();
882         if ( ps == NULL ) {
883                 return;
884         }
885         ps->hidden = false;
886         int index = listStages.GetCurSel();
887         int newIndex = listStages.GetItemData( index );
888         listStages.DeleteString ( index );
889         listStages.InsertString( index, va("stage %i", index ) );
890         listStages.SetItemData( index, newIndex );
891         listStages.SetCurSel( index );
892         EnableStageControls();
893 }
894
895 void CDialogParticleEditor::HideStage() {
896         idParticleStage *ps = GetCurStage();
897         if ( ps == NULL ) {
898                 return;
899         }
900         ps->hidden = true;
901         int index = listStages.GetCurSel();
902         int newIndex = listStages.GetItemData( index );
903         listStages.DeleteString ( index );
904         listStages.InsertString( index, va("stage %i (H) ", index ) );
905         listStages.SetItemData( index, newIndex );
906         listStages.SetCurSel( index );
907         EnableStageControls();
908 }
909
910 idParticleStage *CDialogParticleEditor::GetCurStage() {
911         idDeclParticle *idp = GetCurParticle();
912         int sel = listStages.GetCurSel();
913         int index = listStages.GetItemData( sel );
914         if ( idp == NULL || sel == LB_ERR || index >= idp->stages.Num() ) {
915                 return NULL;
916         }
917         return idp->stages[index];
918 }
919
920 void CDialogParticleEditor::ClearDlgVars() {
921         matName = "";
922         animFrames = "";
923         animRate = "";
924         color = "";
925         fadeColor = "";
926         fadeIn = "";
927         fadeOut = "";
928         fadeFraction = "";
929         count = "";
930         time = "";
931         timeOffset = "";
932         deadTime = "";
933         gravity = "";
934         bunching = "";
935         offset = "";
936         xSize = "";
937         ySize = "";
938         zSize = "";
939         ringOffset = "";
940         directionParm = "";
941         direction = 1;
942         orientation = 1;
943         distribution = 1;
944         speedFrom = "";
945         speedTo = "";
946         rotationFrom = "";
947         rotationTo = "";
948         sizeFrom = "";
949         sizeTo = "";
950         aspectFrom = "";
951         aspectTo = "";
952         customPath = "";
953         customParms = "";
954         trails = "";
955         trailTime = "";
956         worldGravity = FALSE;
957         entityColor = FALSE;
958         randomDistribution = TRUE;
959         initialAngle = "";
960         customDesc = "";
961         UpdateData( FALSE );
962 }
963
964 void CDialogParticleEditor::CurStageToDlgVars() {
965
966         // go ahead and get the two system vars too
967         idDeclParticle *idp = GetCurParticle();
968         if ( idp == NULL ) {
969                 return;
970         }
971
972         depthHack = va( "%.3f", idp->depthHack );
973
974         idParticleStage *ps = GetCurStage();
975         if ( ps == NULL ) {
976                 return;
977         }
978         matName = ps->material->GetName();
979         animFrames = va( "%i", ps->animationFrames );
980         animRate = va( "%i", ps->animationRate );
981         color = ps->color.ToString();
982         fadeColor = ps->fadeColor.ToString();
983         fadeIn = va( "%.3f", ps->fadeInFraction );
984         fadeOut = va( "%.3f", ps->fadeOutFraction );
985         fadeFraction = va( "%.3f", ps->fadeIndexFraction );
986         count = va( "%i", ps->totalParticles );
987         time = va( "%.3f", ps->particleLife );
988         timeOffset = va( "%.3f", ps->timeOffset );
989         deadTime = va( "%.3f", ps->deadTime );
990         gravity = va( "%.3f", ps->gravity );
991         bunching = va( "%.3f", ps->spawnBunching );
992         offset = ps->offset.ToString( 0 );
993         xSize = va( "%.3f", ps->distributionParms[0] );
994         ySize = va( "%.3f", ps->distributionParms[1] );
995         zSize = va( "%.3f", ps->distributionParms[2] );
996         ringOffset = va( "%.3f", ps->distributionParms[3] );
997         directionParm = va( "%.3f", ps->directionParms[0] );
998         direction = ps->directionType;
999         orientation = ps->orientation;
1000         distribution = ps->distributionType;
1001         speedFrom = va( "%.3f", ps->speed.from );
1002         speedTo = va( "%.3f", ps->speed.to );
1003         rotationFrom = va( "%.3f", ps->rotationSpeed.from );
1004         rotationTo = va( "%.3f", ps->rotationSpeed.to );
1005         sizeFrom = va( "%.3f", ps->size.from );
1006         sizeTo = va( "%.3f", ps->size.to );
1007         aspectFrom = va( "%.3f", ps->aspect.from );
1008         aspectTo = va( "%.3f", ps->aspect.to );
1009         trails = va( "%f", ps->orientationParms[0] );
1010         trailTime = va( "%.3f", ps->orientationParms[1] );
1011         cycles = va( "%.3f", ps->cycles );
1012         customPath = ps->GetCustomPathName();
1013         customParms = "";
1014         customDesc = ps->GetCustomPathDesc();
1015         if ( ps->customPathType != PPATH_STANDARD ) {
1016                 for ( int i = 0; i < ps->NumCustomPathParms(); i++ ) {
1017                         customParms += va( "%.1f ", ps->customPathParms[i] );
1018                 }
1019         }
1020         worldGravity = ps->worldGravity;
1021         initialAngle = va( "%.3f", ps->initialAngle );
1022         boundsExpansion = va( "%.3f", ps->boundsExpansion );
1023         randomDistribution = ps->randomDistribution;
1024         entityColor = ps->entityColor;  
1025         UpdateData( FALSE );
1026 }
1027
1028 void CDialogParticleEditor::DlgVarsToCurStage() {
1029
1030         // go ahead and set the two system vars too
1031         idDeclParticle *idp = GetCurParticle();
1032         if ( idp == NULL ) {
1033                 return;
1034         }
1035         idp->depthHack = atof( depthHack );
1036
1037         idParticleStage *ps = GetCurStage();
1038         if ( ps == NULL ) {
1039                 return;
1040         }
1041         ps->material = declManager->FindMaterial( matName );
1042         ps->animationFrames = atoi( animFrames );
1043         ps->animationRate = atoi( animRate );
1044         sscanf( color, "%f %f %f %f", &ps->color.x, &ps->color.y, &ps->color.z, &ps->color.w );
1045         sscanf( fadeColor, "%f %f %f %f", &ps->fadeColor.x, &ps->fadeColor.y, &ps->fadeColor.z, &ps->fadeColor.w );
1046         ps->fadeInFraction = atof( fadeIn );
1047         ps->fadeOutFraction = atof( fadeOut );
1048         ps->fadeIndexFraction = atof( fadeFraction );
1049         ps->totalParticles = atoi( count );
1050         ps->particleLife = atof( time );
1051         ps->timeOffset = atof( timeOffset );
1052         ps->deadTime = atof( deadTime );
1053         ps->gravity = atof( gravity );
1054         ps->spawnBunching = atof( bunching );
1055         sscanf( offset, "%f %f %f", &ps->offset.x, &ps->offset.y, &ps->offset.z );
1056         ps->distributionParms[0] = atof( xSize );
1057         ps->distributionParms[1] = atof( ySize );
1058         ps->distributionParms[2] = atof( zSize );
1059         ps->distributionParms[3] = atof( ringOffset );
1060         ps->directionParms[0] = atof( directionParm );
1061         ps->directionType = static_cast<prtDirection_t>( direction );
1062         ps->orientation = static_cast<prtOrientation_t>( orientation );
1063         ps->distributionType = static_cast<prtDistribution_t>( distribution );
1064         ps->speed.from = atof( speedFrom );
1065         ps->speed.to = atof( speedTo );
1066         ps->rotationSpeed.from = atof( rotationFrom );
1067         ps->rotationSpeed.to = atof( rotationTo );
1068         ps->size.from = atof( sizeFrom );
1069         ps->size.to = atof( sizeTo );
1070         ps->aspect.from = atof( aspectFrom );
1071         ps->aspect.to = atof( aspectTo );
1072         ps->orientationParms[0] = atof( trails );
1073         ps->orientationParms[1] = atof( trailTime );
1074         ps->worldGravity = ( worldGravity == TRUE );
1075         ps->cycles = atof( cycles );
1076         ps->cycleMsec = ( ps->particleLife + ps->deadTime ) * 1000;
1077
1078         sscanf( customParms, "%f %f %f %f %f %f %f %f", &ps->customPathParms[0], &ps->customPathParms[1], &ps->customPathParms[2], 
1079                    &ps->customPathParms[3], &ps->customPathParms[4], &ps->customPathParms[5],
1080                    &ps->customPathParms[6], &ps->customPathParms[7] );
1081
1082         ps->SetCustomPathType( customPath );
1083
1084         ps->initialAngle = atof( initialAngle );
1085         ps->boundsExpansion = atof( boundsExpansion );
1086         ps->randomDistribution = ( randomDistribution != FALSE );
1087         ps->entityColor = ( entityColor == TRUE );
1088
1089 }
1090
1091 void CDialogParticleEditor::ShowCurrentStage() {
1092         ClearDlgVars();
1093         idParticleStage *ps = GetCurStage();
1094         if ( ps == NULL ) {
1095                 return;
1096         }
1097         CurStageToDlgVars();
1098         UpdateControlInfo();
1099 }
1100
1101 void CDialogParticleEditor::OnLbnSelchangeListStages() {
1102         ShowCurrentStage();
1103         EnableStageControls();
1104 }
1105
1106 void CDialogParticleEditor::OnBnClickedButtonNew() {
1107         DialogName dlg("New Particle");
1108         if (dlg.DoModal() == IDOK) {
1109                 CFileDialog dlgSave( TRUE, "prt", NULL, OFN_CREATEPROMPT, "Particle Files (*.prt)|*.prt||All Files (*.*)|*.*||", AfxGetMainWnd() );
1110                 if ( dlgSave.DoModal() == IDOK ) {
1111                         if ( declManager->FindType( DECL_PARTICLE, dlg.m_strName, false ) ) {
1112                                 MessageBox( "Particle already exists!", "Particle exists", MB_OK );
1113                                 return;
1114                         }
1115                         idStr fileName;
1116                         fileName = fileSystem->OSPathToRelativePath( dlgSave.m_ofn.lpstrFile );
1117                         idDecl *decl = declManager->CreateNewDecl( DECL_PARTICLE, dlg.m_strName, fileName );
1118                         if ( decl ) {
1119                                 if ( MessageBox( "Copy current particle?", "Copy current", MB_YESNO | MB_ICONQUESTION ) == IDYES ) {
1120                                         MessageBox( "Copy current particle not implemented yet.. Stay tuned" );
1121                                 }
1122                                 EnumParticles();
1123                                 int index = comboParticle.FindStringExact( -1, dlg.m_strName );
1124                                 if ( index >= 0 ) {
1125                                         comboParticle.SetCurSel( index );
1126                                 }
1127                                 OnBnClickedButtonSave();
1128                                 OnCbnSelchangeComboParticles();
1129                         }
1130                 }
1131         }
1132 }
1133
1134 void CDialogParticleEditor::OnBnClickedButtonSave() {
1135         idDeclParticle *idp = GetCurParticle();
1136         if ( idp == NULL ) {
1137                 return;
1138         }
1139
1140         if ( strstr( idp->GetFileName(), "implicit" ) ) {
1141                 // defaulted, need to choose a file 
1142                 CFileDialog dlgSave( FALSE, "prt", NULL, OFN_OVERWRITEPROMPT, "Particle Files (*.prt)|*.prt||All Files (*.*)|*.*||", AfxGetMainWnd() );
1143                 if ( dlgSave.DoModal() == IDOK ) {
1144                         idStr fileName;
1145                         fileName = fileSystem->OSPathToRelativePath( dlgSave.m_ofn.lpstrFile );
1146                         idp->Save( fileName );
1147                         EnumParticles();
1148                 }
1149         } else {
1150                 idp->Save();
1151         }
1152
1153 }
1154
1155 void CDialogParticleEditor::EnumParticles() {
1156         CWaitCursor cursor;
1157         comboParticle.ResetContent();
1158         for ( int i = 0; i < declManager->GetNumDecls( DECL_PARTICLE ); i++ ) {
1159                 const idDecl *idp = declManager->DeclByIndex( DECL_PARTICLE, i );
1160                 int index = comboParticle.AddString( idp->GetName() );
1161                 if ( index >= 0 ) {
1162                         comboParticle.SetItemData( index, i );
1163                 }
1164         }
1165         comboParticle.SetCurSel( 0 );
1166         OnCbnSelchangeComboParticles();
1167 }
1168
1169 void CDialogParticleEditor::OnDestroy() {
1170         com_editors &= ~EDITOR_PARTICLE;
1171         return CDialog::OnDestroy();
1172 }
1173
1174 void VectorCallBack( idQuat rotation ) {
1175         if ( g_ParticleDialog && g_ParticleDialog->GetSafeHwnd() ) {
1176                 g_ParticleDialog->SetVectorControlUpdate( rotation );
1177         }
1178 }
1179
1180 void CDialogParticleEditor::SetVectorControlUpdate( idQuat rotation ) {
1181         if ( particleMode ) {
1182                 idList<idEntity*> list;
1183
1184                 list.SetNum( 128 );
1185                 int count = gameEdit->GetSelectedEntities( list.Ptr(), list.Num() );
1186                 list.SetNum( count );
1187
1188                 if ( count ) {
1189                         for ( int i = 0; i < count; i++ ) {
1190                                 const idDict *dict = gameEdit->EntityGetSpawnArgs( list[i] );
1191                                 if ( dict == NULL ) {
1192                                         continue;
1193                                 }
1194                                 const char *name = dict->GetString( "name" );
1195                                 gameEdit->EntitySetAxis( list[i], rotation.ToMat3() );
1196                                 gameEdit->EntityUpdateVisuals( list[i] );
1197                                 gameEdit->MapSetEntityKeyVal( name, "rotation", rotation.ToMat3().ToString() );
1198                         }
1199                         CWnd *wnd = GetDlgItem( IDC_BUTTON_SAVE_PARTICLEENTITIES );
1200                         if ( wnd ) {
1201                                 wnd->EnableWindow( TRUE );
1202                         }
1203                 }
1204         }
1205 }
1206
1207 BOOL CDialogParticleEditor::OnInitDialog() {
1208         
1209         com_editors |= EDITOR_PARTICLE;
1210
1211         particleMode = ( cvarSystem->GetCVarInteger( "g_editEntityMode" ) == 4 );
1212         mapModified = false;
1213
1214         CDialog::OnInitDialog();
1215         
1216         sliderBunching.SetRange( 0, 20 );
1217         sliderBunching.SetValueRange( 0.0f, 1.0f );
1218         sliderFadeIn.SetRange( 0, 20 );
1219         sliderFadeIn.SetValueRange( 0.0f, 1.0f );
1220         sliderFadeOut.SetRange( 0, 20 );
1221         sliderFadeOut.SetValueRange( 0.0f, 1.0f );
1222         sliderCount.SetRange( 0, 1024 );
1223         sliderCount.SetValueRange( 0, 4096 );
1224         sliderTime.SetRange( 0, 200 );
1225         sliderTime.SetValueRange( 0.0f, 10.0f );
1226         sliderGravity.SetRange( 0, 600 );
1227         sliderGravity.SetValueRange( -300.0f, 300.0f );
1228         sliderSpeedFrom.SetRange( 0, 600 );
1229         sliderSpeedFrom.SetValueRange( -300.0f, 300.0f );
1230         sliderSpeedTo.SetRange( 0, 600 );
1231         sliderSpeedTo.SetValueRange( -300.0f, 300.0f );
1232         sliderRotationFrom.SetRange( 0, 100 );
1233         sliderRotationFrom.SetValueRange( 0.0f, 100.0f );
1234         sliderRotationTo.SetRange( 0, 100 );
1235         sliderRotationTo.SetValueRange( 0.0f, 100.0f );
1236         sliderSizeFrom.SetRange( 0, 256 );
1237         sliderSizeFrom.SetValueRange( 0.0f, 128.0f );
1238         sliderSizeTo.SetRange( 0, 256 );
1239         sliderSizeTo.SetValueRange( 0.0f, 128.0f );
1240         sliderAspectFrom.SetRange( 0, 256 );
1241         sliderAspectFrom.SetValueRange( 0.0f, 128.0f );
1242         sliderAspectTo.SetRange( 0, 256 );
1243         sliderAspectTo.SetValueRange( 0.0f, 128.0f );
1244         sliderFadeFraction.SetRange( 0, 20 );
1245         sliderFadeFraction.SetValueRange( 0.0f, 1.0f );
1246         
1247         EnumParticles();
1248         SetParticleView();
1249
1250         toolTipCtrl.Create( this );
1251         toolTipCtrl.Activate( TRUE );
1252
1253         CWnd* wnd = GetWindow( GW_CHILD );
1254         CString str;
1255         while ( wnd ) {
1256                 if ( str.LoadString( wnd->GetDlgCtrlID() ) ) {
1257                         toolTipCtrl.AddTool( wnd, str );
1258                 }
1259                 wnd = wnd->GetWindow( GW_HWNDNEXT );
1260         }
1261         
1262         wnd = GetDlgItem( IDC_BUTTON_SAVE_PARTICLEENTITIES );
1263         if ( wnd ) {
1264                 wnd->EnableWindow( FALSE );
1265         }
1266         EnableEditControls();
1267
1268         vectorControl.SetVectorChangingCallback( VectorCallBack );
1269
1270         return TRUE;  // return TRUE unless you set the focus to a control
1271         // EXCEPTION: OCX Property Pages should return FALSE
1272 }
1273
1274 void CDialogParticleEditor::OnHScroll( UINT nSBCode, UINT nPos, CScrollBar* pScrollBar ) {
1275         CDialog::OnHScroll( nSBCode, nPos, pScrollBar );
1276         CSliderCtrl *ctrl = dynamic_cast< CSliderCtrl* >( pScrollBar );
1277         if ( !ctrl ) {
1278                 return;
1279         }
1280         if ( ctrl == &sliderBunching ) {
1281                 // handle bunching
1282                 bunching = va( "%.3f", sliderBunching.GetValue() );
1283                 DlgVarsToCurStage();
1284                 CurStageToDlgVars();
1285         } else if ( ctrl == &sliderFadeIn ) {
1286                 fadeIn = va( "%.3f", sliderFadeIn.GetValue() );
1287                 DlgVarsToCurStage();
1288                 CurStageToDlgVars();
1289         } else if ( ctrl == &sliderFadeOut ) {
1290                 fadeOut = va( "%.3f", sliderFadeOut.GetValue() );
1291                 DlgVarsToCurStage();
1292                 CurStageToDlgVars();
1293         } else if ( ctrl == &sliderFadeFraction ) {
1294                 fadeFraction = va( "%.3f", sliderFadeFraction.GetValue() );
1295                 DlgVarsToCurStage();
1296                 CurStageToDlgVars();
1297         } else if ( ctrl == &sliderCount ) {
1298                 count = va( "%i", (int)sliderCount.GetValue() );
1299                 DlgVarsToCurStage();
1300                 CurStageToDlgVars();
1301         } else if ( ctrl == &sliderTime ) {
1302                 time = va( "%.3f", sliderTime.GetValue() );
1303                 DlgVarsToCurStage();
1304                 CurStageToDlgVars();
1305         } else if ( ctrl == &sliderGravity ) {
1306                 gravity = va( "%.3f", sliderGravity.GetValue() );
1307                 DlgVarsToCurStage();
1308                 CurStageToDlgVars();
1309         } else if ( ctrl == &sliderSpeedFrom ) {
1310                 speedFrom = va( "%.3f", sliderSpeedFrom.GetValue() );
1311                 DlgVarsToCurStage();
1312                 CurStageToDlgVars();
1313         } else if ( ctrl == &sliderSpeedTo ) {
1314                 speedTo = va( "%.3f", sliderSpeedTo.GetValue() );
1315                 DlgVarsToCurStage();
1316                 CurStageToDlgVars();
1317         } else if ( ctrl == &sliderRotationFrom ) {
1318                 rotationFrom = va( "%.3f", sliderRotationFrom.GetValue() );
1319                 DlgVarsToCurStage();
1320                 CurStageToDlgVars();
1321         } else if ( ctrl == &sliderRotationTo ) {
1322                 rotationTo = va( "%.3f", sliderRotationTo.GetValue() );
1323                 DlgVarsToCurStage();
1324                 CurStageToDlgVars();
1325         } else if ( ctrl == &sliderSizeFrom ) {
1326                 sizeFrom = va( "%.3f", sliderSizeFrom.GetValue() );
1327                 DlgVarsToCurStage();
1328                 CurStageToDlgVars();
1329         } else if ( ctrl == &sliderSizeTo ) {
1330                 sizeTo = va( "%.3f", sliderSizeTo.GetValue() );
1331                 DlgVarsToCurStage();
1332                 CurStageToDlgVars();
1333         } else if ( ctrl == &sliderAspectFrom ) {
1334                 aspectFrom = va( "%.3f", sliderAspectFrom.GetValue() );
1335                 DlgVarsToCurStage();
1336                 CurStageToDlgVars();
1337         } else if ( ctrl == &sliderAspectTo ) {
1338                 aspectTo = va( "%.3f", sliderAspectTo.GetValue() );
1339                 DlgVarsToCurStage();
1340                 CurStageToDlgVars();
1341         }
1342 }
1343
1344
1345 BOOL CDialogParticleEditor::PreTranslateMessage(MSG *pMsg) {
1346         if ( pMsg->message >= WM_MOUSEFIRST && pMsg->message <= WM_MOUSELAST ) {
1347                 toolTipCtrl.RelayEvent( pMsg );
1348         }
1349         return CDialog::PreTranslateMessage(pMsg);
1350 }
1351
1352 void CDialogParticleEditor::EnableStageControls() {
1353         idParticleStage *stage = GetCurStage();
1354         bool b = ( stage && stage->hidden ) ? false : true;
1355         for ( int i = 0; i < StageIDCount; i++ ) {
1356                 CWnd *wnd = GetDlgItem( StageEnableID[ i ] );
1357                 if ( wnd ) {
1358                         wnd->EnableWindow( b );
1359                 }
1360         }
1361 }
1362
1363 void CDialogParticleEditor::EnableEditControls() {
1364         for ( int i = 0; i < EditIDCount; i++ ) {
1365                 CWnd *wnd = GetDlgItem( EditEnableID[ i ] );
1366                 if ( wnd ) {
1367                         wnd->EnableWindow( particleMode );
1368                 }
1369         }
1370 }
1371
1372 void CDialogParticleEditor::UpdateSelectedOrigin( float x, float y, float z ) {
1373         idList<idEntity*> list;
1374         idVec3 origin;
1375         idVec3 vec(x, y, z);
1376
1377         list.SetNum( 128 );
1378         int count = gameEdit->GetSelectedEntities( list.Ptr(), list.Num() );
1379         list.SetNum( count );
1380
1381         if ( count ) {
1382                 for ( int i = 0; i < count; i++ ) {
1383                         const idDict *dict = gameEdit->EntityGetSpawnArgs( list[i] );
1384                         if ( dict == NULL ) {
1385                                 continue;
1386                         }
1387                         const char *name = dict->GetString( "name" );
1388                         gameEdit->EntityTranslate( list[i], vec );
1389                         gameEdit->EntityUpdateVisuals( list[i] );
1390                         gameEdit->MapEntityTranslate( name, vec );
1391                 }
1392                 CWnd *wnd = GetDlgItem( IDC_BUTTON_SAVE_PARTICLEENTITIES );
1393                 if ( wnd ) {
1394                         wnd->EnableWindow( TRUE );
1395                 }
1396         }
1397 }
1398
1399 void CDialogParticleEditor::OnBtnYup() 
1400 {
1401         UpdateSelectedOrigin(0, 8, 0);
1402 }
1403
1404 void CDialogParticleEditor::OnBtnYdn() 
1405 {
1406         UpdateSelectedOrigin(0, -8, 0);
1407 }
1408
1409 void CDialogParticleEditor::OnBtnXdn() 
1410 {
1411         UpdateSelectedOrigin(-8, 0, 0);
1412 }
1413
1414 void CDialogParticleEditor::OnBtnXup() 
1415 {
1416         UpdateSelectedOrigin(8, 0, 0);
1417 }
1418
1419 void CDialogParticleEditor::OnBtnZup() 
1420 {
1421         UpdateSelectedOrigin(0, 0, 8);
1422 }
1423
1424 void CDialogParticleEditor::OnBtnZdn() 
1425 {
1426         UpdateSelectedOrigin(0, 0, -8);
1427 }
1428
1429 void CDialogParticleEditor::OnBtnDrop() 
1430 {
1431         idStr           classname;
1432         idStr           key;
1433         idStr           value;
1434         idVec3          org;
1435         idDict          args;
1436         idAngles        viewAngles;
1437
1438         if ( !gameEdit->PlayerIsValid() ) {
1439                 return;
1440         }
1441         
1442         gameEdit->PlayerGetViewAngles( viewAngles );
1443         gameEdit->PlayerGetEyePosition( org );
1444
1445         org += idAngles( 0, viewAngles.yaw, 0 ).ToForward() * 80 + idVec3( 0, 0, 1 );
1446         args.Set("origin", org.ToString());
1447         args.Set("classname", "func_emitter");
1448         args.Set("angle", va( "%f", viewAngles.yaw + 180 ));
1449
1450         idDeclParticle *idp = GetCurParticle();
1451         if ( idp == NULL ) {
1452                 return;
1453         }
1454         idStr str = idp->GetName();
1455         str.SetFileExtension( ".prt" );
1456
1457         args.Set("model", str);
1458
1459         idStr name = gameEdit->GetUniqueEntityName( "func_emitter" );
1460         bool nameValid = false;
1461         while (!nameValid) {
1462                 DialogName dlg("Name Particle", this);
1463                 dlg.m_strName = name;
1464                 if (dlg.DoModal() == IDOK) {
1465                         idEntity *gameEnt = gameEdit->FindEntity( dlg.m_strName );
1466                         if (gameEnt) {
1467                                 if (MessageBox("Please choose another name", "Duplicate Entity Name!", MB_OKCANCEL) == IDCANCEL) {
1468                                         return;
1469                                 }
1470                         } else {
1471                                 nameValid = true;
1472                                 name = dlg.m_strName;
1473                         }
1474                 }
1475         }
1476
1477         args.Set("name", name.c_str());
1478
1479         idEntity *ent = NULL;
1480         gameEdit->SpawnEntityDef( args, &ent );
1481         if (ent) {
1482                 gameEdit->EntityUpdateChangeableSpawnArgs( ent, NULL );
1483                 gameEdit->ClearEntitySelection();
1484                 gameEdit->AddSelectedEntity( ent );
1485         }
1486
1487         gameEdit->MapAddEntity( &args );
1488 }
1489
1490 void CDialogParticleEditor::OnOK()
1491 {
1492         // never return on OK as windows will map this at times when you don't want
1493         // ENTER closing the dialog
1494         // CDialog::OnOK();
1495 }