Map.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122
  1. #include "qedefs.h"
  2. id map_i;
  3. @implementation Map
  4. /*
  5. ===============================================================================
  6. FILE METHODS
  7. ===============================================================================
  8. */
  9. - init
  10. {
  11. [super init];
  12. map_i = self;
  13. minz = 0;
  14. maxz = 80;
  15. oldselection = [[List alloc] init];
  16. return self;
  17. }
  18. - saveSelected
  19. {
  20. int i, c;
  21. id o, w;
  22. [oldselection empty];
  23. w = [self objectAt: 0];
  24. c = [w count];
  25. sb_newowner = oldselection;
  26. for (i=0 ; i<c ; i++)
  27. {
  28. o = [w objectAt: 0];
  29. if ([o selected])
  30. [o moveToEntity];
  31. else
  32. {
  33. [w removeObjectAt: 0];
  34. [o free];
  35. }
  36. }
  37. c = [self count];
  38. for (i=0 ; i<c ; i++)
  39. {
  40. o = [self objectAt: 0];
  41. [self removeObjectAt: 0];
  42. [o freeObjects];
  43. [o free];
  44. }
  45. return self;
  46. }
  47. - addSelected
  48. {
  49. int i, c;
  50. id n, w;
  51. c = [oldselection count];
  52. w = [self objectAt: 0]; // world object
  53. sb_newowner = w;
  54. for (i=0 ; i<c ; i++)
  55. {
  56. n = [oldselection objectAt:i];
  57. [n moveToEntity];
  58. i--;
  59. c--;
  60. }
  61. [oldselection empty];
  62. return self;
  63. }
  64. - newMap
  65. {
  66. id ent;
  67. [self saveSelected];
  68. ent = [[Entity alloc] initClass: "worldspawn"];
  69. [self addObject: ent];
  70. currentEntity = NULL;
  71. [self setCurrentEntity: ent];
  72. [self addSelected];
  73. return self;
  74. }
  75. - currentEntity
  76. {
  77. return currentEntity;
  78. }
  79. - setCurrentEntity: ent
  80. {
  81. id old;
  82. old = currentEntity;
  83. currentEntity = ent;
  84. if (old != ent)
  85. {
  86. [things_i newCurrentEntity]; // update inspector
  87. [inspcontrol_i changeInspectorTo:i_things];
  88. }
  89. return self;
  90. }
  91. - (float)currentMinZ
  92. {
  93. float grid;
  94. grid = [xyview_i gridsize];
  95. minz = grid * rint(minz/grid);
  96. return minz;
  97. }
  98. - setCurrentMinZ: (float)m
  99. {
  100. if (m > -2048)
  101. minz = m;
  102. return self;
  103. }
  104. - (float)currentMaxZ
  105. {
  106. float grid;
  107. [self currentMinZ]; // grid align
  108. grid = [xyview_i gridsize];
  109. maxz = grid * rint(maxz/grid);
  110. if (maxz <= minz)
  111. maxz = minz + grid;
  112. return maxz;
  113. }
  114. - setCurrentMaxZ: (float)m
  115. {
  116. if (m < 2048)
  117. maxz = m;
  118. return self;
  119. }
  120. - removeObject: o
  121. {
  122. o = [super removeObject: o];
  123. if (o == currentEntity)
  124. { // select the world
  125. [self setCurrentEntity: [self objectAt: 0]];
  126. }
  127. return o;
  128. }
  129. - writeStats
  130. {
  131. FILE *f;
  132. extern int c_updateall;
  133. struct timeval tp;
  134. struct timezone tzp;
  135. gettimeofday(&tp, &tzp);
  136. f = fopen (FN_DEVLOG, "a");
  137. fprintf (f,"%i %i\n", (int)tp.tv_sec, c_updateall);
  138. c_updateall = 0;
  139. fclose (f);
  140. return self;
  141. }
  142. - (int)numSelected
  143. {
  144. int i, c;
  145. int num;
  146. num = 0;
  147. c = [currentEntity count];
  148. for (i=0 ; i<c ; i++)
  149. if ( [[currentEntity objectAt: i] selected] )
  150. num++;
  151. return num;
  152. }
  153. - selectedBrush
  154. {
  155. int i, c;
  156. int num;
  157. num = 0;
  158. c = [currentEntity count];
  159. for (i=0 ; i<c ; i++)
  160. if ( [[currentEntity objectAt: i] selected] )
  161. return [currentEntity objectAt: i];
  162. return nil;
  163. }
  164. /*
  165. =================
  166. readMapFile
  167. =================
  168. */
  169. - readMapFile: (char *)fname
  170. {
  171. char *dat, *cl;
  172. id new;
  173. id ent;
  174. int i, c;
  175. vec3_t org;
  176. float angle;
  177. [self saveSelected];
  178. qprintf ("loading %s\n", fname);
  179. LoadFile (fname, (void **)&dat);
  180. StartTokenParsing (dat);
  181. do
  182. {
  183. new = [[Entity alloc] initFromTokens];
  184. if (!new)
  185. break;
  186. [self addObject: new];
  187. } while (1);
  188. free (dat);
  189. [self setCurrentEntity: [self objectAt: 0]];
  190. [self addSelected];
  191. // load the apropriate texture wad
  192. dat = [currentEntity valueForQKey: "wad"];
  193. if (dat && dat[0])
  194. {
  195. if (dat[0] == '/') // remove old style fullpaths
  196. [currentEntity removeKeyPair: "wad"];
  197. else
  198. {
  199. if (strcmp ([texturepalette_i currentWad], dat) )
  200. [project_i setTextureWad: dat];
  201. }
  202. }
  203. // center the camera and XY view on the playerstart
  204. c = [self count];
  205. for (i=1 ; i<c ; i++)
  206. {
  207. ent = [self objectAt: i];
  208. cl = [ent valueForQKey: "classname"];
  209. if (cl && !strcasecmp (cl,"info_player_start"))
  210. {
  211. angle = atof( [ent valueForQKey: "angle"] );
  212. angle = angle/180*M_PI;
  213. [ent getVector: org forKey: "origin"];
  214. [cameraview_i setOrigin: org angle:angle];
  215. [xyview_i centerOn: org];
  216. break;
  217. }
  218. }
  219. return self;
  220. }
  221. /*
  222. =================
  223. writeMapFile
  224. =================
  225. */
  226. - writeMapFile: (char *)fname useRegion: (BOOL)reg
  227. {
  228. FILE *f;
  229. int i;
  230. qprintf ("writeMapFile: %s", fname);
  231. f = fopen (fname,"w");
  232. if (!f)
  233. Error ("couldn't write %s", fname);
  234. for (i=0 ; i<numElements ; i++)
  235. [[self objectAt: i] writeToFILE: f region: reg];
  236. fclose (f);
  237. return self;
  238. }
  239. /*
  240. ==============================================================================
  241. DRAWING
  242. ==============================================================================
  243. */
  244. - ZDrawSelf
  245. {
  246. int i, count;
  247. count = [self count];
  248. for (i=0 ; i<count ; i++)
  249. [[self objectAt: i] ZDrawSelf];
  250. return self;
  251. }
  252. - RenderSelf: (void (*) (face_t *))callback
  253. {
  254. int i, count;
  255. count = [self count];
  256. for (i=0 ; i<count ; i++)
  257. [[self objectAt: i] RenderSelf: callback];
  258. return self;
  259. }
  260. //============================================================================
  261. /*
  262. ===================
  263. entityConnect
  264. A command-shift-click on an entity while an entity is selected will
  265. make a target connection from the original entity.
  266. ===================
  267. */
  268. - entityConnect: (vec3_t)p1 : (vec3_t)p2
  269. {
  270. id oldent, ent;
  271. oldent = [self currentEntity];
  272. if (oldent == [self objectAt: 0])
  273. {
  274. qprintf ("Must have a non-world entity selected to connect");
  275. return self;
  276. }
  277. [self selectRay: p1 : p2 : YES];
  278. ent = [self currentEntity];
  279. if (ent == oldent)
  280. {
  281. qprintf ("Must click on a different entity to connect");
  282. return self;
  283. }
  284. if (ent == [self objectAt: 0])
  285. {
  286. qprintf ("Must click on a non-world entity to connect");
  287. return self;
  288. }
  289. [oldent setKey:"target" toValue: [ent targetname]];
  290. [quakeed_i updateAll];
  291. return self;
  292. }
  293. /*
  294. =================
  295. selectRay
  296. If ef is true, any entity brush along the ray will be selected in preference
  297. to intervening world brushes
  298. =================
  299. */
  300. - selectRay: (vec3_t)p1 : (vec3_t)p2 : (BOOL)ef
  301. {
  302. int i, j, c, c2;
  303. id ent, bestent;
  304. id brush, bestbrush;
  305. int face, bestface;
  306. float time, besttime;
  307. texturedef_t *td;
  308. bestent = nil;
  309. bestface = -1;
  310. bestbrush = nil;
  311. besttime = 99999;
  312. c = [self count];
  313. for (i=c-1 ; i>=0 ; i--)
  314. {
  315. ent = [self objectAt: i];
  316. c2 = [ent count];
  317. for (j=0 ; j<c2 ; j++)
  318. {
  319. brush = [ent objectAt: j];
  320. [brush hitByRay: p1 : p2 : &time : &face];
  321. if (time < 0 || time >besttime)
  322. continue;
  323. bestent = ent;
  324. besttime = time;
  325. bestbrush = brush;
  326. bestface = face;
  327. }
  328. if (i == 1 && ef && bestbrush)
  329. break; // found an entity, so don't check the world
  330. }
  331. if (besttime == 99999)
  332. {
  333. qprintf ("trace missed");
  334. return self;
  335. }
  336. if ( [bestbrush regioned] )
  337. {
  338. qprintf ("WANRING: clicked on regioned brush");
  339. return self;
  340. }
  341. if (bestent != currentEntity)
  342. {
  343. [self makeSelectedPerform: @selector(deselect)];
  344. [self setCurrentEntity: bestent];
  345. }
  346. [quakeed_i disableFlushWindow];
  347. if ( ![bestbrush selected] )
  348. {
  349. if ( [map_i numSelected] == 0)
  350. { // don't grab texture if others are selected
  351. td = [bestbrush texturedefForFace: bestface];
  352. [texturepalette_i setTextureDef: td];
  353. }
  354. [bestbrush setSelected: YES];
  355. qprintf ("selected entity %i brush %i face %i", [self indexOf:bestent], [bestent indexOf: bestbrush], bestface);
  356. }
  357. else
  358. {
  359. [bestbrush setSelected: NO];
  360. qprintf ("deselected entity %i brush %i face %i", [self indexOf:bestent], [bestent indexOf: bestbrush], bestface);
  361. }
  362. [quakeed_i reenableFlushWindow];
  363. [quakeed_i updateAll];
  364. return self;
  365. }
  366. /*
  367. =================
  368. grabRay
  369. only checks the selected brushes
  370. Returns the brush hit, or nil if missed.
  371. =================
  372. */
  373. - grabRay: (vec3_t)p1 : (vec3_t)p2
  374. {
  375. int i, j, c, c2;
  376. id ent;
  377. id brush, bestbrush;
  378. int face;
  379. float time, besttime;
  380. bestbrush = nil;
  381. besttime = 99999;
  382. c = [self count];
  383. for (i=0 ; i<c ; i++)
  384. {
  385. ent = [self objectAt: i];
  386. c2 = [ent count];
  387. for (j=0 ; j<c2 ; j++)
  388. {
  389. brush = [ent objectAt: j];
  390. if (![brush selected])
  391. continue;
  392. [brush hitByRay: p1 : p2 : &time : &face];
  393. if (time < 0 || time >besttime)
  394. continue;
  395. besttime = time;
  396. bestbrush = brush;
  397. }
  398. }
  399. if (besttime == 99999)
  400. return nil;
  401. return bestbrush;
  402. }
  403. /*
  404. =================
  405. getTextureRay
  406. =================
  407. */
  408. - getTextureRay: (vec3_t)p1 : (vec3_t)p2
  409. {
  410. int i, j, c, c2;
  411. id ent, bestent;
  412. id brush, bestbrush;
  413. int face, bestface;
  414. float time, besttime;
  415. texturedef_t *td;
  416. vec3_t mins, maxs;
  417. bestbrush = nil;
  418. bestent = nil;
  419. besttime = 99999;
  420. bestface = -1;
  421. c = [self count];
  422. for (i=0 ; i<c ; i++)
  423. {
  424. ent = [self objectAt: i];
  425. c2 = [ent count];
  426. for (j=0 ; j<c2 ; j++)
  427. {
  428. brush = [ent objectAt: j];
  429. [brush hitByRay: p1 : p2 : &time : &face];
  430. if (time < 0 || time >besttime)
  431. continue;
  432. bestent = ent;
  433. bestface = face;
  434. besttime = time;
  435. bestbrush = brush;
  436. }
  437. }
  438. if (besttime == 99999)
  439. return nil;
  440. if ( ![bestent modifiable])
  441. {
  442. qprintf ("can't modify spawned entities");
  443. return self;
  444. }
  445. td = [bestbrush texturedefForFace: bestface];
  446. [texturepalette_i setTextureDef: td];
  447. qprintf ("grabbed texturedef and sizes");
  448. [bestbrush getMins: mins maxs: maxs];
  449. minz = mins[2];
  450. maxz = maxs[2];
  451. return bestbrush;
  452. }
  453. /*
  454. =================
  455. setTextureRay
  456. =================
  457. */
  458. - setTextureRay: (vec3_t)p1 : (vec3_t)p2 : (BOOL)allsides;
  459. {
  460. int i, j, c, c2;
  461. id ent, bestent;
  462. id brush, bestbrush;
  463. int face, bestface;
  464. float time, besttime;
  465. texturedef_t td;
  466. bestent = nil;
  467. bestface = -1;
  468. bestbrush = nil;
  469. besttime = 99999;
  470. c = [self count];
  471. for (i=0 ; i<c ; i++)
  472. {
  473. ent = [self objectAt: i];
  474. c2 = [ent count];
  475. for (j=0 ; j<c2 ; j++)
  476. {
  477. brush = [ent objectAt: j];
  478. [brush hitByRay: p1 : p2 : &time : &face];
  479. if (time < 0 || time >besttime)
  480. continue;
  481. bestent = ent;
  482. besttime = time;
  483. bestbrush = brush;
  484. bestface = face;
  485. }
  486. }
  487. if (besttime == 99999)
  488. {
  489. qprintf ("trace missed");
  490. return self;
  491. }
  492. if ( ![bestent modifiable])
  493. {
  494. qprintf ("can't modify spawned entities");
  495. return self;
  496. }
  497. if ( [bestbrush regioned] )
  498. {
  499. qprintf ("WANRING: clicked on regioned brush");
  500. return self;
  501. }
  502. [texturepalette_i getTextureDef: &td];
  503. [quakeed_i disableFlushWindow];
  504. if (allsides)
  505. {
  506. [bestbrush setTexturedef: &td];
  507. qprintf ("textured entity %i brush %i", [self indexOf:bestent], [bestent indexOf: bestbrush]);
  508. }
  509. else
  510. {
  511. [bestbrush setTexturedef: &td forFace: bestface];
  512. qprintf ("deselected entity %i brush %i face %i", [self indexOf:bestent], [bestent indexOf: bestbrush], bestface);
  513. }
  514. [quakeed_i reenableFlushWindow];
  515. [quakeed_i updateAll];
  516. return self;
  517. }
  518. /*
  519. ==============================================================================
  520. OPERATIONS ON SELECTIONS
  521. ==============================================================================
  522. */
  523. - makeSelectedPerform: (SEL)sel
  524. {
  525. int i,j, c, c2;
  526. id ent, brush;
  527. int total;
  528. total = 0;
  529. c = [self count];
  530. for (i=c-1 ; i>=0 ; i--)
  531. {
  532. ent = [self objectAt: i];
  533. c2 = [ent count];
  534. for (j = c2-1 ; j >=0 ; j--)
  535. {
  536. brush = [ent objectAt: j];
  537. if (! [brush selected] )
  538. continue;
  539. if ([brush regioned])
  540. continue;
  541. total++;
  542. [brush perform:sel];
  543. }
  544. }
  545. // if (!total)
  546. // qprintf ("nothing selected");
  547. return self;
  548. }
  549. - makeUnselectedPerform: (SEL)sel
  550. {
  551. int i,j, c, c2;
  552. id ent, brush;
  553. c = [self count];
  554. for (i=c-1 ; i>=0 ; i--)
  555. {
  556. ent = [self objectAt: i];
  557. c2 = [ent count];
  558. for (j = c2-1 ; j >=0 ; j--)
  559. {
  560. brush = [ent objectAt: j];
  561. if ( [brush selected] )
  562. continue;
  563. if ([brush regioned])
  564. continue;
  565. [brush perform:sel];
  566. }
  567. }
  568. return self;
  569. }
  570. - makeAllPerform: (SEL)sel
  571. {
  572. int i,j, c, c2;
  573. id ent, brush;
  574. c = [self count];
  575. for (i=c-1 ; i>=0 ; i--)
  576. {
  577. ent = [self objectAt: i];
  578. c2 = [ent count];
  579. for (j = c2-1 ; j >=0 ; j--)
  580. {
  581. brush = [ent objectAt: j];
  582. if ([brush regioned])
  583. continue;
  584. [brush perform:sel];
  585. }
  586. }
  587. return self;
  588. }
  589. - makeGlobalPerform: (SEL)sel // in and out of region
  590. {
  591. int i,j, c, c2;
  592. id ent, brush;
  593. c = [self count];
  594. for (i=c-1 ; i>=0 ; i--)
  595. {
  596. ent = [self objectAt: i];
  597. c2 = [ent count];
  598. for (j = c2-1 ; j >=0 ; j--)
  599. {
  600. brush = [ent objectAt: j];
  601. [brush perform:sel];
  602. }
  603. }
  604. return self;
  605. }
  606. void sel_identity (void)
  607. {
  608. sel_x[0]=1; sel_x[1]=0; sel_x[2]=0;
  609. sel_y[0]=0; sel_y[1]=1; sel_y[2]=0;
  610. sel_z[0]=0; sel_z[1]=0; sel_z[2]=1;
  611. }
  612. - transformSelection
  613. {
  614. if ( ![currentEntity modifiable])
  615. {
  616. qprintf ("can't modify spawned entities");
  617. return self;
  618. }
  619. // find an origin to apply the transformation to
  620. sb_mins[0] = sb_mins[1] = sb_mins[2] = 99999;
  621. sb_maxs[0] = sb_maxs[1] = sb_maxs[2] = -99999;
  622. [self makeSelectedPerform: @selector(addToBBox)];
  623. sel_org[0] = [xyview_i snapToGrid: (sb_mins[0] + sb_maxs[0])/2];
  624. sel_org[1] = [xyview_i snapToGrid: (sb_mins[1] + sb_maxs[1])/2];
  625. sel_org[2] = [xyview_i snapToGrid: (sb_mins[2] + sb_maxs[2])/2];
  626. // do it!
  627. [self makeSelectedPerform: @selector(transform)];
  628. [quakeed_i updateAll];
  629. return self;
  630. }
  631. void swapvectors (vec3_t a, vec3_t b)
  632. {
  633. vec3_t temp;
  634. VectorCopy (a, temp);
  635. VectorCopy (b, a);
  636. VectorSubtract (vec3_origin, temp, b);
  637. }
  638. /*
  639. ===============================================================================
  640. UI operations
  641. ===============================================================================
  642. */
  643. - rotate_x: sender
  644. {
  645. sel_identity ();
  646. swapvectors(sel_y, sel_z);
  647. [self transformSelection];
  648. return self;
  649. }
  650. - rotate_y: sender
  651. {
  652. sel_identity ();
  653. swapvectors(sel_x, sel_z);
  654. [self transformSelection];
  655. return self;
  656. }
  657. - rotate_z: sender
  658. {
  659. sel_identity ();
  660. swapvectors(sel_x, sel_y);
  661. [self transformSelection];
  662. return self;
  663. }
  664. - flip_x: sender
  665. {
  666. sel_identity ();
  667. sel_x[0] = -1;
  668. [self transformSelection];
  669. [map_i makeSelectedPerform: @selector(flipNormals)];
  670. return self;
  671. }
  672. - flip_y: sender
  673. {
  674. sel_identity ();
  675. sel_y[1] = -1;
  676. [self transformSelection];
  677. [map_i makeSelectedPerform: @selector(flipNormals)];
  678. return self;
  679. }
  680. - flip_z: sender
  681. {
  682. sel_identity ();
  683. sel_z[2] = -1;
  684. [self transformSelection];
  685. [map_i makeSelectedPerform: @selector(flipNormals)];
  686. return self;
  687. }
  688. - cloneSelection: sender
  689. {
  690. int i,j , c, originalElements;
  691. id o, b;
  692. id new;
  693. sb_translate[0] = sb_translate[1] = [xyview_i gridsize];
  694. sb_translate[2] = 0;
  695. // copy individual brushes in the world entity
  696. o = [self objectAt: 0];
  697. c = [o count];
  698. for (i=0 ; i<c ; i++)
  699. {
  700. b = [o objectAt: i];
  701. if (![b selected])
  702. continue;
  703. // copy the brush, then translate the original
  704. new = [b copy];
  705. [new setSelected: YES];
  706. [new translate];
  707. [b setSelected: NO];
  708. [o addObject: new];
  709. }
  710. // copy entire entities otherwise
  711. originalElements = numElements; // don't copy the new ones
  712. for (i=1 ; i<originalElements ; i++)
  713. {
  714. o = [self objectAt: i];
  715. if (![[o objectAt: 0] selected])
  716. continue;
  717. new = [o copy];
  718. [self addObject: new];
  719. c = [o count];
  720. for (j=0 ; j<c ; j++)
  721. [[o objectAt: j] setSelected: NO];
  722. c = [new count];
  723. for (j=0 ; j<c ; j++)
  724. {
  725. b = [new objectAt: j];
  726. [b translate];
  727. [b setSelected: YES];
  728. }
  729. }
  730. [quakeed_i updateAll];
  731. return self;
  732. }
  733. - selectCompleteEntity: sender
  734. {
  735. id o;
  736. int i, c;
  737. o = [self selectedBrush];
  738. if (!o)
  739. {
  740. qprintf ("nothing selected");
  741. return self;
  742. }
  743. o = [o parent];
  744. c = [o count];
  745. for (i=0 ; i<c ; i++)
  746. [[o objectAt: i] setSelected: YES];
  747. qprintf ("%i brushes selected", c);
  748. [quakeed_i updateAll];
  749. return self;
  750. }
  751. - makeEntity: sender
  752. {
  753. if (currentEntity != [self objectAt: 0])
  754. {
  755. qprintf ("ERROR: can't makeEntity inside an entity");
  756. NXBeep ();
  757. return self;
  758. }
  759. if ( [self numSelected] == 0)
  760. {
  761. qprintf ("ERROR: must have a seed brush to make an entity");
  762. NXBeep ();
  763. return self;
  764. }
  765. sb_newowner = [[Entity alloc] initClass: [things_i spawnName]];
  766. if ( [sb_newowner modifiable] )
  767. [self makeSelectedPerform: @selector(moveToEntity)];
  768. else
  769. { // throw out seed brush and select entity fixed brush
  770. [self makeSelectedPerform: @selector(remove)];
  771. [[sb_newowner objectAt: 0] setSelected: YES];
  772. }
  773. [self addObject: sb_newowner];
  774. [self setCurrentEntity: sb_newowner];
  775. [quakeed_i updateAll];
  776. return self;
  777. }
  778. - selbox: (SEL)selector
  779. {
  780. id b;
  781. if ([self numSelected] != 1)
  782. {
  783. qprintf ("must have a single brush selected");
  784. return self;
  785. }
  786. b = [self selectedBrush];
  787. [b getMins: select_min maxs: select_max];
  788. [b remove];
  789. [self makeUnselectedPerform: selector];
  790. qprintf ("identified contents");
  791. [quakeed_i updateAll];
  792. return self;
  793. }
  794. - selectCompletelyInside: sender
  795. {
  796. return [self selbox: @selector(selectComplete)];
  797. }
  798. - selectPartiallyInside: sender
  799. {
  800. return [self selbox: @selector(selectPartial)];
  801. }
  802. - tallBrush: sender
  803. {
  804. id b;
  805. vec3_t mins, maxs;
  806. texturedef_t td;
  807. if ([self numSelected] != 1)
  808. {
  809. qprintf ("must have a single brush selected");
  810. return self;
  811. }
  812. b = [self selectedBrush];
  813. td = *[b texturedef];
  814. [b getMins: mins maxs: maxs];
  815. [b remove];
  816. mins[2] = -2048;
  817. maxs[2] = 2048;
  818. b = [[SetBrush alloc] initOwner: [map_i objectAt:0] mins: mins maxs: maxs texture: &td];
  819. [[map_i objectAt: 0] addObject: b];
  820. [b setSelected: YES];
  821. [quakeed_i updateAll];
  822. return self;
  823. }
  824. - shortBrush: sender
  825. {
  826. id b;
  827. vec3_t mins, maxs;
  828. texturedef_t td;
  829. if ([self numSelected] != 1)
  830. {
  831. qprintf ("must have a single brush selected");
  832. return self;
  833. }
  834. b = [self selectedBrush];
  835. td = *[b texturedef];
  836. [b getMins: mins maxs: maxs];
  837. [b remove];
  838. mins[2] = 0;
  839. maxs[2] = 16;
  840. b = [[SetBrush alloc] initOwner: [map_i objectAt:0] mins: mins maxs: maxs texture: &td];
  841. [[map_i objectAt: 0] addObject: b];
  842. [b setSelected: YES];
  843. [quakeed_i updateAll];
  844. return self;
  845. }
  846. /*
  847. ==================
  848. subtractSelection
  849. ==================
  850. */
  851. - subtractSelection: semder
  852. {
  853. int i, j, c, c2;
  854. id o, o2;
  855. id sellist, sourcelist;
  856. qprintf ("performing brush subtraction...");
  857. sourcelist = [[List alloc] init];
  858. sellist = [[List alloc] init];
  859. carve_in = [[List alloc] init];
  860. carve_out = [[List alloc] init];
  861. c = [currentEntity count];
  862. for (i=0 ; i<c ; i++)
  863. {
  864. o = [currentEntity objectAt: i];
  865. if ([o selected])
  866. [sellist addObject: o];
  867. else
  868. [sourcelist addObject: o];
  869. }
  870. c = [sellist count];
  871. for (i=0 ; i<c ; i++)
  872. {
  873. o = [sellist objectAt: i];
  874. [o setCarveVars];
  875. c2 = [sourcelist count];
  876. for (j=0 ; j<c2 ; j++)
  877. {
  878. o2 = [sourcelist objectAt: j];
  879. [o2 carve];
  880. [carve_in freeObjects];
  881. }
  882. [sourcelist free]; // the individual have been moved/freed
  883. sourcelist = carve_out;
  884. carve_out = [[List alloc] init];
  885. }
  886. // add the selection back to the remnants
  887. [currentEntity empty];
  888. [currentEntity appendList: sourcelist];
  889. [currentEntity appendList: sellist];
  890. [sourcelist free];
  891. [sellist free];
  892. [carve_in free];
  893. [carve_out free];
  894. if (![currentEntity count])
  895. {
  896. o = currentEntity;
  897. [self removeObject: o];
  898. [o free];
  899. }
  900. qprintf ("subtracted selection");
  901. [quakeed_i updateAll];
  902. return self;
  903. }
  904. @end