1 // world.cpp: core map management stuff
8 VAR(octaentsize
, 0, 128, 1024);
9 VAR(entselradius
, 0, 2, 10);
11 bool getentboundingbox(extentity
&e
, ivec
&o
, ivec
&r
)
19 model
*m
= loadmodel(NULL
, e
.attr2
);
23 m
->boundbox(0, center
, radius
);
24 rotatebb(center
, radius
, e
.attr1
);
35 // invisible mapmodels use entselradius
39 r
.x
= r
.y
= r
.z
= entselradius
*2;
45 void modifyoctaentity(bool add
, int id
, cube
*c
, const ivec
&cor
, int size
, const ivec
&bo
, const ivec
&br
, vtxarray
*lastva
= NULL
)
47 loopoctabox(cor
, size
, bo
, br
)
49 ivec
o(i
, cor
.x
, cor
.y
, cor
.z
, size
);
50 vtxarray
*va
= c
[i
].ext
&& c
[i
].ext
->va
? c
[i
].ext
->va
: lastva
;
51 if(c
[i
].children
!= NULL
&& size
> octaentsize
)
52 modifyoctaentity(add
, id
, c
[i
].children
, o
, size
>>1, bo
, br
, va
);
55 if(!c
[i
].ext
|| !c
[i
].ext
->ents
) ext(c
[i
]).ents
= new octaentities(o
, size
);
56 octaentities
&oe
= *c
[i
].ext
->ents
;
57 switch(et
->getents()[id
]->type
)
60 if(loadmodel(NULL
, et
->getents()[id
]->attr2
))
62 if(va
&& oe
.mapmodels
.empty())
64 if(!va
->mapmodels
) va
->mapmodels
= new vector
<octaentities
*>;
65 va
->mapmodels
->add(&oe
);
70 oe
.bbmin
[k
] = min(oe
.bbmin
[k
], max(oe
.o
[k
], bo
[k
]));
71 oe
.bbmax
[k
] = max(oe
.bbmax
[k
], min(oe
.o
[k
]+size
, bo
[k
]+br
[k
]));
82 else if(c
[i
].ext
&& c
[i
].ext
->ents
)
84 octaentities
&oe
= *c
[i
].ext
->ents
;
85 switch(et
->getents()[id
]->type
)
88 if(loadmodel(NULL
, et
->getents()[id
]->attr2
))
90 oe
.mapmodels
.removeobj(id
);
91 if(va
&& va
->mapmodels
&& oe
.mapmodels
.empty())
93 va
->mapmodels
->removeobj(&oe
);
94 if(va
->mapmodels
->empty()) DELETEP(va
->mapmodels
);
96 oe
.bbmin
= oe
.bbmax
= oe
.o
;
97 oe
.bbmin
.add(oe
.size
);
100 extentity
&e
= *et
->getents()[oe
.mapmodels
[j
]];
102 if(getentboundingbox(e
, eo
, er
)) loopk(3)
104 oe
.bbmin
[k
] = min(oe
.bbmin
[k
], eo
[k
]);
105 oe
.bbmax
[k
] = max(oe
.bbmax
[k
], eo
[k
]+er
[k
]);
110 oe
.bbmin
[k
] = max(oe
.bbmin
[k
], oe
.o
[k
]);
111 oe
.bbmax
[k
] = min(oe
.bbmax
[k
], oe
.o
[k
]+size
);
115 // invisible mapmodel
117 oe
.other
.removeobj(id
);
120 if(oe
.mapmodels
.empty() && oe
.other
.empty())
121 freeoctaentities(c
[i
]);
123 if(c
[i
].ext
&& c
[i
].ext
->ents
) c
[i
].ext
->ents
->query
= NULL
;
127 static void modifyoctaent(bool add
, int id
)
129 vector
<extentity
*> &ents
= et
->getents();
130 if(!ents
.inrange(id
)) return;
132 extentity
&e
= *ents
[id
];
133 if((e
.inoctanode
!=0)==add
|| !getentboundingbox(e
, o
, r
)) return;
135 modifyoctaentity(add
, id
, worldroot
, ivec(0, 0, 0), hdr
.worldsize
>>1, o
, r
);
136 if(e
.type
== ET_LIGHT
) clearlightcache(id
);
137 else if(add
) lightent(e
);
140 static inline void addentity(int id
) { modifyoctaent(true, id
); }
141 static inline void removeentity(int id
) { modifyoctaent(false, id
); }
143 void freeoctaentities(cube
&c
)
146 if(et
->getents().length())
148 while(c
.ext
->ents
&& !c
.ext
->ents
->mapmodels
.empty()) removeentity(c
.ext
->ents
->mapmodels
.pop());
149 while(c
.ext
->ents
&& !c
.ext
->ents
->other
.empty()) removeentity(c
.ext
->ents
->other
.pop());
158 void entitiesinoctanodes()
160 loopv(et
->getents()) addentity(i
);
163 char *entname(entity
&e
)
165 static string fullentname
;
166 s_strcpy(fullentname
, "@");
167 s_strcat(fullentname
, et
->entname(e
.type
));
168 const char *einfo
= et
->entnameinfo(e
);
171 s_strcat(fullentname
, ": ");
172 s_strcat(fullentname
, einfo
);
178 extern bool havesel
, selectcorners
;
179 int entlooplevel
= 0;
180 int efocus
= -1, enthover
= -1, entorient
= -1, oldhover
= -1;
181 bool undonext
= true;
183 VAR(entediting
, 0, 0, 1);
187 if(!editmode
) { conoutf("operation only allowed in edit mode"); return true; }
191 bool pointinsel(selinfo
&sel
, vec
&o
)
193 return(o
.x
<= sel
.o
.x
+sel
.s
.x
*sel
.grid
195 && o
.y
<= sel
.o
.y
+sel
.s
.y
*sel
.grid
197 && o
.z
<= sel
.o
.z
+sel
.s
.z
*sel
.grid
201 vector
<int> entgroup
;
205 return entgroup
.length() > 0;
219 void initundoent(undoblock
&u
)
222 u
.n
= entgroup
.length();
224 u
.e
= new undoent
[u
.n
];
227 u
.e
->i
= entgroup
[i
];
228 u
.e
->e
= *et
->getents()[entgroup
[i
]];
236 if(!undonext
) return;
244 void detachentity(extentity
&e
)
246 if(!e
.attached
) return;
247 e
.attached
->attached
= NULL
;
251 VAR(attachradius
, 1, 100, 1000);
253 void attachentity(extentity
&e
)
261 if(e
.type
<ET_GAMESPECIFIC
|| !et
->mayattach(e
)) return;
267 vector
<extentity
*> &ents
= et
->getents();
269 float closedist
= 1e10f
;
272 extentity
*a
= ents
[i
];
273 if(a
->attached
) continue;
277 if(a
->type
!=ET_LIGHT
) continue;
281 if(e
.type
<ET_GAMESPECIFIC
|| !et
->attachent(e
, *a
)) continue;
284 float dist
= e
.o
.dist(a
->o
);
291 if(closedist
>attachradius
) return;
292 e
.attached
= ents
[closest
];
293 ents
[closest
]->attached
= &e
;
296 void attachentities()
298 vector
<extentity
*> &ents
= et
->getents();
299 loopv(ents
) attachentity(*ents
[i
]);
302 // convenience macros implicitly define:
303 // e entity, currently edited ent
304 // n int, index to currently edited ent
305 #define addimplicit(f) { if(entgroup.empty() && enthover>=0) { entadd(enthover); undonext = (enthover != oldhover); f; entgroup.drop(); } else f; }
306 #define entfocus(i, f) { int n = efocus = (i); if(n>=0) { extentity &e = *et->getents()[n]; f; } }
307 #define entedit(i, f) \
310 int oldtype = e.type; \
313 if(oldtype!=e.type) detachentity(e); \
314 if(e.type!=ET_EMPTY) { addentity(n); if(oldtype!=e.type) attachentity(e); } \
317 #define addgroup(exp) { loopv(et->getents()) entfocus(i, if(exp) entadd(n)); }
318 #define setgroup(exp) { entcancel(); addgroup(exp); }
319 #define groupeditloop(f){ entlooplevel++; int _ = efocus; loopv(entgroup) entedit(entgroup[i], f); efocus = _; entlooplevel--; }
320 #define groupeditpure(f){ if(entlooplevel>0) { entedit(efocus, f); } else groupeditloop(f); }
321 #define groupeditundo(f){ makeundoent(); groupeditpure(f); }
322 #define groupedit(f) { addimplicit(groupeditundo(f)); }
324 void copyundoents(undoblock
&d
, undoblock
&s
)
330 loopi(s
.n
) if(s
.e
[i
].e
.type
==ET_EMPTY
)
331 entgroup
.remove(s
.e
[i
].i
);
334 void pasteundoents(undoblock
&u
)
337 entedit(u
.e
[i
].i
, (entity
&)e
= u
.e
[i
].e
);
342 if(noentedit()) return;
343 int d
= dimension(sel
.orient
);
344 float mid
= sel
.s
[d
]*sel
.grid
/2+sel
.o
[d
];
345 groupeditundo(e
.o
[d
] -= (e
.o
[d
]-mid
)*2);
348 void entrotate(int *cw
)
350 if(noentedit()) return;
351 int d
= dimension(sel
.orient
);
352 int dd
= *cw
<0 == dimcoord(sel
.orient
) ? R
[d
] : C
[d
];
353 float mid
= sel
.s
[dd
]*sel
.grid
/2+sel
.o
[dd
];
356 e
.o
[dd
] -= (e
.o
[dd
]-mid
)*2;
358 swap(float, e
.o
[R
[d
]], e
.o
[C
[d
]]);
363 void entselectionbox(const entity
&e
, vec
&eo
, vec
&es
)
366 if(e
.type
== ET_MAPMODEL
&& (m
= loadmodel(NULL
, e
.attr2
)))
368 m
->collisionbox(0, eo
, es
);
369 rotatebb(eo
, es
, e
.attr1
);
371 eo
.z
-= player
->aboveeye
; // wacky but true. see physics collide
373 es
.div(2); // cause the usual bb is too big...
378 es
= vec(entselradius
);
385 VAR(entselsnap
, 0, 0, 1);
386 VAR(entmovingshadow
, 0, 1, 1);
388 extern void boxs(int orient
, vec o
, const vec
&s
);
389 extern void boxs3D(const vec
&o
, vec s
, int g
);
390 extern void editmoveplane(const vec
&o
, const vec
&ray
, int d
, float off
, vec
&handle
, vec
&dest
, bool first
);
392 bool initentdragging
= true;
394 void entdrag(const vec
&ray
)
396 if(noentedit() || !haveselent()) return;
399 static vec v
, handle
;
401 int d
= dimension(entorient
),
402 dc
= dimcoord(entorient
);
404 entfocus(entgroup
.last(),
405 entselectionbox(e
, eo
, es
);
407 editmoveplane(e
.o
, ray
, d
, eo
[d
] + (dc
? es
[d
] : 0), handle
, v
, initentdragging
);
410 int z
= g
[d
]&(~(sel
.grid
-1));
411 g
.add(sel
.grid
/2).mask(~(sel
.grid
-1));
414 r
= (entselsnap
? g
[R
[d
]] : v
[R
[d
]]) - e
.o
[R
[d
]];
415 c
= (entselsnap
? g
[C
[d
]] : v
[C
[d
]]) - e
.o
[C
[d
]];
418 if(initentdragging
) makeundoent();
419 groupeditpure(e
.o
[R
[d
]] += r
; e
.o
[C
[d
]] += c
);
420 initentdragging
= false;
423 VAR(showentradius
, 0, 1, 1);
425 void renderentradius(extentity
&e
)
427 if(!showentradius
) return;
428 float radius
= 0.0f
, angle
= 0.0f
, ring
= 0.0f
;
430 float color
[3] = {0, 1, 1};
435 color
[0] = e
.attr2
/255.0f
;
436 color
[1] = e
.attr3
/255.0f
;
437 color
[2] = e
.attr4
/255.0f
;
443 radius
= e
.attached
->attr1
;
444 if(!radius
) radius
= 2*e
.o
.dist(e
.attached
->o
);
445 dir
= vec(e
.o
).sub(e
.attached
->o
).normalize();
446 angle
= max(1, min(90, e
.attr1
));
456 extern int envmapradius
;
457 radius
= e
.attr1
? max(0, min(10000, e
.attr1
)) : envmapradius
;
464 if(e
.type
==ET_MAPMODEL
&& e
.attr3
) ring
= checktriggertype(e
.attr3
, TRIG_COLLIDE
) ? 20 : 12;
465 vecfromyawpitch(e
.attr1
, 0, 1, 0, dir
);
469 if(e
.type
>=ET_GAMESPECIFIC
) et
->entradius(e
, radius
, angle
, dir
);
472 if(radius
<=0) return;
473 glPolygonMode(GL_FRONT_AND_BACK
, GL_FILL
);
478 glDepthFunc(GL_GREATER
);
479 glColor3f(0.25f
, 0.25f
, 0.25f
);
483 glDepthFunc(GL_LESS
);
490 glVertex3fv(e
.attached
->o
.v
);
495 glBegin(GL_LINE_LOOP
);
499 p
.x
+= ring
*cosf(2*M_PI
*i
/16.0f
);
500 p
.y
+= ring
*sinf(2*M_PI
*i
/16.0f
);
505 if(dir
.iszero()) loopk(3)
507 glBegin(GL_LINE_LOOP
);
511 p
[k
>=2 ? 1 : 0] += radius
*cosf(2*M_PI
*i
/16.0f
);
512 p
[k
>=1 ? 2 : 1] += radius
*sinf(2*M_PI
*i
/16.0f
);
519 float arrowsize
= min(radius
/8, 0.5f
);
520 vec
target(vec(dir
).mul(radius
).add(e
.o
)), arrowbase(vec(dir
).mul(radius
- arrowsize
).add(e
.o
)), spoke
;
521 spoke
.orthogonal(dir
);
523 spoke
.mul(arrowsize
);
526 glVertex3fv(target
.v
);
528 glBegin(GL_TRIANGLE_FAN
);
529 glVertex3fv(target
.v
);
533 p
.rotate(2*M_PI
*i
/4.0f
, dir
);
541 vec
spot(vec(dir
).mul(radius
*cosf(angle
*RAD
)).add(e
.attached
->o
)), spoke
;
542 spoke
.orthogonal(dir
);
544 spoke
.mul(radius
*sinf(angle
*RAD
));
549 p
.rotate(2*M_PI
*i
/8.0f
, dir
);
551 glVertex3fv(e
.attached
->o
.v
);
555 glBegin(GL_LINE_LOOP
);
559 p
.rotate(2*M_PI
*i
/8.0f
, dir
);
566 glPolygonMode(GL_FRONT_AND_BACK
, GL_LINE
);
569 void renderentselection(const vec
&o
, const vec
&ray
, bool entmoving
)
571 if(noentedit()) return;
574 glColor3ub(0, 40, 0);
575 loopv(entgroup
) entfocus(entgroup
[i
],
576 entselectionbox(e
, eo
, es
);
582 entfocus(enthover
, entselectionbox(e
, eo
, es
)); // also ensures enthover is back in focus
584 if(entmoving
&& entmovingshadow
==1)
587 glColor3ub(20, 20, 20);
588 (a
=eo
).x
=0; (b
=es
).x
=hdr
.worldsize
; boxs3D(a
, b
, 1);
589 (a
=eo
).y
=0; (b
=es
).y
=hdr
.worldsize
; boxs3D(a
, b
, 1);
590 (a
=eo
).z
=0; (b
=es
).z
=hdr
.worldsize
; boxs3D(a
, b
, 1);
594 boxs(entorient
, eo
, es
);
598 loopv(entgroup
) entfocus(entgroup
[i
], renderentradius(e
));
599 if(enthover
>=0) entfocus(enthover
, renderentradius(e
));
602 bool enttoggle(int id
)
605 int i
= entgroup
.find(id
);
613 bool hoveringonent(int ent
, int orient
)
615 if(noentedit()) return false;
617 if((efocus
= enthover
= ent
) >= 0)
619 efocus
= entgroup
.empty() ? -1 : entgroup
.last();
624 VAR(entitysurf
, 0, 0, 1);
625 VARF(entmoving
, 0, 0, 2,
626 if(enthover
< 0 || noentedit())
628 else if(entmoving
== 1)
629 entmoving
= enttoggle(enthover
);
630 else if(entmoving
== 2 && entgroup
.find(enthover
) < 0)
633 initentdragging
= true;
636 void entpush(int *dir
)
638 if(noentedit()) return;
639 int d
= dimension(entorient
);
640 int s
= dimcoord(entorient
) ? -*dir
: *dir
;
643 groupeditpure(e
.o
[d
] += float(s
*sel
.grid
)); // editdrag supplies the undo
646 groupedit(e
.o
[d
] += float(s
*sel
.grid
));
648 player
->o
[d
] += float(s
*sel
.grid
);
651 VAR(entautoviewdist
, 0, 25, 100);
652 void entautoview(int *dir
)
654 if(!haveselent()) return;
659 v
.mul(entautoviewdist
);
661 s
= abs(t
) % entgroup
.length();
662 if(t
<0 && s
>0) s
= entgroup
.length() - s
;
663 entfocus(entgroup
[s
],
669 COMMAND(entautoview
, "i");
670 COMMAND(entflip
, "");
671 COMMAND(entrotate
, "i");
672 COMMAND(entpush
, "i");
676 if(noentedit()) return;
677 groupedit(e
.type
= ET_EMPTY
;);
681 int findtype(char *what
)
683 for(int i
= 0; *et
->entname(i
); i
++) if(strcmp(what
, et
->entname(i
))==0) return i
;
684 conoutf("unknown entity type \"%s\"", what
);
688 VAR(entdrop
, 0, 2, 3);
690 bool dropentity(entity
&e
, int drop
= -1)
692 vec
radius(4.0f
, 4.0f
, 4.0f
);
693 if(drop
<0) drop
= entdrop
;
694 if(e
.type
== ET_MAPMODEL
)
696 model
*m
= loadmodel(NULL
, e
.attr2
);
700 m
->boundbox(0, center
, radius
);
701 rotatebb(center
, radius
, e
.attr1
);
702 radius
.x
+= fabs(center
.x
);
703 radius
.y
+= fabs(center
.y
);
710 if(e
.type
!= ET_LIGHT
&& e
.type
!= ET_SPOTLIGHT
)
716 if(sel
.cxs
== 1 && sel
.cys
== 1)
718 cx
= (sel
.cx
? 1 : -1) * sel
.grid
/ 2;
719 cy
= (sel
.cy
? 1 : -1) * sel
.grid
/ 2;
722 int d
= dimension(sel
.orient
), dc
= dimcoord(sel
.orient
);
723 e
.o
[R
[d
]] += sel
.grid
/ 2 + cx
;
724 e
.o
[C
[d
]] += sel
.grid
/ 2 + cy
;
726 e
.o
[D
[d
]] -= radius
[D
[d
]];
728 e
.o
[D
[d
]] += sel
.grid
+ radius
[D
[d
]];
739 if(noentedit()) return;
740 groupedit(dropentity(e
));
745 if(noentedit()) return;
746 groupedit(attachentity(e
));
749 COMMAND(attachent
, "");
751 extentity
*newentity(bool local
, const vec
&o
, int type
, int v1
, int v2
, int v3
, int v4
)
753 extentity
&e
= *et
->newentity();
763 e
.inoctanode
= false;
764 e
.color
= vec(1, 1, 1);
774 e
.attr1
= (int)camera1
->yaw
;
782 void newentity(int type
, int a1
, int a2
, int a3
, int a4
)
784 extentity
*t
= newentity(true, player
->o
, type
, a1
, a2
, a3
, a4
);
786 et
->getents().add(t
);
787 int i
= et
->getents().length()-1;
791 entedit(i
, e
.type
= type
);
794 void newent(char *what
, int *a1
, int *a2
, int *a3
, int *a4
)
796 if(noentedit()) return;
797 int type
= findtype(what
);
799 newentity(type
, *a1
, *a2
, *a3
, *a4
);
803 vector
<entity
> entcopybuf
;
807 if(noentedit()) return;
808 entcopygrid
= sel
.grid
;
809 entcopybuf
.setsize(0);
811 entfocus(entgroup
[i
], entcopybuf
.add(e
).o
.sub(sel
.o
.v
));
816 if(noentedit()) return;
817 if(entcopybuf
.length()==0) return;
819 int last
= et
->getents().length()-1;
820 float m
= float(sel
.grid
)/float(entcopygrid
);
823 entity
&c
= entcopybuf
[i
];
825 o
.mul(m
).add(sel
.o
.v
);
826 extentity
*e
= newentity(true, o
, ET_EMPTY
, c
.attr1
, c
.attr2
, c
.attr3
, c
.attr4
);
827 et
->getents().add(e
);
831 groupeditundo(e
.type
= entcopybuf
[j
++].type
;);
834 COMMAND(newent
, "siiii");
836 COMMAND(dropent
, "");
837 COMMAND(entcopy
, "");
838 COMMAND(entpaste
, "");
840 void entset(char *what
, int *a1
, int *a2
, int *a3
, int *a4
)
842 if(noentedit()) return;
843 int type
= findtype(what
);
844 groupedit(e
.type
=type
;
851 ICOMMAND(enthavesel
,"", (), addimplicit(intret(entgroup
.length())));
852 ICOMMAND(entselect
, "s", (char *body
), if(!noentedit()) addgroup(e
.type
!= ET_EMPTY
&& entgroup
.find(n
)<0 && execute(body
)>0));
853 ICOMMAND(entloop
, "s", (char *body
), if(!noentedit()) addimplicit(groupeditloop(((void)e
, execute(body
)))));
854 ICOMMAND(insel
, "", (), entfocus(efocus
, intret(pointinsel(sel
, e
.o
))));
855 ICOMMAND(entget
, "", (), entfocus(efocus
, s_sprintfd(s
)("%s %d %d %d %d", et
->entname(e
.type
), e
.attr1
, e
.attr2
, e
.attr3
, e
.attr4
); result(s
)));
856 COMMAND(entset
, "siiii");
859 int findentity(int type
, int index
)
861 const vector
<extentity
*> &ents
= et
->getents();
862 for(int i
= index
; i
<ents
.length(); i
++) if(ents
[i
]->type
==type
) return i
;
863 loopj(min(index
, ents
.length())) if(ents
[j
]->type
==type
) return j
;
867 int spawncycle
= -1, fixspawn
= 4;
869 void findplayerspawn(dynent
*d
, int forceent
) // place at random spawn. also used by monsters!
874 int r
= fixspawn
-->0 ? 7 : rnd(10)+1;
875 loopi(r
) spawncycle
= findentity(ET_PLAYERSTART
, spawncycle
+1);
882 for(int attempt
= pick
;;)
884 d
->o
= et
->getents()[attempt
]->o
;
885 d
->yaw
= et
->getents()[attempt
]->attr1
;
886 if(entinmap(d
, true)) break;
887 attempt
= findentity(ET_PLAYERSTART
, attempt
+1);
888 if(attempt
<0 || attempt
==pick
)
890 d
->o
= et
->getents()[attempt
]->o
;
891 d
->yaw
= et
->getents()[attempt
]->attr1
;
899 d
->o
.x
= d
->o
.y
= d
->o
.z
= 0.5f
*getworldsize();
904 void splitocta(cube
*c
, int size
)
906 if(size
<= VVEC_INT_MASK
+1) return;
909 if(!c
[i
].children
) c
[i
].children
= newcubes(isempty(c
[i
]) ? F_EMPTY
: F_SOLID
);
910 splitocta(c
[i
].children
, size
>>1);
925 setvar("gamespeed", 100);
928 et
->getents().deletecontentsp();
931 void startmap(const char *name
)
936 bool emptymap(int scale
, bool force
) // main empty world creation routine
938 if(!force
&& !editmode
)
940 conoutf("newmap only allowed in edit mode");
946 strncpy(hdr
.head
, "OCTA", 4);
947 hdr
.version
= MAPVERSION
;
948 hdr
.headersize
= sizeof(header
);
949 hdr
.worldsize
= 1 << (scale
<10 ? 10 : (scale
>20 ? 20 : scale
));
951 s_strncpy(hdr
.maptitle
, "Untitled Map by Unknown", 128);
952 hdr
.waterlevel
= -100000;
953 memset(hdr
.watercolour
, 0, sizeof(hdr
.watercolour
));
958 memset(hdr
.skylight
, 0, sizeof(hdr
.skylight
));
959 memset(hdr
.reserved
, 0, sizeof(hdr
.reserved
));
962 worldroot
= newcubes(F_EMPTY
);
963 loopi(4) solidfaces(worldroot
[i
]);
965 if(hdr
.worldsize
> VVEC_INT_MASK
+1) splitocta(worldroot
, hdr
.worldsize
>>1);
970 overrideidents
= true;
971 execfile("data/default_map_settings.cfg");
972 overrideidents
= false;
975 player
->o
.z
+= player
->eyeheight
+1;
980 bool enlargemap(bool force
)
982 if(!force
&& !editmode
)
984 conoutf("mapenlarge only allowed in edit mode");
987 if(hdr
.worldsize
>= 1<<20) return false;
990 cube
*c
= newcubes(F_EMPTY
);
991 c
[0].children
= worldroot
;
992 loopi(3) solidfaces(c
[i
+1]);
995 if(hdr
.worldsize
> VVEC_INT_MASK
+1) splitocta(worldroot
, hdr
.worldsize
>>1);
1002 void newmap(int *i
) { if(emptymap(*i
, false)) cl
->newmap(max(*i
, 0)); }
1003 void mapenlarge() { if(enlargemap(false)) cl
->newmap(-1); }
1004 COMMAND(newmap
, "i");
1005 COMMAND(mapenlarge
, "");
1009 result(cl
->getclientmap());
1012 COMMAND(mapname
, "");
1014 void mpeditent(int i
, const vec
&o
, int type
, int attr1
, int attr2
, int attr3
, int attr4
, bool local
)
1016 if(et
->getents().length()<=i
)
1018 while(et
->getents().length()<i
) et
->getents().add(et
->newentity())->type
= ET_EMPTY
;
1019 extentity
*e
= newentity(local
, o
, type
, attr1
, attr2
, attr3
, attr4
);
1020 et
->getents().add(e
);
1026 extentity
&e
= *et
->getents()[i
];
1028 int oldtype
= e
.type
;
1029 if(oldtype
!=type
) detachentity(e
);
1032 e
.attr1
= attr1
; e
.attr2
= attr2
; e
.attr3
= attr3
; e
.attr4
= attr4
;
1034 if(oldtype
!=type
) attachentity(e
);
1038 int getworldsize() { return hdr
.worldsize
; }
1039 int getmapversion() { return hdr
.version
; }
1041 int triggertypes
[NUMTRIGGERTYPES
] =
1047 TRIG_TOGGLE
| TRIG_RUMBLE
,
1049 TRIG_MANY
| TRIG_RUMBLE
,
1050 TRIG_MANY
| TRIG_TOGGLE
,
1051 TRIG_MANY
| TRIG_TOGGLE
| TRIG_RUMBLE
,
1052 TRIG_COLLIDE
| TRIG_TOGGLE
| TRIG_RUMBLE
,
1053 TRIG_COLLIDE
| TRIG_TOGGLE
| TRIG_AUTO_RESET
| TRIG_RUMBLE
,
1054 TRIG_COLLIDE
| TRIG_TOGGLE
| TRIG_LOCKED
| TRIG_RUMBLE
,
1056 TRIG_DISAPPEAR
| TRIG_RUMBLE
,
1057 TRIG_DISAPPEAR
| TRIG_COLLIDE
| TRIG_LOCKED
,
1061 void resettriggers()
1063 const vector
<extentity
*> &ents
= et
->getents();
1066 extentity
&e
= *ents
[i
];
1067 if(e
.type
!= ET_MAPMODEL
|| !e
.attr3
) continue;
1068 e
.triggerstate
= TRIGGER_RESET
;
1073 void unlocktriggers(int tag
, int oldstate
= TRIGGER_RESET
, int newstate
= TRIGGERING
)
1075 const vector
<extentity
*> &ents
= et
->getents();
1078 extentity
&e
= *ents
[i
];
1079 if(e
.type
!= ET_MAPMODEL
|| !e
.attr3
) continue;
1080 if(e
.attr4
== tag
&& e
.triggerstate
== oldstate
&& checktriggertype(e
.attr3
, TRIG_LOCKED
))
1082 if(newstate
== TRIGGER_RESETTING
&& checktriggertype(e
.attr3
, TRIG_COLLIDE
) && overlapsdynent(e
.o
, 20)) continue;
1083 e
.triggerstate
= newstate
;
1084 e
.lasttrigger
= lastmillis
;
1085 if(checktriggertype(e
.attr3
, TRIG_RUMBLE
)) et
->rumble(e
);
1090 void trigger(int *tag
, int *state
)
1092 if(*state
) unlocktriggers(*tag
);
1093 else unlocktriggers(*tag
, TRIGGERED
, TRIGGER_RESETTING
);
1096 COMMAND(trigger
, "ii");
1098 VAR(triggerstate
, -1, 0, 1);
1100 void doleveltrigger(int trigger
, int state
)
1102 s_sprintfd(aliasname
)("level_trigger_%d", trigger
);
1103 if(identexists(aliasname
))
1105 triggerstate
= state
;
1110 void checktriggers()
1112 if(player
->state
!= CS_ALIVE
) return;
1114 o
.z
-= player
->eyeheight
;
1115 const vector
<extentity
*> &ents
= et
->getents();
1118 extentity
&e
= *ents
[i
];
1119 if(e
.type
!= ET_MAPMODEL
|| !e
.attr3
) continue;
1120 switch(e
.triggerstate
)
1123 case TRIGGER_RESETTING
:
1124 if(lastmillis
-e
.lasttrigger
>=1000)
1128 if(e
.triggerstate
== TRIGGERING
) unlocktriggers(e
.attr4
);
1129 else unlocktriggers(e
.attr4
, TRIGGERED
, TRIGGER_RESETTING
);
1131 if(checktriggertype(e
.attr3
, TRIG_DISAPPEAR
)) e
.triggerstate
= TRIGGER_DISAPPEARED
;
1132 else if(e
.triggerstate
==TRIGGERING
&& checktriggertype(e
.attr3
, TRIG_TOGGLE
)) e
.triggerstate
= TRIGGERED
;
1133 else e
.triggerstate
= TRIGGER_RESET
;
1139 if(checktriggertype(e
.attr3
, TRIG_AUTO_RESET
|TRIG_MANY
|TRIG_LOCKED
) && e
.o
.dist(o
)-player
->radius
>=(checktriggertype(e
.attr3
, TRIG_COLLIDE
) ? 20 : 12))
1143 else if(e
.o
.dist(o
)-player
->radius
>=(checktriggertype(e
.attr3
, TRIG_COLLIDE
) ? 20 : 12)) break;
1144 else if(checktriggertype(e
.attr3
, TRIG_LOCKED
))
1147 doleveltrigger(e
.attr4
, -1);
1148 e
.lasttrigger
= lastmillis
;
1151 e
.triggerstate
= TRIGGERING
;
1152 e
.lasttrigger
= lastmillis
;
1153 if(checktriggertype(e
.attr3
, TRIG_RUMBLE
)) et
->rumble(e
);
1155 if(e
.attr4
) doleveltrigger(e
.attr4
, 1);
1158 if(e
.o
.dist(o
)-player
->radius
<(checktriggertype(e
.attr3
, TRIG_COLLIDE
) ? 20 : 12))
1160 if(e
.lasttrigger
) break;
1162 else if(checktriggertype(e
.attr3
, TRIG_AUTO_RESET
))
1164 if(lastmillis
-e
.lasttrigger
<6000) break;
1166 else if(checktriggertype(e
.attr3
, TRIG_MANY
))
1172 if(checktriggertype(e
.attr3
, TRIG_COLLIDE
) && overlapsdynent(e
.o
, 20)) break;
1173 e
.triggerstate
= TRIGGER_RESETTING
;
1174 e
.lasttrigger
= lastmillis
;
1175 if(checktriggertype(e
.attr3
, TRIG_RUMBLE
)) et
->rumble(e
);
1177 if(e
.attr4
) doleveltrigger(e
.attr4
, 0);