Param.cpp 78 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. /******************************************************************************/
  4. ObjClassEditor ObjClassEdit;
  5. /******************************************************************************/
  6. /******************************************************************************/
  7. const flt ParamEditor::ParamWindow::elm_h=0.042f;
  8. NameDesc ParamEditor::path_mode[]=
  9. {
  10. {MLTC(u"Create" , PL, u"Utwórz" ), u"Object will be included in Path Mesh generation."},
  11. {MLTC(u"Ignore" , PL, u"Ignoruj" ), u"Object will not be included in Path Mesh generation."},
  12. {MLTC(u"Fully Block", PL, u"Całkowicie Zablokuj"), u"Object will fully block any passage at its location."},
  13. };
  14. NameDesc ParamEditor::etype_mode[]=
  15. {
  16. {u"Mesh" , u"Object will be drawn using its mesh."},
  17. {u"Point Light", u"Object will be drawn as point light."},
  18. {u"Cone Light" , u"Object will be drawn as cone light."},
  19. {u"Particles" , u"Object will be drawn as particles."},
  20. };
  21. /******************************************************************************/
  22. ParamEditor::RenameWindow::RenameWindow(ParamEditor &pe) : pe(pe) {}
  23. void ParamEditor::RenameWindow::create()
  24. {
  25. Gui+=::EE::Window::create(Rect_C(0, 0, 1, 0.14f), "Rename").hide(); button[2].show();
  26. T +=dest .create(Rect (0, -clientHeight(), clientWidth(), 0).extend(-0.01f));
  27. }
  28. void ParamEditor::RenameWindow::rename(C Str &src, PARAM_TYPE type, C UID &id)
  29. {
  30. data.name=src ;
  31. data.type=type;
  32. T .id =id ;
  33. setTitle(S+"Rename \""+src+"\" parameter");
  34. activate();
  35. dest.set(src).selectAll().activate();
  36. }
  37. bool ParamEditor::RenameWindow::Rename(ParamEditor &pe, EditObject &obj, C RenameWindow &rename)
  38. {
  39. bool changed=false; REPA(obj)
  40. {
  41. EditParam &param=obj[i]; if(ParamCompatible(param, rename.data) && !param.removed){param.setName(rename.dest()); changed=true;}
  42. }
  43. return changed;
  44. }
  45. void ParamEditor::RenameWindow::update(C GuiPC &gpc)
  46. {
  47. ::EE::ClosableWindow::update(gpc);
  48. if(Gui.window()==this && Kb.k(KB_ENTER))
  49. {
  50. if(pe.world)
  51. {
  52. REPA(Selection)
  53. {
  54. Obj &obj=Selection[i];
  55. REPA(obj.params){EditParam &param=obj.params[i]; if(ParamCompatible(param, data) && !param.removed){obj.setUndo(); param.setName(dest()); obj.setChanged();}}
  56. }
  57. }else
  58. {
  59. pe.setUndo(this);
  60. REPA(*pe.p){EditParam &param=(*pe.p)[i]; if(param.id==id && !param.removed)param.setName(dest());}
  61. pe.setChanged(false);
  62. }
  63. pe.multiFunc(Rename, T);
  64. pe.toGui();
  65. hide();
  66. }
  67. }
  68. ::ParamEditor::ParamWindow::EditParamEx& ParamEditor::ParamWindow::EditParamEx::create(C EditParam &src, MODE mode)
  69. {
  70. SCAST(EditParam, T)=src;
  71. switch(mode)
  72. {
  73. case CUR : cur =true; break;
  74. case BASE : base =true; break;
  75. case BASE_OVR: base_ovr=true; break;
  76. }
  77. return T;
  78. }
  79. void ParamEditor::ParamWindow::Param::GuiColor::update(C GuiPC &gpc)
  80. {
  81. REPA(MT)if(MT.guiObj(i)==this && MT.bp(i))
  82. {
  83. Param &param=*(Param*)user;
  84. if(ParamEditor *pe=param.pe)
  85. {
  86. pe->newParam(param, false); // don't refresh gui because it may delete 'param', and what's worse we're inside param's virtual function !!
  87. param.ovr.set(true, QUIET); // adjust override checkbox because we're not refreshing gui
  88. param.cur=true;
  89. param.setSkin();
  90. param.cp.activate();
  91. }
  92. break;
  93. }
  94. }
  95. void ParamEditor::ParamWindow::Param::GuiColor::draw(C GuiPC &gpc)
  96. {
  97. if(gpc.visible && visible())
  98. {
  99. D.clip(gpc.clip);
  100. Param &param=*(Param*)user;
  101. Rect rect=T.rect()+gpc.offset;
  102. Color c=param.cp(); c.a=alpha;
  103. rect.draw(c, true);
  104. rect.draw(Gui.borderColor(), false);
  105. }
  106. }
  107. void ParamEditor::ParamWindow::Param::ChangedOvr(Param &param) {if(param.ovr())param.pe->newParam(param);else param.pe->removeParam(param);}
  108. void ParamEditor::ParamWindow::Param::RemoveParam(Param &param) {param.pe->removeParam(param);}
  109. void ParamEditor::ParamWindow::Param::ClearValue(Param &param) {param.pe->clearValue (param);}
  110. void ParamEditor::ParamWindow::Param::ChangedType(Param &param) {param.pe->setType (param);}
  111. void ParamEditor::ParamWindow::Param::ChangedName(Param &param) {param.pe->setName (param);}
  112. void ParamEditor::ParamWindow::Param::ChangedCheck(Param &param) {param.pe->setBool (param);}
  113. void ParamEditor::ParamWindow::Param::ChangedCombo(Param &param) {param.pe->setEnum (param);}
  114. void ParamEditor::ParamWindow::Param::ChangedColor(Param &param) {param.pe->setColor (param);}
  115. void ParamEditor::ParamWindow::Param::ChangedText(Param &param) {param.pe->setText (param);}
  116. void ParamEditor::ParamWindow::Param::MouseEdit(Param &param)
  117. {
  118. // get total delta movement (and number of start touches)
  119. Vec2 d=0; int start=0; if(Gui.ms()==&param.val_adjust && Ms.b(0)){d+=Ms.d(); if(Ms.bp(0))start++; Ms.freeze();} REPA(Touches)if(Touches[i].guiObj()==&param.val_adjust && Touches[i].on()){d+=Touches[i].ad(); if(Touches[i].pd())start++;}
  120. if(start==1)param.mouse_edit_value=param.src.asVec2(); // on start set initial value
  121. if(d.any())
  122. {
  123. d*=param.mouse_edit_speed;
  124. if((Kb.ctrlCmd() && Kb.shift()) || (Gui.ms()==&param.val_adjust && Ms.b(1)))d.set(d.x+d.y );else
  125. if( Kb.shift() && param.src.type==PARAM_VEC2 )d.set(0, d.x+d.y);else
  126. if( Kb.ctrlCmd() || param.src.type!=PARAM_VEC2 )d.set(d.x+d.y, 0);
  127. switch(param.mouse_edit_mode)
  128. {
  129. case PROP_MOUSE_EDIT_SCALAR: {flt min=(/*(prop.md.type==DATA_REAL && prop.md.size==SIZE(Dbl)) ? 0.000000001 : */0.001f);
  130. if(Equal(param.mouse_edit_value.x, 0, min))param.mouse_edit_value.x=min;
  131. if(Equal(param.mouse_edit_value.y, 0, min))param.mouse_edit_value.y=min;
  132. param.mouse_edit_value*=Vec2(ScaleFactor(d.x), ScaleFactor(d.y));} break;
  133. case PROP_MOUSE_EDIT_LINEAR: {param.mouse_edit_value+= d;} break;
  134. }
  135. param.mouse_edit_value.x=param.clamp(param.mouse_edit_value.x);
  136. param.mouse_edit_value.y=param.clamp(param.mouse_edit_value.y);
  137. param.set (param.mouse_edit_value );
  138. }
  139. }
  140. bool ParamEditor::ParamWindow::Param::forceEnum()C {return gui && Is(gui->enum_name);}
  141. int ParamEditor::ParamWindow::Param::enumValue()C {return (gui && InRange(val_combobox(), gui->name_vals)) ? gui->name_vals[val_combobox()].val : -1;}
  142. bool ParamEditor::ParamWindow::Param::contains(C GuiObj *obj)C
  143. {
  144. return obj==&ovr || obj==&remove || obj==&clear || obj==&type || obj==&name || obj==&val_checkbox || obj==&val_textline || obj==&val_combobox || obj==&val_color || obj==&val_adjust;
  145. }
  146. void ParamEditor::ParamWindow::Param::setSkin()
  147. {
  148. bool exists=(cur || base);
  149. GuiSkinPtr skin=(exists ? null : &HalfTransparentSkin),
  150. val_skin=(valid_value ? skin : &RedSkin);
  151. if(!valid_type)type.skin(&RedSkin, false);else type.skin(skin, false); // if invalid then highlight as removed
  152. clear.skin=skin;
  153. if(src.hasID(Proj.list.lit_elm_id))skin=val_skin=&LitSkin;
  154. name.skin(skin);
  155. val_checkbox.skin =val_skin;
  156. val_textline.skin (val_skin);
  157. val_combobox.skin (val_skin, false); // don't change skin of menu because we only set red color here
  158. val_adjust .skin =val_skin;
  159. val_color .alpha=(exists ? 255 : 128);
  160. }
  161. void ParamEditor::ParamWindow::Param::toGui()
  162. {
  163. bool base=(T.base || T.base_ovr);
  164. valid_type=valid_value=true;
  165. ovr .set(multi_cur ? false : cur, QUIET).visible( base); if(multi_cur)ovr.setMulti(); // force false for multi so next click will toggle to true
  166. remove. visible(!base);
  167. type .set(src.type==PARAM_ID_ARRAY ? PARAM_ID : src.type, QUIET).disabled(base ).show(); // for simplicity, display PARAM_ID_ARRAY as PARAM_ID
  168. name .set( src.name, QUIET).disabled(base || multi_obj).show();
  169. bool force_enum=forceEnum();
  170. if(force_enum)
  171. {
  172. type.setText(gui->enum_name, true, QUIET);
  173. ListColumn lc[]=
  174. {
  175. ListColumn(MEMBER(GuiEditParam::NameVal, name), LCW_DATA, "name"),
  176. };
  177. val_combobox.setColumns(lc, Elms(lc)).setData(ConstCast(gui->name_vals));
  178. if(multi_val)val_combobox.setText(MultipleName, true, QUIET);else{int sel=-1, value=src.asInt(); REPA(gui->name_vals)if(gui->name_vals[i].val==value){sel=i; break;} val_combobox.set(sel, QUIET);}
  179. }else
  180. switch(src.type)
  181. {
  182. default : val_textline.clear( QUIET); break;
  183. case PARAM_BOOL : val_checkbox.set ( src.value.b , QUIET); if(multi_val)val_checkbox.setMulti(); break;
  184. case PARAM_INT : val_textline.set (multi_val ? MultipleName : src.asText(), QUIET); break;
  185. case PARAM_FLT : val_textline.set (multi_val ? MultipleName : src.asText(), QUIET); break;
  186. case PARAM_VEC2 : val_textline.set (multi_val ? MultipleName : src.asText(), QUIET); break;
  187. case PARAM_VEC : val_textline.set (multi_val ? MultipleName : src.asText(), QUIET); break;
  188. case PARAM_VEC4 : val_textline.set (multi_val ? MultipleName : src.asText(), QUIET); break;
  189. case PARAM_STR : val_textline.set (multi_val ? MultipleName : src.asText(), QUIET); break;
  190. case PARAM_COLOR: if(!cp.is())cp.create("Parameter Color").func(ChangedColor, T).hide(); cp.set(multi_val ? Vec4(1) : src.value.c.asVec4(), QUIET); break;
  191. case PARAM_ID:
  192. {
  193. Str text=(multi_val ? MultipleName : Proj.idToText(src.value.id, &valid_value));
  194. val_textline.set(text, QUIET);
  195. }break;
  196. case PARAM_ID_ARRAY:
  197. {
  198. Str text; if(multi_val)text=MultipleName;else FREP(src.IDs())
  199. {
  200. if(i)text+='|';
  201. bool valid; text+=Proj.idToText(src.asID(i), &valid); valid_value&=valid;
  202. }
  203. val_textline.set(text, QUIET);
  204. }break;
  205. case PARAM_ENUM:
  206. {
  207. if(src.enum_type)
  208. {
  209. type.setText(src.enum_type->name, true, QUIET);
  210. ListColumn lc[]=
  211. {
  212. ListColumn(MEMBER(Enum::Elm, name), LCW_DATA, "name"),
  213. };
  214. val_combobox.setColumns(lc, Elms(lc)).setData(ConstCast(src.enum_type->elms()));
  215. valid_type=!Proj.invalidRef(Enums.id(src.enum_type));
  216. }else
  217. {
  218. val_combobox.clear();
  219. }
  220. val_combobox.setText(multi_val ? MultipleName : src.value.s, true, QUIET);
  221. }break;
  222. }
  223. setSkin();
  224. val_checkbox.visible(src.type==PARAM_BOOL );
  225. val_textline.visible(src.type!=PARAM_BOOL && src.type!=PARAM_ENUM && src.type!=PARAM_COLOR && !force_enum);
  226. val_combobox.visible(src.type==PARAM_ENUM || force_enum);
  227. val_color .visible(src.type==PARAM_COLOR);
  228. val_adjust .visible((src.type==PARAM_INT || src.type==PARAM_FLT || src.type==PARAM_VEC2) && !force_enum);
  229. Rect r=val_textline.rect(); r.max.x=(val_adjust.visible() ? val_adjust.rect().min.x : clear.rect().min.x); val_textline.rect(r); // adjust 'val_textline' rect because it depends on 'val_adjust' visibility
  230. mouse_edit_speed=(custom_mouse_edit_speed ? custom_mouse_edit_speed : (src.type==PARAM_INT) ? 40 : 1);
  231. if(!gui || !gui->desc().is()) // if there's no custom description, then adjust description depending on if it's an ID
  232. {
  233. Str desc;
  234. if(ParamTypeID(src.type))
  235. {
  236. desc="Parameter Value\nDrag and drop Project Elements here or manually type an ID \"UID(..)\"\nYou can drag and drop multiple elements at the same time.\nHold Ctrl during drag and drop to add selected elements to existing value.\nCtrl+LeftClick to open the element.";
  237. if(!multi_val)
  238. {
  239. bool count=(src.IDs()>1);
  240. FREP(src.IDs())
  241. {
  242. desc.line(); if(count)desc+=S+i+" - "; desc+=Proj.idToText(src.asID(i));
  243. }
  244. }
  245. }else desc=MLTC(u"Parameter Value", PL, u"Wartość Parametru", DE, u"Parameter Wert", RU, u"Значение параметра", PO, u"Valor do Parâmtero");
  246. val_textline.desc(desc);
  247. }
  248. }
  249. ::ParamEditor::ParamWindow::Param& ParamEditor::ParamWindow::Param::moveToTop()
  250. {
  251. ovr .moveToTop();
  252. remove .moveToTop();
  253. type .moveToTop();
  254. name .moveToTop();
  255. val_textline.moveToTop();
  256. val_checkbox.moveToTop();
  257. val_combobox.moveToTop();
  258. val_color .moveToTop();
  259. val_adjust .moveToTop();
  260. clear .moveToTop();
  261. return T;
  262. }
  263. ::ParamEditor::ParamWindow::Param& ParamEditor::ParamWindow::Param::setRect(int i, flt list_w)
  264. {
  265. flt h=elm_h,
  266. y=-h*i,
  267. w=list_w-h-0.24f-h,
  268. name_val_frac=0.40f,
  269. w_name =w*name_val_frac,
  270. w_value=w-w_name;
  271. ovr .rect(Rect_LU( 0, y, h, h));
  272. remove .rect(Rect_LU( 0, y, h, h));
  273. type .rect(Rect_LU(remove .rect().max.x , y, 0.24f , h));
  274. name .rect(Rect_LU(type .rect().max.x , y, w_name , h));
  275. val_checkbox.rect(Rect_U (name .rect().max.x+w_value/2, y, h, h));
  276. val_combobox.rect(Rect_LU(name .rect().max.x , y, w_value, h));
  277. val_color .rect(Rect_U (name .rect().max.x+w_value/2, y, h, h));
  278. clear .rect(Rect_RU(list_w , y, h, h));
  279. val_adjust .rect(Rect_RU(clear .rect().min.x , y, h, h));
  280. val_textline.rect(Rect (name .rect().max.x , y-h, val_adjust.visible() ? val_adjust.rect().min.x : clear.rect().min.x, y));
  281. return T;
  282. }
  283. void ParamEditor::ParamWindow::Param::desc(C Str &desc)
  284. {
  285. type .desc(desc);
  286. name .desc(desc);
  287. val_checkbox.desc(desc);
  288. val_textline.desc(desc);
  289. val_combobox.desc(desc);
  290. val_color .desc(desc);
  291. }
  292. dbl ParamEditor::ParamWindow::Param::clamp(dbl value)
  293. {
  294. if(min_use)MAX(value, min_value);
  295. if(max_use)MIN(value, max_value);
  296. return value;
  297. }
  298. void ParamEditor::ParamWindow::Param::set(C Vec2 &value)
  299. {
  300. if(pe)
  301. {
  302. Vec2 v(clamp(value.x), clamp(value.y));
  303. pe->newParam(T, false); // don't refresh gui because it will may delete param, and what's worse we're inside param's function !!
  304. ovr.set(true, QUIET); // adjust override checkbox because we're not refreshing gui
  305. cur=true;
  306. setSkin();
  307. val_textline.enabled(true);
  308. switch(src.type)
  309. {
  310. case PARAM_INT: val_textline.set(S+Round(v.x)); break;
  311. case PARAM_FLT: val_textline.set(S+ v.x ); break;
  312. default : val_textline.set(S+ v ); break;
  313. }
  314. }
  315. }
  316. ::ParamEditor::ParamWindow::Param& ParamEditor::ParamWindow::Param::create(ParamEditor &pe, EditObject &owner, EditParamEx &src) // !! do not store pointer to 'src' because it might be temporary !!
  317. {
  318. T.pe =&pe;
  319. T.owner =&owner;
  320. T.id = src.id;
  321. T.src = src;
  322. T.cur = src.cur;
  323. T.base = src.base;
  324. T.base_ovr= src.base_ovr;
  325. pe.param_window.region+=ovr .create( ).func(ChangedOvr , T).desc(MLT("Override default value", PL,u"Zmień domyślną wartość" , DE,u"Überschreibt Ausgangswert", RU,u"Перезаписать значение по умолчанию", PO,"Substituir o valor default" ));
  326. pe.param_window.region+=remove .create( ).func(RemoveParam , T).desc(MLT("Remove this parameter" , PL,u"Usuń ten parametr (Remove)", DE,"Entfernt diesen Parameter", RU,u"Удалить этот параметр" , PO,u"Remover este parâmetro" )); remove.image="Gui/close.img";
  327. pe.param_window.region+=type .create(Proj.param_type_node).func(ChangedType , T).desc(MLT("Parameter Type" , PL,"Typ Parametru" , DE,"Parameter Typ" , RU,u"Тип параметра" , PO,u"Tipo de Parâmetro" )); type.set(0, QUIET); FlagDisable(type.flag, COMBOBOX_MOUSE_WHEEL);
  328. pe.param_window.region+=name .create( ).func(ChangedName , T).desc(MLT("Parameter Name" , PL,"Nazwa Parametru" , DE,"Parameter Name" , RU,u"Имя параметра" , PO,u"Nome do Parâmetro" ));
  329. pe.param_window.region+=val_checkbox.create( ).func(ChangedCheck, T).focusable(false).desc(MLT("Parameter Value" , PL,u"Wartość Parametru" , DE,"Parameter Wert" , RU,u"Значение параметра" , PO,u"Valor do Parâmtero" ));
  330. pe.param_window.region+=val_textline.create( ).func(ChangedText , T).desc(MLT("Parameter Value" , PL,u"Wartość Parametru" , DE,"Parameter Wert" , RU,u"Значение параметра" , PO,u"Valor do Parâmtero" ));
  331. pe.param_window.region+=val_combobox.create( ).func(ChangedCombo, T).desc(MLT("Parameter Value" , PL,u"Wartość Parametru" , DE,"Parameter Wert" , RU,u"Значение параметра" , PO,u"Valor do Parâmtero" ));
  332. pe.param_window.region+=val_color .create(this ). desc(MLT("Parameter Value" , PL,u"Wartość Parametru" , DE,"Parameter Wert" , RU,u"Значение параметра" , PO,u"Valor do Parâmtero" ));
  333. pe.param_window.region+=val_adjust .create( ).func(MouseEdit , T).subType(BUTTON_TYPE_PROPERTY_VALUE).focusable(false); val_adjust.mode=BUTTON_CONTINUOUS;
  334. pe.param_window.region+=clear .create('C' ).func(ClearValue , T).desc(MLT("Clear this value" , PL,u"Wyczyść wartość (Clear)" , DE,u"Diesen Wert zurücksetzen" , RU,u"Очистить значение" , PO,"Limpar este valor" ));
  335. if(gui=src.gui)
  336. {
  337. if(gui->desc().is())desc(gui->desc()); // override description
  338. priority=gui->priority;
  339. min_use=gui->min_use; min_value=gui->min_value;
  340. max_use=gui->max_use; max_value=gui->max_value;
  341. custom_mouse_edit_speed=gui->mouse_edit_speed;
  342. mouse_edit_mode =gui->mouse_edit_mode;
  343. }
  344. if(pe.multi())multi_obj=true; // if 'multi' option is enabled then assume that parameter belongs to multiple objects
  345. return T;
  346. }
  347. void ParamEditor::ParamWindow::Param::enumChanged()
  348. {
  349. type.setData(Proj.param_type_node);
  350. if(src.type==PARAM_ENUM)toGui();
  351. }
  352. void ParamEditor::ParamWindow::SubObj::RemoveSubObj(SubObj &sub_obj) {sub_obj.pe->removeSubObj(sub_obj);}
  353. ::ParamEditor::ParamWindow::SubObj& ParamEditor::ParamWindow::SubObj::setRect(int i, flt list_w)
  354. {
  355. flt h=elm_h,
  356. y=-h*i,
  357. w=list_w-0.05f;
  358. remove.rect(Rect_LU( 0, y, 0.05f, h));
  359. obj .rect(Rect_LU(remove.rect().max.x, y, w , h));
  360. return T;
  361. }
  362. ::ParamEditor::ParamWindow::SubObj& ParamEditor::ParamWindow::SubObj::create(ParamEditor &pe, EditObject::SubObj &src, int i, flt list_w)
  363. {
  364. T.pe=&pe;
  365. T.id= src.id;
  366. pe.param_window.region+=remove.create('R').func(RemoveSubObj, T).desc(MLT("Remove this object", PL,u"Usuń ten obiekt" , DE,"Dieses Objekt entfernen", RU,u"Удалить этот объект" , PO,"Remover este objecto"));
  367. pe.param_window.region+=obj .create( ).desc("Sub Object"); //.func( EditSubObj, T).desc(MLT("Edit this object" , PL,"Edytuj ten obiekt", DE,"Dieses Objekt editieren", RU,"Редактирование объекта", PO,"Editar este objecto" ));
  368. obj.text=Proj.elmFullName(src.elm_obj_id);
  369. if(Proj.invalidRef(src.elm_obj_id))obj.skin=&RedSkin;
  370. return setRect(i, list_w);
  371. }
  372. ParamEditor::ParamWindow::ParamWindow(ParamEditor &pe) : pe(pe) {}
  373. void ParamEditor::ParamWindow::NewParam(ParamEditor &pe) {pe.newParam();}
  374. int ParamEditor::ParamWindow::CompareParam(C Param &p0, C Param &p1) {if(int c=Compare(p0.priority, p1.priority))return c; return Compare(p0.src, p1.src);}
  375. ::ParamEditor::ParamWindow& ParamEditor::ParamWindow::create(C Rect &rect, bool is_class)
  376. {
  377. T.is_class=is_class;
  378. ::EE::Window::create("Object Class Editor").barVisible(is_class).visible(!is_class);
  379. if(!is_class){resize_mask=DIRF_LEFT|DIRF_DOWN; FlagDisable(flag, WIN_MOVABLE); skin(&NoShadowSkin); button[2].func(null);}else button[2].func(HideProjAct, SCAST(GuiObj, T)).show(); flag|=WIN_RESIZABLE;
  380. ts.reset().size=0.035f; ts.align.set(1, -1);
  381. T+=type .create("Type" , &ts);
  382. T+=name .create("Name" , &ts);
  383. T+=value .create("Value", &ts);
  384. T+=new_par.create(Rect_LU(0, 0, 0.18f, 0.041f), MLTC(u"New Param", PL, u"Dodaj Parametr", DE, u"Param hinzufügen", RU, u"Добавить параметр", PO, u"Adicionar Parâmetro")).func(NewParam, pe);
  385. T+=region .create().slidebarSize(0.04f).skin(&LightSkin, false);
  386. return T.rect(rect);
  387. }
  388. Rect ParamEditor::ParamWindow::sizeLimit()C
  389. {
  390. Rect r=::EE::Window::sizeLimit();
  391. r.min.set(is_class ? 0.72f : 0.69f, is_class ? 0.33f : 0.15f);
  392. if(!is_class)r.max.set(2.0f, screenPos().y-(-D.h())-0.1f);
  393. return r;
  394. }
  395. flt ParamEditor::ParamWindow::listWidth() {return region.rect().w()-region.slidebarSize();}
  396. C Rect& ParamEditor::ParamWindow::rect()C {return ::EE::Window::rect();}
  397. ::ParamEditor::ParamWindow& ParamEditor::ParamWindow::rect(C Rect &rect)
  398. {
  399. ::EE::Window::rect(rect);
  400. flt y=(is_class ? -0.16f : -0.01f);
  401. type .pos(Vec2(0.06f , y));
  402. name .pos(Vec2(type.pos().x+0.24f, y));
  403. value.pos(Vec2(Lerp(name.pos().x, clientWidth()-0.10f, 0.4f), y));
  404. new_par.move(Vec2(clientWidth()-0.01f, y)-new_par.rect().ru());
  405. region.rect(Rect(0, -clientHeight(), clientWidth(), new_par.rect().min.y).extend(-0.01f));
  406. REPAO( params).setRect( i, listWidth());
  407. REPAO(sub_objs).setRect(params.elms()+1+i, listWidth());
  408. return T;
  409. }
  410. void ParamEditor::ParamWindow::update(C GuiPC &gpc)
  411. {
  412. ::EE::ClosableWindow::update(gpc);
  413. if(!is_class && rect().h()>sizeLimit().max.y+EPS)rect(rect());
  414. if(Gui.window()==this)
  415. {
  416. REPA(MT)if(MT.bp(i))
  417. {
  418. REPA(params)
  419. if(MT.guiObj(i)==&params[i].name && params[i].multi_obj && params[i].cur)pe.rename_window.rename(params[i].src.name, params[i].src.type, params[i].id);
  420. }
  421. if(Kb.k(KB_ENTER))REPA(params)if(Gui.kb()==&params[i].name || Gui.kb()==&params[i].val_textline) // refresh/sort on enter (this can sort by name after changing one, or resolve ID's to text after typing them manually)
  422. {
  423. toGui();
  424. Kb.eatKey();
  425. break;
  426. }
  427. }
  428. }
  429. ::ParamEditor::ParamWindow& ParamEditor::ParamWindow::hide(){if(visible()){::EE::Window::hide(); pe.paramWindowHidden();} return T;}
  430. ::ParamEditor::ParamWindow::Param* ParamEditor::ParamWindow::findParam(GuiObj *go)
  431. {
  432. if(contains(go))REPA(params)if(params[i].contains(go))return &params[i];
  433. return null;
  434. }
  435. void ParamEditor::ParamWindow::addParams(EditObject &obj)
  436. {
  437. // first get all params of single 'obj' object
  438. Memt<EditParamEx> obj_params;
  439. // current parameters first (so param.id will be set to current id)
  440. Memt<EditObject*> bases;
  441. bases.add(&obj); FREPA(obj)
  442. {
  443. EditParam &src=obj[i];
  444. if(!src.removed)obj_params.New().create(src, CUR);
  445. }
  446. // base parameters
  447. for(EditObject *base=&obj; base=base->base(); )
  448. {
  449. if(!bases.include(base))break; // if base was already processed
  450. FREPA(*base)
  451. {
  452. EditParam &src=(*base)[i];
  453. if(!src.removed)
  454. {
  455. // find existing
  456. REPA(obj_params)if(obj_params[i].name==src.name){obj_params[i].base=true; goto found_base;}
  457. obj_params.New().create(src, BASE);
  458. found_base:;
  459. }
  460. }
  461. }
  462. // base parameters from overriden type
  463. if((obj.flag&EditObject::OVR_TYPE) && obj.type.valid() && obj.base) // check if that object has base (which means that it's not OBJ_CLASS)
  464. if(EditObjectPtr obj_class=Proj.editPath(obj.type))
  465. if(bases.include(obj_class()))
  466. FREPA(*obj_class)
  467. {
  468. EditParam &src=(*obj_class)[i];
  469. if(!src.removed)
  470. {
  471. // find existing
  472. REPA(obj_params)if(obj_params[i].name==src.name){obj_params[i].base_ovr=true; goto found_base_ovr;}
  473. obj_params.New().create(src, BASE_OVR);
  474. found_base_ovr:;
  475. }
  476. }
  477. // base parameters from editor type
  478. if(C Memc<GuiEditParam> *edit_params=EditObjType.findParams(obj.editor_type))REPA(*edit_params)
  479. {
  480. C GuiEditParam &src=(*edit_params)[i];
  481. // find existing
  482. REPA(obj_params)if(obj_params[i].name==src.name){obj_params[i].gui=&src; obj_params[i].base=true; goto found_edit;}
  483. obj_params.New().create(src, BASE).gui=&src;
  484. found_edit:;
  485. }
  486. // now when we have object parameters, add them to the list of all params
  487. FREPA(obj_params)
  488. {
  489. EditParamEx &src=obj_params[i];
  490. REPA(params)
  491. {
  492. Param &param=params[i];
  493. if(ParamCompatible(param.src, src) && param.owner!=&obj) // here check also type, check only params from different object (so we won't merge multiple params of the same object)
  494. {
  495. param.multi_obj =true; // this belongs to multiple objects and potentialy has multiple id's
  496. if( param.base!=src.base ){param.multi_base=true; param.base=true;} // have different 'base', also set 'base' to true to specify that at least one object has 'base' enabled
  497. if( param.cur !=src.cur ){param.multi_cur =true; param.cur =true;} // have different 'cur ', also set 'cur ' to true to specify that at least one object has 'cur ' enabled
  498. if(Compare(param.src , src )) param.multi_val =true; // have different values
  499. goto processed;
  500. }
  501. }
  502. params.New().create(pe, obj, src); // it was not found, so add new
  503. processed:;
  504. }
  505. }
  506. void ParamEditor::ParamWindow::addSubObjs(EditObject &obj)
  507. {
  508. int order=params.elms()+1;
  509. FREPA(obj.sub_objs)
  510. {
  511. EditObject::SubObj &src=obj.sub_objs[i];
  512. if(!src.removed)sub_objs.New().create(pe, src, order++, listWidth());
  513. }
  514. }
  515. void ParamEditor::ParamWindow::setSkin() {REPAO(params).setSkin();}
  516. void ParamEditor::ParamWindow::toGui()
  517. {
  518. Vec2 offset(region.slidebar[0].offset(), region.slidebar[1].offset());
  519. params .clear();
  520. sub_objs.clear();
  521. // add params from all objects
  522. if(pe.world)REPA(Selection)addParams(Selection[i].params);else
  523. if(pe.p ) addParams(*pe.p);
  524. params.sort(CompareParam);
  525. FREPAO(params).moveToTop().setRect(i, listWidth()).toGui(); // go from start because of 'moveToTop'
  526. // add sub objects
  527. if(pe.world){if(Selection.elms()==1)addSubObjs(Selection[0].params);}
  528. else addSubObjs(*pe.p);
  529. region.slidebar[0].offset(offset.x);
  530. region.slidebar[1].offset(offset.y);
  531. }
  532. void ParamEditor::Change::create(ptr user)
  533. {
  534. if(ParamEditor *editor=(ParamEditor*)user)if(editor->p)
  535. {
  536. data=*editor->p;
  537. editor->undoVis();
  538. }
  539. }
  540. void ParamEditor::Change::apply(ptr user)
  541. {
  542. if(ParamEditor *editor=(ParamEditor*)user)if(editor->p)
  543. {
  544. editor->p->undo(data, Proj.edit_path);
  545. editor->setChanged();
  546. editor->undoVis();
  547. }
  548. }
  549. C Str& ParamEditor::MeshVariation::originalName()C {return id ? name : S;}
  550. void ParamEditor::MeshVariation::set(C Str &name, uint id) {T.name=name; T.id=id;}
  551. void ParamEditor::undoVis() {SetUndo(undos, undo, redo);}
  552. void ParamEditor::multiFunc(bool Func(ParamEditor &pe, EditObject &obj, cptr user), cptr user)
  553. {
  554. if(multi())REPA(Proj.list.sel)if(Elm *elm=Proj.list.absToElm(Proj.list.sel[i]))if(ElmObj *data=elm->objData())if(elm!=ObjEdit.obj_elm)
  555. {
  556. ObjectPtr game =Proj.gamePath(elm->id); // get data of the previous version from the file
  557. TerrainObj2 old_terrain=*game;
  558. PhysPath old_phys =*game;
  559. EditObject edit; edit.load(Proj.editPath(elm->id));
  560. Proj.elmChanging(*elm);
  561. if(Func(T, edit, user))
  562. {
  563. Save(edit, Proj.editPath(elm->id));
  564. data->from(edit);
  565. data->newVer();
  566. Proj.makeGameVer(*elm);
  567. Proj.elmChanged (*elm);
  568. if(old_terrain!=TerrainObj2(*game))Proj.rebuildEmbedForObj(elm->id ); // if saving changed 'terrainObj'
  569. if(old_phys !=PhysPath (*game))Proj.rebuildPathsForObj(elm->id, true); // if saving changed 'physPath', rebuild only for objects that don't override paths (if they override then it means that changing the base doesn't affect their path mode), we must rebuild this also for objects with final path mode set to ignore, in case we've just disabled paths
  570. Server.setElmLong(elm->id);
  571. }
  572. }
  573. }
  574. bool ParamEditor::ChangedOConst(ParamEditor &pe, EditObject &obj, cptr user)
  575. {
  576. obj.setConst(pe.o_const(), obj.constant());
  577. return true;
  578. }
  579. void ParamEditor::ChangedOConst(ParamEditor &pe)
  580. {
  581. pe.setUndo("const");
  582. if(pe.world)REPA(Selection)ChangedOConst(pe, Selection[i].params);
  583. else ChangedOConst(pe, *pe.p);
  584. pe.multiFunc(ChangedOConst);
  585. pe.setChanged();
  586. }
  587. bool ParamEditor::ChangedConst(ParamEditor &pe, EditObject &obj, cptr user)
  588. {
  589. obj.setConst(true, pe.v_const());
  590. return true;
  591. }
  592. void ParamEditor::ChangedConst(ParamEditor &pe)
  593. {
  594. pe.setUndo("const");
  595. if(pe.world)REPA(Selection)ChangedConst(pe, Selection[i].params);
  596. else ChangedConst(pe, *pe.p);
  597. pe.multiFunc(ChangedConst);
  598. pe.setChanged();
  599. }
  600. void ParamEditor::ChangedOClass(ParamEditor &pe) // Access is merged with Type/Class
  601. {
  602. if(pe.world)
  603. {
  604. Selection.setUndo();
  605. REPA(Selection)
  606. {
  607. Obj &obj=Selection[i]; TerrainObj terrain=obj.terrainObj();
  608. obj.params.setType(pe.o_class(), obj.params.type, Proj.edit_path).setAccess(pe.o_class(), obj.params.access);
  609. obj.setMeshPhys(); // could have changed 'editor_type/draw_as'
  610. obj.setChanged(); // call before 'setChangedEmbed'
  611. if(obj.terrainObj()!=terrain)obj.setChangedEmbed();
  612. }
  613. pe.toGui();
  614. }else
  615. {
  616. // this is not available for non-world objects
  617. }
  618. ObjList.setChanged();
  619. }
  620. bool ParamEditor::ChangedClass(ParamEditor &pe, EditObject &obj, C AccessType &at)
  621. {
  622. if(at.access==OBJ_ACCESS_CUSTOM)obj.setAccess(false ).setBase(Proj.editPath(at.type), Proj.edit_path); // use access from base
  623. else obj.setAccess(true, at.access).setBase(null , Proj.edit_path);
  624. return true;
  625. }
  626. void ParamEditor::ChangedClass(ParamEditor &pe) // Access is merged with Type/Class
  627. {
  628. AccessType at;
  629. bool found=false;
  630. REP (ObjAccessNamesElms)if(i!=OBJ_ACCESS_CUSTOM && Equal(pe.v_class.text(), ObjAccessNames[i], true)){at.access=OBJ_ACCESS(i); found=true; break;} // don't check CUSTOM here, it will be checked below
  631. if(!found)REPA(Proj.existing_obj_classes)if(Elm *obj_class=Proj.findElm(Proj.existing_obj_classes[i]))if(Equal(obj_class->name, pe.v_class.text(), true)){at.access=OBJ_ACCESS_CUSTOM; at.type=obj_class->id; found=true; break;}
  632. if( found)
  633. {
  634. pe.setUndo("access");
  635. if(pe.world) // in world edit we operate on access and type only (base can be drag and dropped separately)
  636. {
  637. REPA(Selection)
  638. {
  639. Obj &obj=Selection[i]; TerrainObj terrain=obj.terrainObj();
  640. if(at.access==OBJ_ACCESS_CUSTOM)obj.params.setAccess(true, at.access).setType(true , at.type, Proj.edit_path);
  641. else obj.params.setAccess(true, at.access).setType(false, UIDZero, Proj.edit_path);
  642. obj.setMeshPhys(); // could have changed 'editor_type/draw_as'
  643. obj.setChanged(); // call before 'setChangedEmbed'
  644. if(obj.terrainObj()!=terrain )obj.setChangedEmbed();
  645. }
  646. }else // in obj edit we operate on base and access only (type cannot be changed, it is always taken from base)
  647. {
  648. ChangedClass(pe, *pe.p, at);
  649. pe.setChanged(false);
  650. }
  651. pe.multiFunc(ChangedClass, at);
  652. pe.toGui();
  653. ObjList.setChanged();
  654. }
  655. }
  656. void ParamEditor::ChangedEditorType(ParamEditor &pe)
  657. {
  658. UID type=EditObjType.elmID(pe.v_editor_type());
  659. pe.setUndo("editorType");
  660. if(pe.world)
  661. {
  662. REPAO(Selection).params.setEditorType(type);
  663. }else
  664. {
  665. pe.p->setEditorType(type);
  666. }
  667. pe.setChanged();
  668. }
  669. bool ParamEditor::ChangedOPath(ParamEditor &pe, EditObject &obj, cptr user)
  670. {
  671. obj.setPath(pe.o_path(), obj.path);
  672. return true;
  673. }
  674. void ParamEditor::ChangedOPath(ParamEditor &pe)
  675. {
  676. pe.setUndo("path");
  677. if(pe.world)
  678. {
  679. REPA(Selection)
  680. {
  681. Obj &obj=Selection[i]; PhysPath phys_path=obj.physPath();
  682. ChangedOPath(pe, obj.params);
  683. if(obj.physPath()!=phys_path)obj.setChangedPaths();
  684. }
  685. }else ChangedOPath(pe, *pe.p);
  686. pe.multiFunc(ChangedOPath);
  687. pe.setChanged();
  688. }
  689. bool ParamEditor::ChangedPath(ParamEditor &pe, EditObject &obj, cptr user)
  690. {
  691. obj.setPath(true, (OBJ_PATH)pe.v_path());
  692. return true;
  693. }
  694. void ParamEditor::ChangedPath(ParamEditor &pe)
  695. {
  696. pe.setUndo("path");
  697. if(pe.world)
  698. {
  699. REPA(Selection)
  700. {
  701. Obj &obj=Selection[i]; PhysPath phys_path=obj.physPath();
  702. ChangedPath(pe, obj.params);
  703. if(obj.physPath()!=phys_path)obj.setChangedPaths();
  704. }
  705. }else ChangedPath(pe, *pe.p);
  706. pe.multiFunc(ChangedPath);
  707. pe.setChanged();
  708. }
  709. bool ParamEditor::ChangedOMeshVariation(ParamEditor &pe, EditObject &obj, cptr user)
  710. {
  711. obj.setMeshVariation(pe.o_mesh_var(), obj.mesh_variation_id);
  712. return true;
  713. }
  714. void ParamEditor::ChangedOMeshVariation(ParamEditor &pe)
  715. {
  716. pe.setUndo("meshVar");
  717. if(pe.world)
  718. {
  719. REPA(Selection)
  720. {
  721. Obj &obj=Selection[i];
  722. ChangedOMeshVariation(pe, obj.params);
  723. obj.setMeshVariation();
  724. obj.setChangedEmbed();
  725. }
  726. }else ChangedOMeshVariation(pe, *pe.p);
  727. //pe.multiFunc(ChangedOMeshVariation); this can't be 'multi' because each object mesh has its unique mesh variation ID's
  728. pe.setChanged();
  729. }
  730. bool ParamEditor::ChangedMeshVariation(ParamEditor &pe, EditObject &obj, cptr user)
  731. {
  732. obj.setMeshVariation(true, InRange(pe.v_mesh_var(), pe.mesh_variations) ? pe.mesh_variations[pe.v_mesh_var()].id : 0);
  733. return true;
  734. }
  735. void ParamEditor::ChangedMeshVariation(ParamEditor &pe)
  736. {
  737. pe.setUndo("meshVar");
  738. if(pe.world)
  739. {
  740. bool has_var_name=false; Str8 var_name; if(InRange(pe.v_mesh_var(), pe.mesh_variations)){has_var_name=true; var_name=pe.mesh_variations[pe.v_mesh_var()].originalName();}
  741. REPA(Selection)
  742. {
  743. Obj &obj=Selection[i];
  744. if(obj.mesh_proper==Selection[0].mesh_proper) // process objects only with the same mesh, this is because other meshes have different variation ID's
  745. {
  746. ChangedMeshVariation(pe, obj.params);
  747. obj.setMeshVariation();
  748. obj.setChangedEmbed();
  749. }else
  750. if(has_var_name && obj.mesh_proper) // for different meshes, find by name
  751. {
  752. int var_i=obj.mesh_proper->variationFind(var_name); if(var_i>=0) // set only if a name match was found
  753. {
  754. obj.params.setMeshVariation(true, obj.mesh_proper->variationID(var_i));
  755. obj.setMeshVariation();
  756. obj.setChangedEmbed();
  757. }
  758. }
  759. }
  760. }else ChangedMeshVariation(pe, *pe.p);
  761. //pe.multiFunc(ChangedMeshVariation); this can't be 'multi' because each object mesh has its unique mesh variation ID's
  762. pe.setChanged();
  763. }
  764. void ParamEditor::ToBase(ParamEditor &pe)
  765. {
  766. EditObject *params=null;
  767. if(pe.world)
  768. {
  769. if(Selection.elms())params=&Selection[0].params;
  770. }else
  771. {
  772. params=pe.p;
  773. }
  774. if(params)
  775. {
  776. if(Elm *elm=Proj.findElm(params->base.id()))switch(elm->type)
  777. {
  778. case ELM_OBJ : ObjEdit .activate(elm); break;
  779. case ELM_OBJ_CLASS: ObjClassEdit.set (elm); break;
  780. }
  781. }
  782. }
  783. void ParamEditor::ChangedMulti(ParamEditor &edit) {edit.undo.hidden(edit.multi()); edit.redo.hidden(edit.multi()); edit.param_window.toGui();}
  784. void ParamEditor::Undo(ParamEditor &edit) {edit.undos.undo();}
  785. void ParamEditor::Redo(ParamEditor &edit) {edit.undos.redo();}
  786. ::ParamEditor::ParamWindow::Param* ParamEditor::findParam(GuiObj *go)
  787. {
  788. return param_window.findParam(go);
  789. }
  790. void ParamEditor::setUndo(cptr change_type, bool force_create)
  791. {
  792. if(world)
  793. {
  794. Selection.setUndo();
  795. }else
  796. {
  797. undos.set(change_type, force_create);
  798. }
  799. }
  800. void ParamEditor::setChanged(bool refresh_gui)
  801. {
  802. if(world)
  803. {
  804. REPAO(Selection).setChanged();
  805. }else
  806. {
  807. changed=true;
  808. }
  809. if(refresh_gui)toGui();
  810. }
  811. void ParamEditor::toGuiMeshVariation(uint mesh_variation_id)
  812. {
  813. REPA(mesh_variations)if(!i || mesh_variations[i].id==mesh_variation_id){v_mesh_var.set(i, QUIET); break;} // go from end, and if didn't found a custom variation, then stop on #0 default variation
  814. }
  815. void ParamEditor::toGui(bool params)
  816. {
  817. meshVariationChanged();
  818. if(world)
  819. {
  820. if(Selection.elms())
  821. {
  822. Obj &obj_full=Selection[0];
  823. EditObject &obj=obj_full.params;
  824. bool any_custom=(obj.access==OBJ_ACCESS_CUSTOM);
  825. bool mbase =false; EditObjectPtr base =obj.base ;
  826. bool oaccess =FlagTest(obj.flag, EditObject::OVR_ACCESS ), moaccess =false, maccess =false; OBJ_ACCESS access =obj.access ;
  827. bool otype =FlagTest(obj.flag, EditObject::OVR_TYPE ), motype =false, mtype =false; UID type =obj.type ;
  828. bool oconst =FlagTest(obj.flag, EditObject::OVR_CONST ), moconst =false, mconst =false; bool bconst =obj.constant();
  829. bool opath =FlagTest(obj.flag, EditObject::OVR_PATH ), mopath =false, mpath =false; OBJ_PATH path =obj.path ;
  830. bool omesh_var=FlagTest(obj.flag, EditObject::OVR_MESH_VARIATION), momesh_var=false, mmesh_var=false; uint mesh_var=obj.mesh_variation_id; cchar8 *mesh_var_name=obj_full.variationName();
  831. //bool oscale =FlagTest(obj.flag, OBJ_OVR_SCALE ), moscale =false, mscale =false; Vec scale =obj.scale3 () ;
  832. //bool omesh =FlagTest(obj.flag, OBJ_OVR_MESH ), momesh =false, mmesh =false; Mesh *mesh =obj.mesh ()();
  833. //bool ophys =FlagTest(obj.flag, OBJ_OVR_PHYS ), mophys =false, mphys =false; PhysBody *phys =obj.phys ()();
  834. //bool omtrl =FlagTest(obj.flag, OBJ_OVR_MATERIAL ), momtrl =false, mmtrl =false; Material *material=obj.material()();
  835. //bool oalign =FlagTest(obj.flag, OBJ_OVR_ALIGN ), moalign =false, malign =false; UInt align =(obj.alignX()|(obj.alignY()<<2)|(obj.alignZ()<<4));
  836. REPA(Selection)
  837. {
  838. Obj &obj_full=Selection[i];
  839. EditObject &obj=obj_full.params;
  840. if(obj.access==OBJ_ACCESS_CUSTOM)any_custom=true;
  841. if(base !=obj.base )mbase =true;
  842. if(oaccess !=FlagTest(obj.flag, EditObject::OVR_ACCESS )){oaccess =false; moaccess =true;} if(access !=obj.access )maccess =true;
  843. if(otype !=FlagTest(obj.flag, EditObject::OVR_TYPE )){otype =false; motype =true;} if(type !=obj.type )mtype =true;
  844. if(oconst !=FlagTest(obj.flag, EditObject::OVR_CONST )){oconst =false; moconst =true;} if(bconst !=obj.constant() )mconst =true;
  845. if(opath !=FlagTest(obj.flag, EditObject::OVR_PATH )){opath =false; mopath =true;} if(path !=obj.path )mpath =true;
  846. if(omesh_var!=FlagTest(obj.flag, EditObject::OVR_MESH_VARIATION)){omesh_var=false; momesh_var=true;} if(mesh_var!=obj.mesh_variation_id && !Equal(mesh_var_name, obj_full.variationName()))mmesh_var=true;
  847. //if(oscale !=FlagTest(obj.flag, OBJ_OVR_SCALE )){oscale =false; moscale =true;} if(!Equal(scale , obj.scale3 () ))mscale =true;
  848. //if(omesh !=FlagTest(obj.flag, OBJ_OVR_MESH )){omesh =false; momesh =true;} if( mesh !=obj.mesh ()())mmesh =true;
  849. //if(ophys !=FlagTest(obj.flag, OBJ_OVR_PHYS )){ophys =false; mophys =true;} if( phys !=obj.phys ()())mphys =true;
  850. //if(omtrl !=FlagTest(obj.flag, OBJ_OVR_MATERIAL )){omtrl =false; momtrl =true;} if( material!=obj.material()())mmtrl =true;
  851. //if(oalign !=FlagTest(obj.flag, OBJ_OVR_ALIGN )){oalign =false; moalign =true;} if( align !=(obj.alignX()|(obj.alignY()<<2)|(obj.alignZ()<<4)))malign=true;
  852. // force default |false| in above codes so next first click will toggle to true
  853. }
  854. T.t_obj .set (mbase ? MultipleName : Proj.elmFullName(base.id()), QUIET);
  855. T.o_const .set(oconst , QUIET); if(moconst )T.o_const .setMulti(); if(mconst )T.v_const .setMulti( );else T.v_const.set(bconst, QUIET); T.o_const.visible(any_custom); T.v_const.visible(any_custom);
  856. T.o_path .set(opath , QUIET); if(mopath )T.o_path .setMulti(); if(mpath )T.v_path .setText (MultipleName, true, QUIET);else T.v_path .set(path , QUIET);
  857. T.o_mesh_var.set(omesh_var , QUIET); if(momesh_var )T.o_mesh_var.setMulti(); if(mmesh_var )T.v_mesh_var.setText (MultipleName, true, QUIET);else toGuiMeshVariation(mesh_var);
  858. T.o_class .set(oaccess || otype, QUIET); if(moaccess || motype)T.o_class .setMulti(); if(maccess || mtype)T.v_class .setText (MultipleName, true, QUIET);else
  859. {
  860. Elm *obj_class=null; if(access==OBJ_ACCESS_CUSTOM)obj_class=Proj.findElm(type);
  861. if(obj_class )T.v_class.setText(obj_class->name , true, QUIET);else
  862. if(InRange(access, ObjAccessNamesElms))T.v_class.setText(ObjAccessNames[access], true, QUIET);else T.v_class.set(-1, QUIET);
  863. }
  864. //T.oscale.set(oscale, QUIET); if(moscale)T.oscale.setMulti(); T.scale .set(mscale ? MultipleName : TextScale(scale ), QUIET).disabled(!oscale && !moscale);
  865. //T.omesh .set(omesh , QUIET); if(momesh )T.omesh .setMulti(); T.mesh .set(mmesh ? MultipleName : Meshes .name(mesh ), QUIET);
  866. //T.ophys .set(ophys , QUIET); if(mophys )T.ophys .setMulti(); T.phys .set(mphys ? MultipleName : PhysBodies.name(phys ), QUIET); make_phys.hide();
  867. //T.omtrl .set(omtrl , QUIET); if(momtrl )T.omtrl .setMulti(); T.mtrl .set(mmtrl ? MultipleName : Materials .name(material), QUIET);
  868. //T.oalign.set(oalign, QUIET); if(moalign)T.oalign.setMulti(); T.align[0].set((align&3)-1, QUIET); T.align[1].set(((align>>2)&3)-1, QUIET); T.align[2].set(((align>>4)&3)-1, QUIET);
  869. }
  870. }else
  871. {
  872. UID base_id=p->base.id(); Elm *base=Proj.findElm(base_id); if(!base)base=Proj.findElm(p->type); // just in case
  873. b_class.visible(base!=null);
  874. v_class.skin(Proj.invalidRef(base_id) ? &RedSkin : null, false); // don't change skin of menu because we only set red color here
  875. if(base && base->type==ELM_OBJ )v_class.setText(Proj.elmFullName(base) , true, QUIET);else
  876. if(base && base->type==ELM_OBJ_CLASS )v_class.setText(base->name , true, QUIET);else
  877. if(InRange(p->access, ObjAccessNamesElms))v_class.setText(ObjAccessNames[p->access], true, QUIET);else v_class.set(-1, QUIET);
  878. o_const .set(FlagTest(p->flag, EditObject::OVR_CONST ), QUIET).visible(p->access==OBJ_ACCESS_CUSTOM); v_const.set(p->constant(), QUIET).visible(p->access==OBJ_ACCESS_CUSTOM);
  879. o_path .set(FlagTest(p->flag, EditObject::OVR_PATH ), QUIET); v_path.set(p->path, QUIET);
  880. v_editor_type.set(Max(0, EditObjType.find(p->editor_type)), QUIET); // if not found then default to 0, hence "Max(0, -1)" -> 0
  881. o_mesh_var .set(FlagTest(p->flag, EditObject::OVR_MESH_VARIATION), QUIET); toGuiMeshVariation(p->mesh_variation_id);
  882. }
  883. if(params)param_window.toGui();
  884. }
  885. void ParamEditor::setSkin() {param_window.setSkin();}
  886. void ParamEditor::create(GuiObj &parent, bool is_class, bool world)
  887. {
  888. T.world=world;
  889. flt y=-0.03f, h=0.04f;
  890. ListColumn name_desc_column[]=
  891. {
  892. ListColumn(MEMBER(NameDesc, name), LCW_MAX_DATA_PARENT, "Name"),
  893. };
  894. ts_black.reset(); ts_black.size=0.038f; ts_black.align.set(1, 0);
  895. ts_white.reset(); ts_white.size=0.038f; ts_white.align.set(1, 0);
  896. if(!is_class)
  897. {
  898. flt w=0.63f;
  899. parent+=::EE::Region::create().skin(&TransparentSkin, false); kb_lit=false;
  900. if(world)
  901. {
  902. T+=b_class.create(Rect_L(0.01f, y, 0.16f, h), "Object").func(ToBase, T).desc("Keyboard Shortctut: Ctrl+E"); T+=t_obj.create(Rect_L(0.22f, y, 0.40f, h)).disabled(true).desc("Drag and drop an object here"); y-=h;
  903. }else
  904. {
  905. y-=0.005f;
  906. T+=multi.create(Rect_L (0.010f, y , 0.10f, 0.044f), "Multi").func(ChangedMulti, T).focusable(false).desc("When this option is enabled, all changes made to the object will additionally be applied to all currently selected project element objects.\nThis allows to modify multiple objects at the same time."); multi.mode=BUTTON_TOGGLE;
  907. T+=undo .create(Rect_L (0.22f , y , 0.05f, 0.05f )).func(Undo, T).focusable(false).desc("Undo"); undo.image="Gui/Misc/undo.img";
  908. T+=redo .create(Rect_LU(undo .rect().ru(), 0.05f, 0.05f )).func(Redo, T).focusable(false).desc("Redo"); redo.image="Gui/Misc/redo.img";
  909. y-=0.05f;
  910. }
  911. T+=t_class .create(Vec2(0.01f, y), "Class" , &ts_white); if(world)T+=o_class .create(Rect_L(0.17f, y, h, h)).func(ChangedOClass , T).desc("Override default class" );else T+=b_class.create(Rect_L(0.01f, y, 0.10f, h), "Class").func(ToBase, T); T+=v_class .create(Rect_L(0.22f , y, 0.4f, h)).func(ChangedClass , T).desc(S+"Object Class"+(world ? "" : "\nYou can drag and drop an object here to set it as the base")); y-=h;
  912. T+=t_const .create(Vec2(0.01f, y), "Const" , &ts_white); T+=o_const .create(Rect_L(0.17f, y, h, h)).func(ChangedOConst , T).desc("Override default const" ); T+=v_const .create(Rect_L(0.22f+(0.4f-h)/2, y, h, h)).func(ChangedConst , T).desc("Object Const Mode.\nThis option should be enabled if the object will not be changed during the entire game.\nWhen the option is enabled the object will not be included in the SaveGame file,\nmaking the save file smaller."); y-=h;
  913. T+=t_path .create(Vec2(0.01f, y), "Path Mesh", &ts_white); T+=o_path .create(Rect_L(0.17f, y, h, h)).func(ChangedOPath , T).desc("Override default path mesh" ); T+=v_path .create(Rect_L(0.22f , y, 0.4f, h)).func(ChangedPath , T).desc("Object Path Mesh Generation Mode."); v_path.setColumns(name_desc_column, Elms(name_desc_column)).setData(path_mode, Elms(path_mode)).menu.list.setElmDesc(MEMBER(NameDesc, desc)); y-=h;
  914. T+=t_mesh_var.create(Vec2(0.01f, y), "Variation", &ts_white); T+=o_mesh_var.create(Rect_L(0.17f, y, h, h)).func(ChangedOMeshVariation, T).desc("Override default mesh variation"); T+=v_mesh_var.create(Rect_L(0.22f , y, 0.4f, h)).func(ChangedMeshVariation, T).desc("Mesh Material Variation."); y-=h/2+0.01f;
  915. rect(Rect_LU(0, 0, w, -y));
  916. }
  917. parent+=param_window.create(is_class ? Rect(Rect_C(0, 0, 1, 1)) : Rect_RU(rect().rd(), rect().w(), 0.3f), is_class);
  918. if(is_class)
  919. {
  920. param_window.button[1].show();
  921. param_window+=t_const .create(Vec2(0.01f, y), "Const" , &ts_black); param_window+=v_const .create(Rect_L(0.18f+(0.23f-h)/2, y, h, h)).func(ChangedConst , T).desc("Object Const Mode.\nThis option should be enabled if the object will not be changed during the entire game.\nWhen the option is enabled the object will not be included in the SaveGame file,\nmaking the save file smaller."); y-=h;
  922. param_window+=t_path .create(Vec2(0.01f, y), "Path Mesh", &ts_black); param_window+=v_path .create(Rect_L(0.18f , y, 0.23f, h)).func(ChangedPath , T).desc("Object Path Mesh Generation Mode." ); v_path.setColumns(name_desc_column, Elms(name_desc_column)).setData( path_mode, Elms( path_mode)).menu.list.setElmDesc(MEMBER(NameDesc, desc)); y-=h;
  923. param_window+=t_editor_type.create(Vec2(0.01f, y), "Draw As" , &ts_black); param_window+=v_editor_type.create(Rect_L(0.18f , y, 0.23f, h)).func(ChangedEditorType, T).desc("How the object should be drawn in the editor."); v_editor_type.setColumns(name_desc_column, Elms(name_desc_column)).setData(etype_mode, Elms(etype_mode)).menu.list.setElmDesc(MEMBER(NameDesc, desc)); y-=h/2+0.01f;
  924. param_window+=undo .create(Rect_LU(v_path.rect().max.x+0.05f, v_const.rect().max.y, 0.05f, 0.05f)).func(Undo, T).focusable(false).desc("Undo"); undo.image="Gui/Misc/undo.img";
  925. param_window+=redo .create(Rect_LU( undo.rect().ru() , 0.05f, 0.05f)).func(Redo, T).focusable(false).desc("Redo"); redo.image="Gui/Misc/redo.img";
  926. }
  927. rename_window.create();
  928. ListColumn lc[]=
  929. {
  930. ListColumn(MEMBER(MeshVariation, name), LCW_MAX_DATA_PARENT, "Name"),
  931. };
  932. v_mesh_var.setColumns(lc, Elms(lc), true);
  933. }
  934. void ParamEditor::enumChanged()
  935. {
  936. v_class.setData(Proj.obj_class_node);
  937. REPAO(param_window.params).enumChanged();
  938. toGui(false);
  939. }
  940. void ParamEditor::meshVariationChanged()
  941. {
  942. mesh_variations.setNum(1)[0].set("Default", 0);
  943. MeshPtr mesh=null;
  944. if(world)
  945. {
  946. if(Selection.elms())mesh=Selection[0].mesh_proper;
  947. }else
  948. {
  949. mesh=&ObjEdit.mesh;
  950. if(!OverrideMeshSkel(mesh(), ObjEdit.mesh_skel)) // if mesh is empty, then use variation ID from base
  951. #if 1 // this works on temporary data
  952. if(Elm *base_obj=Proj.findElm(p->base.id(), ELM_OBJ))
  953. #else // this works only on saved data
  954. if(Elm *obj_elm=ObjEdit.obj_elm)if(ElmObj *obj_data=obj_elm.objData())if(Elm *base_obj=Proj.findElm(obj_data.base_id, ELM_OBJ))
  955. #endif
  956. if(ElmObj *base_data=base_obj->objData())
  957. mesh=Proj.gamePath(base_data->mesh_id);
  958. }
  959. if(mesh)for(int i=1; i<mesh->variations(); i++)mesh_variations.New().set(mesh->variationName(i), mesh->variationID(i));
  960. v_mesh_var.setData(mesh_variations, null, true);
  961. // for object editor refresh the gui mesh variation, because mesh is loaded after object
  962. if(!world)toGuiMeshVariation(p->mesh_variation_id);
  963. }
  964. void ParamEditor::objChanged(C UID *obj_id)
  965. {
  966. if(!obj_id || p->hasBase(*obj_id))toGui(); // there's no need to check if 'p' is the changed object, because 'p' will be synced in 'syncObj' with 'toGui' called there if needed
  967. }
  968. ParamEditor& ParamEditor::move(C Vec2 &delta)
  969. {
  970. ::EE::Region::move(delta);
  971. param_window.move(rect().rd()-param_window.rect().ru());
  972. return T;
  973. }
  974. void ParamEditor::paramWindowHidden() {}
  975. void ParamEditor::newSubObj(C UID &elm_obj_id)
  976. {
  977. if(world)
  978. {
  979. if(Selection.elms()==1)
  980. {
  981. setUndo();
  982. Selection[0].params.sub_objs.New().elm_obj_id=elm_obj_id;
  983. setChanged();
  984. }
  985. }else
  986. {
  987. setUndo("newSubObj");
  988. p->sub_objs.New().elm_obj_id=elm_obj_id;
  989. setChanged();
  990. }
  991. }
  992. void ParamEditor::removeSubObj(ParamWindow::SubObj &sub_obj)
  993. {
  994. if(world)
  995. {
  996. REPA(Selection)
  997. {
  998. Obj &obj=Selection[i];
  999. if(EditObject::SubObj *so=obj.params.findSubObj(sub_obj.id))if(!so->removed){obj.setUndo(); so->setRemoved(true); obj.setChanged();}
  1000. }
  1001. toGui();
  1002. }else
  1003. {
  1004. if(EditObject::SubObj *so=p->findSubObj(sub_obj.id))if(!so->removed){undos.set("delSubObj"); so->setRemoved(true); setChanged();}
  1005. }
  1006. }
  1007. bool ParamEditor::NewParam(ParamEditor &pe, EditObject &obj, cptr user)
  1008. {
  1009. obj.New().type=PARAM_FLT;
  1010. return true;
  1011. }
  1012. void ParamEditor::newParam()
  1013. {
  1014. if(world)
  1015. {
  1016. if(Selection.elms()==1)
  1017. {
  1018. setUndo();
  1019. NewParam(T, Selection[0].params);
  1020. setChanged();
  1021. }else
  1022. {
  1023. REPA(Selection)
  1024. {
  1025. Obj &obj=Selection[i];
  1026. REPA(obj.params){EditParam &param=obj.params[i]; if(param.type==PARAM_FLT && !param.name.is() && !param.removed)goto already_exists;} // don't add empty flt param if object already has it
  1027. obj.setUndo(); NewParam(T, obj.params); obj.setChanged();
  1028. already_exists:;
  1029. }
  1030. toGui();
  1031. }
  1032. }else
  1033. {
  1034. setUndo("newParam");
  1035. NewParam(T, *p);
  1036. setChanged();
  1037. }
  1038. multiFunc(NewParam);
  1039. }
  1040. bool ParamEditor::NewParam(ParamEditor &pe, EditObject &obj, C Param &param)
  1041. {
  1042. return obj.newParam(param.name, param.type, Proj);
  1043. }
  1044. void ParamEditor::newParam(ParamWindow::Param &src, bool refresh_gui) // override 'src' parameter (this is done only if the object has a base containing that parameter)
  1045. {
  1046. if(world)
  1047. {
  1048. REPA(Selection)
  1049. {
  1050. EditObjectPtr obj_class; // need to keep ptr here because we're storing pointer to its parameter in 'base_param'
  1051. Obj &obj=Selection[i];
  1052. C EditParam *base_param=obj.params.baseFindParam(src.src.name, src.src.type);
  1053. if(!base_param && (obj.params.flag&EditObject::OVR_TYPE))
  1054. if(obj_class=Proj.editPath(obj.params.type))
  1055. base_param=obj_class->EditParams::findParam(src.src.name, src.src.type);
  1056. if(base_param) // if base has that param
  1057. { // copy values from 'base_param' instead of 'src' in case object bases have different default value than 'src'
  1058. obj.setUndo();
  1059. EditParam *dest=obj.params.EditParams::findParam(base_param->id); // find existing param with same id
  1060. if(!dest){dest=&obj.params.New(); dest->id=base_param->id;}else if(dest->removed)dest->setRemoved(false);else continue; // if doesn't exist then create new and set id, if removed then un-remove, if exists then continue and don't override its value (this can happen if we're overriding value for multiple objects and some of which have this value already and some don't)
  1061. SCAST(Param, *dest)=*base_param; dest->nameTypeValueUTC(); // set values and update times
  1062. obj.setChanged();
  1063. }
  1064. }
  1065. }else
  1066. { // no need to check for base in this case
  1067. setUndo(&src);
  1068. EditParam *dest=p->EditParams::findParam(src.id); // find existing param with same id
  1069. if(!dest){dest=&p->New(); dest->id=src.id;}else if(dest->removed)dest->setRemoved(false);else return; // if doesn't exist then create new and set id, if removed then un-remove, if exists then continue and don't override its value (this can happen if we're overriding value for multiple objects and some of which have this value already and some don't)
  1070. SCAST(Param, *dest)=src.src; dest->nameTypeValueUTC(); // set values and update times
  1071. setChanged(false);
  1072. }
  1073. multiFunc(NewParam, src.src);
  1074. if(refresh_gui)toGui();
  1075. }
  1076. bool ParamEditor::RemoveParam(ParamEditor &pe, EditObject &obj, C Param &src)
  1077. {
  1078. bool changed=false; REPA(obj)
  1079. {
  1080. EditParam &param=obj[i]; if(ParamCompatible(param, src) && !param.removed){param.setRemoved(true); changed=true;}
  1081. }
  1082. return changed;
  1083. }
  1084. void ParamEditor::removeParam(ParamWindow::Param &src)
  1085. {
  1086. if(world)
  1087. {
  1088. REPA(Selection)
  1089. {
  1090. Obj &obj=Selection[i];
  1091. REPA(obj.params){EditParam &param=obj.params[i]; if(ParamCompatible(param, src.src) && !param.removed && (src.multi_obj ? true : param.id==src.id)){obj.setUndo(); param.setRemoved(true); obj.setChanged();}}
  1092. }
  1093. }else
  1094. {
  1095. setUndo(&src);
  1096. REPA(*p){EditParam &param=(*p)[i]; if(param.id==src.id && !param.removed)param.setRemoved(true);}
  1097. setChanged(false);
  1098. }
  1099. multiFunc(RemoveParam, src.src);
  1100. toGui();
  1101. }
  1102. bool ParamEditor::ClearValue(ParamEditor &pe, EditObject &obj, C Param &src)
  1103. {
  1104. bool changed=false; REPA(obj)
  1105. {
  1106. EditParam &param=obj[i]; if(ParamCompatible(param, src) && !param.removed){param.clearValue(); changed=true;}
  1107. }
  1108. return changed;
  1109. }
  1110. void ParamEditor::clearValue(ParamWindow::Param &src)
  1111. {
  1112. newParam(src, false); // don't refresh gui because it may make 'src' param invalid, and we will call it anyway later
  1113. if(world)
  1114. {
  1115. REPA(Selection)
  1116. {
  1117. Obj &obj=Selection[i];
  1118. REPA(obj.params){EditParam &param=obj.params[i]; if(ParamCompatible(param, src.src) && !param.removed && (src.multi_obj ? true : param.id==src.id)){obj.setUndo(); param.clearValue(); obj.setChanged();}}
  1119. }
  1120. }else
  1121. {
  1122. setUndo(&src);
  1123. REPA(*p){EditParam &param=(*p)[i]; if(param.id==src.id && !param.removed)param.clearValue();}
  1124. setChanged(false);
  1125. }
  1126. multiFunc(ClearValue, src.src);
  1127. toGui();
  1128. }
  1129. bool ParamEditor::SetType(ParamEditor &pe, EditObject &obj, C ParamWindow::Param &src)
  1130. {
  1131. bool changed =false;
  1132. PARAM_TYPE type =PARAM_TYPE(src.type());
  1133. Enum *enum_type=((type==PARAM_ENUM) ? Proj.getEnumFromName(src.type.text()) : null);
  1134. REPA(obj)
  1135. {
  1136. EditParam &param=obj[i]; if(ParamCompatible(param, src.src) && !param.removed){param.setType(type, enum_type); changed=true;}
  1137. }
  1138. return changed;
  1139. }
  1140. void ParamEditor::setType(ParamWindow::Param &src)
  1141. {
  1142. PARAM_TYPE type =PARAM_TYPE(src.type());
  1143. Enum *enum_type=((type==PARAM_ENUM) ? Proj.getEnumFromName(src.type.text()) : null);
  1144. if(world)
  1145. {
  1146. REPA(Selection)
  1147. {
  1148. Obj &obj=Selection[i];
  1149. REPA(obj.params){EditParam &param=obj.params[i]; if(ParamCompatible(param, src.src) && !param.removed && (src.multi_obj ? true : param.id==src.id)){obj.setUndo(); param.setType(type, enum_type); obj.setChanged();}}
  1150. }
  1151. }else
  1152. {
  1153. setUndo(&src);
  1154. REPA(*p){EditParam &param=(*p)[i]; if(param.id==src.id && !param.removed)param.setType(type, enum_type);}
  1155. setChanged(false);
  1156. }
  1157. multiFunc(SetType, src);
  1158. toGui(); // refresh because changing types may now use different gui objects, and different value
  1159. }
  1160. bool ParamEditor::SetName(ParamEditor &pe, EditObject &obj, C ParamWindow::Param &src)
  1161. {
  1162. bool changed=false;
  1163. REPA(obj)
  1164. {
  1165. EditParam &param=obj[i]; if(ParamCompatible(param, src.src) && !param.removed){param.setName(src.name()); changed=true;}
  1166. }
  1167. return changed;
  1168. }
  1169. void ParamEditor::setName(ParamWindow::Param &src)
  1170. {
  1171. if(world)
  1172. {
  1173. REPA(Selection)
  1174. {
  1175. Obj &obj=Selection[i];
  1176. REPA(obj.params){EditParam &param=obj.params[i]; if(ParamCompatible(param, src.src) && !param.removed && (src.multi_obj ? true : param.id==src.id)){obj.setUndo(); param.setName(src.name()); obj.setChanged();}}
  1177. }
  1178. src.src.name=src.name(); // modify here because we're not resetting gui (and after changing world objects because they check for src.src.name)
  1179. }else
  1180. {
  1181. setUndo(&src);
  1182. REPA(*p){EditParam &param=(*p)[i]; if(param.id==src.id && !param.removed)param.setName(src.name());}
  1183. src.src.name=src.name(); // modify here because we're not resetting gui
  1184. setChanged(false);
  1185. }
  1186. multiFunc(SetName, src);
  1187. }
  1188. bool ParamEditor::SetBool(ParamEditor &pe, EditObject &obj, C ParamWindow::Param &src)
  1189. {
  1190. bool changed=false; REPA(obj)
  1191. {
  1192. EditParam &param=obj[i]; if(ParamCompatible(param, src.src) && !param.removed){param.setBool(src.val_checkbox()); changed=true;}
  1193. }
  1194. return changed;
  1195. }
  1196. void ParamEditor::setBool(ParamWindow::Param &src)
  1197. {
  1198. newParam(src, false); // don't refresh gui because it may make 'src' param invalid, and we will call it anyway later
  1199. if(world)
  1200. {
  1201. REPA(Selection)
  1202. {
  1203. Obj &obj=Selection[i];
  1204. REPA(obj.params){EditParam &param=obj.params[i]; if(ParamCompatible(param, src.src) && !param.removed && (src.multi_obj ? true : param.id==src.id)){obj.setUndo(); param.setBool(src.val_checkbox()); obj.setChanged();}}
  1205. }
  1206. }else
  1207. {
  1208. setUndo(&src);
  1209. REPA(*p){EditParam &param=(*p)[i]; if(param.id==src.id && !param.removed)param.setBool(src.val_checkbox());}
  1210. setChanged(false);
  1211. }
  1212. multiFunc(SetBool, src);
  1213. toGui();
  1214. }
  1215. bool ParamEditor::SetEnum(ParamEditor &pe, EditObject &obj, C ParamWindow::Param &src)
  1216. {
  1217. bool force_enum=src.forceEnum();
  1218. int value =src.enumValue();
  1219. bool changed =false; REPA(obj)
  1220. {
  1221. EditParam &param=obj[i]; if(ParamCompatible(param, src.src) && !param.removed)
  1222. {
  1223. if(force_enum)param.setValue(value);else param.setValue(src.val_combobox.text()); changed=true;
  1224. }
  1225. }
  1226. return changed;
  1227. }
  1228. void ParamEditor::setEnum(ParamWindow::Param &src)
  1229. {
  1230. newParam(src, false); // don't refresh gui because it may make 'src' param invalid, and we will call it anyway later
  1231. bool force_enum=src.forceEnum();
  1232. int value =src.enumValue();
  1233. if(world)
  1234. {
  1235. REPA(Selection)
  1236. {
  1237. Obj &obj=Selection[i];
  1238. REPA(obj.params){EditParam &param=obj.params[i]; if(ParamCompatible(param, src.src) && !param.removed && (src.multi_obj ? true : param.id==src.id))
  1239. {
  1240. obj.setUndo(); if(force_enum)param.setValue(value);else param.setValue(src.val_combobox.text()); obj.setChanged();
  1241. }}
  1242. }
  1243. }else
  1244. {
  1245. setUndo(&src);
  1246. REPA(*p){EditParam &param=(*p)[i]; if(param.id==src.id && !param.removed)
  1247. {
  1248. if(force_enum)param.setValue(value);else param.setValue(src.val_combobox.text());
  1249. }}
  1250. setChanged(false);
  1251. }
  1252. multiFunc(SetEnum, src);
  1253. toGui();
  1254. }
  1255. bool ParamEditor::SetColor(ParamEditor &pe, EditObject &obj, C ParamWindow::Param &src)
  1256. {
  1257. bool changed=false; REPA(obj)
  1258. {
  1259. EditParam &param=obj[i]; if(ParamCompatible(param, src.src) && !param.removed){param.setColor(src.cp()); changed=true;}
  1260. }
  1261. return changed;
  1262. }
  1263. void ParamEditor::setColor(ParamWindow::Param &src) // don't refresh gui because it will delete color pickers
  1264. {
  1265. src.src.value.c=src.cp(); // modify here because we're not resetting gui
  1266. src.ovr.set(true, QUIET);
  1267. src.cur=true;
  1268. src.setSkin();
  1269. if(world)
  1270. {
  1271. REPA(Selection)
  1272. {
  1273. Obj &obj=Selection[i];
  1274. REPA(obj.params){EditParam &param=obj.params[i]; if(ParamCompatible(param, src.src) && !param.removed && (src.multi_obj ? true : param.id==src.id)){obj.setUndo(); param.setColor(src.cp()); obj.setChanged();}}
  1275. }
  1276. }else
  1277. {
  1278. setUndo(&src);
  1279. REPA(*p){EditParam &param=(*p)[i]; if(param.id==src.id && !param.removed)param.setColor(src.cp());}
  1280. setChanged(false);
  1281. }
  1282. multiFunc(SetColor, src);
  1283. }
  1284. bool ParamEditor::SetText(ParamEditor &pe, EditObject &obj, C ParamIDs &src)
  1285. {
  1286. bool changed=false; REPA(obj)
  1287. {
  1288. EditParam &param=obj[i]; if(ParamCompatible(param, src) && !param.removed)
  1289. {
  1290. if(ParamTypeID(src.type))param.setAsIDArray(ConstCast(src.ids));else param.setValue(src.value.s); changed=true;
  1291. }
  1292. }
  1293. return changed;
  1294. }
  1295. void ParamEditor::setText(ParamWindow::Param &src) // don't refresh gui because it will lose textline keyboard focus and cursor/selection
  1296. {
  1297. newParam(src, false); // don't refresh gui because it may make 'src' param invalid
  1298. src.ovr.set(true, QUIET);
  1299. src.cur=true;
  1300. src.setSkin();
  1301. ParamIDs data; data.name=src.src.name; data.type=src.src.type;
  1302. if(ParamTypeID(data.type))
  1303. {
  1304. if(src.val_textline().is())
  1305. {
  1306. Memt<Str> splits; Split(splits, src.val_textline(), '|');
  1307. FREPA(splits)
  1308. {
  1309. UID id;
  1310. if(!id.fromText(splits[i]))
  1311. if(Elm *elm=Proj.findElm(splits[i]))
  1312. {
  1313. if(ElmPublish(elm->type))id=elm->id;else continue;
  1314. }else id.zero();
  1315. data.ids.add(id);
  1316. }
  1317. }
  1318. src.src.setAsIDArray(data.ids); // modify here because we're not resetting gui
  1319. }else
  1320. {
  1321. data.value.s=src.val_textline();
  1322. src.src.setValue(data.value.s); // modify here because we're not resetting gui
  1323. }
  1324. if(world)
  1325. {
  1326. REPA(Selection)
  1327. {
  1328. Obj &obj=Selection[i];
  1329. REPA(obj.params){EditParam &param=obj.params[i]; if(ParamCompatible(param, src.src) && !param.removed && (src.multi_obj ? true : param.id==src.id)){obj.setUndo(); if(ParamTypeID(param.type))param.setAsIDArray(data.ids);else param.setValue(data.value.s); obj.setChanged();}}
  1330. }
  1331. }else
  1332. {
  1333. setUndo(&src);
  1334. REPA(*p){EditParam &param=(*p)[i]; if(param.id==src.id && !param.removed)if(ParamTypeID(param.type))param.setAsIDArray(data.ids);else param.setValue(data.value.s);}
  1335. setChanged(false);
  1336. }
  1337. multiFunc(SetText, data);
  1338. }
  1339. bool ParamEditor::SetBase(ParamEditor &pe, EditObject &obj, C EditObjectPtr &base)
  1340. {
  1341. obj.setBase(base, Proj.edit_path).setAccess(false); // use access from base
  1342. return true;
  1343. }
  1344. void ParamEditor::setBase(C EditObjectPtr &base)
  1345. {
  1346. if(world)
  1347. {
  1348. setUndo();
  1349. REPA(Selection)
  1350. {
  1351. Obj &obj=Selection[i]; TerrainObj terrain=obj.terrainObj(); PhysPath phys_path=obj.physPath(); uint mesh_variation_id=obj.params.mesh_variation_id;
  1352. obj.params.setBase(base, Proj.edit_path); // don't modify access because in WorldEditor we can modify it manually
  1353. obj.setMeshPhys();
  1354. obj.setChanged(); // call before 'setChangedEmbed'
  1355. if(obj.terrainObj()!=terrain || obj.params.mesh_variation_id!=mesh_variation_id)obj.setChangedEmbed(); // call after 'setChanged' and before 'setChangedPaths'
  1356. if(obj.physPath() !=phys_path )obj.setChangedPaths(); // call after 'setChangedEmbed'
  1357. }
  1358. ObjList.setChanged();
  1359. }else
  1360. {
  1361. setUndo("setBase");
  1362. SetBase(T, *p, base);
  1363. setChanged(false);
  1364. }
  1365. multiFunc(SetBase, base);
  1366. toGui();
  1367. }
  1368. bool ParamEditor::SetID(ParamEditor &pe, EditObject &obj, C ParamIDs &src)
  1369. {
  1370. bool changed=false; REPA(obj)
  1371. {
  1372. EditParam &param=obj[i]; if(ParamCompatible(param, src) && !param.removed){param.setAsIDArray(ConstCast(src.ids)); changed=true;}
  1373. }
  1374. return changed;
  1375. }
  1376. bool ParamEditor::IncludeID(ParamEditor &pe, EditObject &obj, C ParamIDs &src)
  1377. {
  1378. bool changed=false; REPA(obj)
  1379. {
  1380. EditParam &param=obj[i]; if(ParamCompatible(param, src) && !param.removed){param.includeAsIDArray(ConstCast(src.ids)); changed=true;}
  1381. }
  1382. return changed;
  1383. }
  1384. void ParamEditor::drag(Memc<UID> &elms, GuiObj* &obj, C Vec2 &screen_pos)
  1385. {
  1386. if(elms.elms() && (contains(obj) || param_window.contains(obj)))
  1387. {
  1388. if(param_window.contains(obj)) // set project element as param id or sub object
  1389. {
  1390. if(ParamWindow::Param *src=findParam(obj))
  1391. {
  1392. bool include=Kb.ctrl(); // if Ctrl pressed then include ID's
  1393. Memt<UID> publishable; FREPA(elms)if(Elm *elm=Proj.findElm(elms[i]))if(ElmPublish(elm->type))publishable.add(elm->id);
  1394. if(publishable.elms())
  1395. if(!src->base || ParamTypeID(src->src.type)) // only if there's no base, or it's of PARAM_ID* type
  1396. {
  1397. newParam(*src, false); // don't refresh gui because it may make 'src' param invalid, and we will call it anyway later
  1398. if(world)
  1399. {
  1400. REPA(Selection)
  1401. {
  1402. Obj &obj=Selection[i];
  1403. REPA(obj.params){EditParam &param=obj.params[i]; if(ParamCompatible(param, src->src) && !param.removed && (src->multi_obj ? true : param.id==src->id)){obj.setUndo(); if(include)param.includeAsIDArray(publishable);else param.setAsIDArray(publishable); obj.setChanged();}}
  1404. }
  1405. }else
  1406. {
  1407. REPA(*p){EditParam &param=(*p)[i]; if(param.id==src->id && !param.removed){setUndo(src); if(include)param.includeAsIDArray(publishable);else param.setAsIDArray(publishable); setChanged(false); break;}}
  1408. }
  1409. ParamIDs data; data.name=src->src.name; data.type=src->src.type; data.ids=publishable;
  1410. multiFunc(include ? IncludeID : SetID, data);
  1411. toGui();
  1412. }
  1413. }else
  1414. REPA(elms)if(Elm *elm=Proj.findElm(elms[i], ELM_OBJ))
  1415. {
  1416. newSubObj(elm->id);
  1417. break; // allow adding only one object at a time, in case user accidentally dragged entire project
  1418. }
  1419. }else // set object base
  1420. {
  1421. //if(obj==&t_obj || v_class.contains(obj)) any place is good to set the base
  1422. {
  1423. undos.set("setBase");
  1424. FREPA(elms)if(Elm *elm=Proj.findElm(elms[i]))if(elm->type==ELM_OBJ || (elm->type==ELM_OBJ_CLASS && !world)) // for world objects we can't set obj.base as ELM_OBJ_CLASS
  1425. {
  1426. EditObjectPtr base=Proj.editPath(elm->id); setBase(base);
  1427. break;
  1428. }
  1429. ObjList.setChanged();
  1430. }
  1431. }
  1432. obj=null; // clear in case got deleted, this is very important, because if the obj was pointing to a parameter that due to 'toGui' got deleted, then 'obj' would become an invalid pointer and accessing it later could cause a crash
  1433. }
  1434. }
  1435. void ObjClassEditor::Locate(ObjClassEditor &editor) {Proj.elmLocate(editor.elm_id);}
  1436. void ObjClassEditor::paramWindowHidden(){set(null);}
  1437. void ObjClassEditor::create()
  1438. {
  1439. ::ParamEditor::create(*Gui.desktop(), true);
  1440. param_window+=locate.create(Rect_LU(redo.rect().ru()+Vec2(0.01f, 0), 0.14f, 0.05f), "Locate").func(Locate, T).focusable(false).desc("Locate this element in the Project");
  1441. }
  1442. void ObjClassEditor::flush()
  1443. {
  1444. if(elm && changed)
  1445. {
  1446. if(ElmObjClass *data=elm->objClassData())
  1447. {
  1448. OBJ_PATH path=data->pathSelf();
  1449. data->newVer(); data->from(*p); // modify just before saving/sending in case we've received data from server after edit
  1450. if(path!=data->pathSelf())Proj.verifyPathsForObjClass(elm->id); // if paths were changed
  1451. }
  1452. Save(*p, Proj.editPath(elm->id));
  1453. Object obj; p->copyTo(obj, Proj, false, null, null); Save(obj, Proj.gamePath(elm->id)); Proj.savedGame(*elm);
  1454. Server.setElmLong(elm->id);
  1455. Proj.objChanged(*elm); // call 'Proj.objChanged' instead of 'Proj.elmChanged' so 'ObjClassEdit.elmChanged' won't get called
  1456. }
  1457. changed=false;
  1458. }
  1459. void ObjClassEditor::set(Elm *elm)
  1460. {
  1461. if(elm && elm->type!=ELM_OBJ_CLASS)elm=null;
  1462. if(T.elm!=elm)
  1463. {
  1464. rename_window.hide();
  1465. flush();
  1466. undos.del(); undoVis();
  1467. T.elm =elm;
  1468. T.elm_id=(elm ? elm->id : UIDZero);
  1469. if(elm)p->load(Proj.editPath(elm->id));else p->del();
  1470. toGui();
  1471. Proj.refresh(false, false);
  1472. param_window.visible(T.elm!=null).moveToTop();
  1473. }
  1474. }
  1475. void ObjClassEditor::activate(Elm *elm) {set(elm); if(T.elm)::EE::GuiObj::activate();}
  1476. void ObjClassEditor::toggle(Elm *elm) {if(elm==T.elm)elm=null; set(elm);}
  1477. void ObjClassEditor::elmChanged(C UID &elm_id)
  1478. {
  1479. if(elm_id==T.elm_id && elm_id.valid())
  1480. {
  1481. undos.set(null, true);
  1482. EditObject temp; if(temp.load(Proj.editPath(elm_id)))if(p->sync(temp, Proj.edit_path))toGui();
  1483. }
  1484. }
  1485. void ObjClassEditor::erasing(C UID &elm_id) {if(elm && elm->id==elm_id)set(null);}
  1486. ParamEditor::ParamEditor() : param_window(T), rename_window(T), world(false), p(&temp_p), changed(false), undos(true, this) {}
  1487. ParamEditor::ParamWindow::EditParamEx::EditParamEx() : cur(false), base(false), base_ovr(false), gui(null) {}
  1488. ParamEditor::ParamWindow::Param::Param() : cur(false), base(false), base_ovr(false), valid_type(true), valid_value(true), multi_obj(false), multi_cur(false), multi_base(false), multi_val(false), priority(0), id(UIDZero), pe(null), owner(null), min_use(false), max_use(false), min_value(0 ), max_value(0), mouse_edit_speed(1), custom_mouse_edit_speed(0), mouse_edit_value(0), gui(null), mouse_edit_mode(PROP_MOUSE_EDIT_LINEAR) {}
  1489. ParamEditor::ParamWindow::Param::GuiColor::GuiColor() : alpha(255) {}
  1490. ParamEditor::ParamWindow::SubObj::SubObj() : id(UIDZero), pe(null) {}
  1491. ParamEditor::MeshVariation::MeshVariation() : id(0) {}
  1492. ParamEditor::AccessType::AccessType() : type(UIDZero) {}
  1493. ObjClassEditor::ObjClassEditor() : elm(null), elm_id(UIDZero) {}
  1494. /******************************************************************************/