/* viDOOM - level editor for DOOM Copyright (C) 2000 Ian Cowburn (ianc@noddybox.demon.co.uk) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ------------------------------------------------------------------------- Editor LINEDEF definitions */ static const char rcs_id[]="$Id$"; #include "config.h" #include "globals.h" #include #include #include "editvar.h" #include "sectors.h" #include "texture.h" #include "genlines.h" #include "specials.h" #include "flags.h" #include "util.h" /* ---------------------------------------- MACROS */ #define LinesCrossV(v1,v2,v3,v4) \ LinesCross ((v1)->v.x,(v1)->v.y,(v2)->v.x,(v2)->v.y, \ (v3)->v.x,(v3)->v.y,(v4)->v.x,(v4)->v.y) #define SetPoint(pt,vt) do {(pt).x=(vt)->v.x;(pt).y=(vt)->v.y;} while(0) #define SPLINE_THRESH 1 /* This following define relies on the naming of parameters in DoCalcSpline() and DoCalcLine() */ #define CHECKSTEP(X,Y) do { \ (*len)++; \ \ if (p) \ if (((*len)%step)==0) \ { \ if ((*idx)v[0]->v.x)-(l->v[1]->v.x))/2; dy=((l->v[0]->v.y)-(l->v[1]->v.y))/2; MapLineD(l,col); MapLine((int)(l->v[0]->v.x-dx),(int)(l->v[0]->v.y-dy), (int)(l->v[0]->v.x-dx-dy/6),(int)(l->v[0]->v.y-dy+dx/6),col); } static int SelColour(int selmode) { switch(selmode) { case SELECT_OVER: return(OVERCOL); break; case SELECT_SELECTED: return(SELCOL); break; default: if (edit_mode==SECTOR_MODE) return(SECTCOL); else return(LINECOL); break; } } static void RemoveHighlights(List l) { Iterator i; Object *o; int *n; i=ListIterator(l); while(i) { n=IteratorData(i); if ((o=MapElem(linedef,*n))) DrawLinedef(o->data,SelColour(o->select)); i=IteratorNext(i); } } static void LineSelectionCentre(int *x,int *y) { int *f; EditLine *l; Iterator i; int min_x,min_y,max_x,max_y; VIDOOM_TRACE; i=ListIterator(selected); min_x=99999; min_y=99999; max_x=-99999; max_y=-99999; while(i) { f=IteratorData(i); l=GETLINE(*f); min_x=MIN(l->min_x,min_x); min_y=MIN(l->min_y,min_y); max_x=MAX(l->max_x,max_x); max_y=MAX(l->max_y,max_y); i=IteratorNext(i); } *x=min_x+(max_x-min_x)/2; *y=min_y+(max_y-min_y)/2; } static void DeleteLeftSidedef(EditLine *l) { Object *o; int n; int del; int side; EditLine *cl; VIDOOM_TRACE; if (l->l.left==-1) return; l->sl=NULL; side=l->l.left; l->l.left=-1; /* Free up the sidedef if there is no other linedef bound to it */ del=TRUE; for(n=0;(nl.left==side)||(cl->l.right==side))) del=FALSE; } if (del) { o=MapElem(sidedef,side); Release(o->data); o->data=NULL; } } static void DeleteRightSidedef(EditLine *l) { Object *o; int n; int del; int side; EditLine *cl; VIDOOM_TRACE; if (l->l.right==-1) return; l->sr=NULL; side=l->l.right; l->l.right=-1; /* Free up the sidedef if there is no other linedef bound to it */ del=TRUE; for(n=0;(nl.left==side)||(cl->l.right==side))) del=FALSE; } if (del) { o=MapElem(sidedef,side); Release(o->data); o->data=NULL; } } static void DeleteLinedef(int f) { Object *o; EditLine *l; VIDOOM_TRACE; o=MapElem(linedef,f); l=o->data; DeleteLeftSidedef(l); DeleteRightSidedef(l); IntListRm(l->v[0]->l,f); IntListRm(l->v[1]->l,f); Release(o->data); o->data=NULL; o->select=SELECT_NONE; } static int SplitLinedef(int f) { Object o; EditLine *l,*nl; Sidedef *nsl,*nsr; EditVert *nv; int n_nl,dx,dy,tw; VIDOOM_TRACE; l=GETLINE(f); o.select=SELECT_NONE; /* Create copies of the original linedefs and sidedefs */ nl=Grab(sizeof(EditLine)); memcpy(nl,l,sizeof(EditLine)); o.data=nl; n_nl=MapSize(linedef); nl->no=n_nl; MapAdd(linedef,n_nl,&o); nsr=Grab(sizeof(Sidedef)); memcpy(nsr,l->sr,sizeof(Sidedef)); o.data=nsr; nl->l.right=MapSize(sidedef); MapAdd(sidedef,nl->l.right,&o); nl->sr=GETSIDE(nl->l.right); if (l->l.left!=-1) { nsl=Grab(sizeof(Sidedef)); memcpy(nsl,l->sl,sizeof(Sidedef)); o.data=nsl; nl->l.left=MapSize(sidedef); MapAdd(sidedef,nl->l.left,&o); nl->sl=GETSIDE(nl->l.left); } else nsl=NULL; /* Work out the middle of the current line and create the vertex there */ nv=Grab(sizeof(EditVert)); nv->l=ListNew(sizeof(int)); dx=((l->v[1]->v.x)-(l->v[0]->v.x))/2; dy=((l->v[1]->v.y)-(l->v[0]->v.y))/2; nv->v.x=l->v[0]->v.x+dx; nv->v.y=l->v[0]->v.y+dy; o.data=nv; /* Update the vertex map, and the from/to in the old/new linedefs */ nl->l.from=MapSize(vertex); MapAdd(vertex,nl->l.from,&o); l->l.to=nl->l.from; l->v[1]=GETVERT(l->l.to); nl->v[0]=GETVERT(nl->l.from); /* Update the vertex lists */ IntListRm(nl->v[1]->l,f); IntListUniqAdd(nl->v[0]->l,f); IntListUniqAdd(nl->v[0]->l,n_nl); /* Align textures */ tw=CalcTextureWidth(nsr->upper,nsr->middle,nsr->lower); if (tw) nsr->x=(l->sr->x+Len(l->v[0]->v.x,l->v[0]->v.y, l->v[1]->v.x,l->v[1]->v.y))%tw; else nsr->x=0; if (nsl) { tw=CalcTextureWidth(nsl->upper,nsl->middle,nsl->lower); if (tw) nsl->x=(l->sl->x+Len(l->v[0]->v.x,l->v[0]->v.y, l->v[1]->v.x,l->v[1]->v.y))%tw; else nsl->x=0; } /* New linedef no */ return(n_nl); } /* ---------------------------------------- STEP CREATION CODE CALCULATIONS */ static void DoCalcLine (int x0,int y0,int x1,int y1, Point p[],int step,int *idx,int *len) { int dx,dy,ix,iy,incre,incrne,d,x,y,ymode; int p1x,p1y,p2x,p2y; VIDOOM_TRACE; p1x=x0; p1y=y0; p2x=x1; p2y=y1; dx=p2x-p1x; dy=p2y-p1y; ix=SGN(dx); iy=SGN(dy); dx=ABS(dx); dy=ABS(dy); if (dy>dx) { ymode=TRUE; d=dx*2-dy; incre=dx*2; incrne=(dx-dy)*2; } else { ymode=FALSE; d=dy*2-dx; incre=dy*2; incrne=(dy-dx)*2; } x=p1x; y=p1y; CHECKSTEP(x,y); if (ymode) while(y!=p2y) { if (d<=0) { d+=incre; y+=iy; } else { d+=incrne; y+=iy; x+=ix; } CHECKSTEP(x,y); } else while(x!=p2x) { if (d<=0) { d+=incre; x+=ix; } else { d+=incrne; y+=iy; x+=ix; } CHECKSTEP(x,y); } } static void DoCalcSpline (int x0,int y0,int x1,int y1,int x2,int y2, Point p[],int step,int *idx,int *len) { int xa, ya, xb, yb, xc, yc, xp, yp; VIDOOM_TRACE; if ((x0==x1==x2)&&(y0==y1==y2)) { CHECKSTEP(x0,y0); return; } if ((x0==x1)&&(y0==y1)) { if (p) DoCalcLine(x1,y1,x2,y2,p,step,idx,len); else *len+=Len(x1,y1,x2,y2); return; } if ((x0==x2)&&(y0==y2)) { if (p) DoCalcLine(x0,y0,x1,y1,p,step,idx,len); else *len+=Len(x0,y0,x1,y1); return; } if ((x1==x2)&&(y1==y2)) { if (p) DoCalcLine(x0,y0,x2,y2,p,step,idx,len); else *len+=Len(x0,y0,x2,y2); return; } xa = ( x0 + x1 ) / 2; ya = ( y0 + y1 ) / 2; xc = ( x1 + x2 ) / 2; yc = ( y1 + y2 ) / 2; xb = ( xa + xc ) / 2; yb = ( ya + yc ) / 2; xp = ( x0 + xb ) / 2; yp = ( y0 + yb ) / 2; if ( ABS( xa - xp ) + ABS( ya - yp ) > SPLINE_THRESH ) DoCalcSpline(x0,y0,xa,ya,xb,yb,p,step,idx,len); else if (p) DoCalcLine(x0,y0,xb,yb,p,step,idx,len); else *len+=Len(x0,y0,xb,yb); xp = ( x2 + xb ) / 2; yp = ( y2 + yb ) / 2; if ( ABS( xc - xp ) + ABS( yc - yp ) > SPLINE_THRESH ) DoCalcSpline(xb,yb,xc,yc,x2,y2,p,step,idx,len); else if (p) DoCalcLine(xb,yb,x2,y2,p,step,idx,len); else *len+=Len(xb,yb,x2,y2); } static void Spline(int x0,int y0, int x1, int y1, int x2, int y2, Point p[],int step) { int len; int idx; VIDOOM_TRACE; len=0; idx=0; DoCalcSpline(x0,y0,x1,y1,x2,y2,p,step,&idx,&len); } static int SplineLen(int x0,int y0, int x1, int y1, int x2, int y2) { int len; VIDOOM_TRACE; len=0; DoCalcSpline(x0,y0,x1,y1,x2,y2,NULL,0,NULL,&len); return (len); } static void CalcSteps(void) { double dx,dy; int f,r; int len; VIDOOM_TRACE; step.step_f=((double)step.to_f-step.from_f)/(step.no+2); step.step_c=((double)step.to_c-step.from_c)/(step.no+2); if (step.circular) { f=step.side; len=SplineLen(step.vf[f].x,step.vf[f].y,step.x,step.y, step.vt[f].x,step.vt[f].y); len=(int)((double)len/((double)step.no+1.0)+1.0); if (len<1) len=1; Spline(step.vf[f].x,step.vf[f].y,step.x,step.y, step.vt[f].x,step.vt[f].y,&step.p[f][1],len); step.p[f][0].x=step.vf[f].x; step.p[f][0].y=step.vf[f].y; step.p[f][step.no+1].x=step.vt[f].x; step.p[f][step.no+1].y=step.vt[f].y; } else for(f=0;f<2;f++) { dx=(double)(step.vt[f].x-step.vf[f].x)/(step.no+1); dy=(double)(step.vt[f].y-step.vf[f].y)/(step.no+1); for(r=1;r<=step.no;r++) { step.p[f][r].x=(int)(step.vf[f].x+(dx*r)); step.p[f][r].y=(int)(step.vf[f].y+(dy*r)); } step.p[f][0].x=step.vf[f].x; step.p[f][0].y=step.vf[f].y; step.p[f][step.no+1].x=step.vt[f].x; step.p[f][step.no+1].y=step.vt[f].y; } } int SectorInBox(Point tl, Point br) { int x,y; x=tl.x+(br.x-tl.x)/2; y=tl.y+(br.y-tl.y)/2; return (SectorHoldingPoint(x,y)); } /* ---------------------------------------- STEP CREATION CALLBACKS */ static void PickDraw(int *x, int *y) { int f; VIDOOM_TRACE; step.x=*x; step.y=*y; if (step.circular) CalcSteps(); for(f=0;f<2;f++) MapLine(step.p[f][0].x,step.p[f][0].y, step.p[f][1].x,step.p[f][1].y,LINECOL); for(f=1;f<=step.no;f++) { MapLine(step.p[0][f].x,step.p[0][f].y, step.p[1][f].x,step.p[1][f].y,LINECOL); MapLine(step.p[0][f].x,step.p[0][f].y, step.p[0][f+1].x,step.p[0][f+1].y,LINECOL); MapLine(step.p[1][f].x,step.p[1][f].y, step.p[1][f+1].x,step.p[1][f+1].y,LINECOL); } } static void PickKey(GFXKey k) { VIDOOM_TRACE; if (k.code==GFX_ASCII) switch(toupper(k.ascii)) { case 'C': step.circular=!step.circular; CalcSteps(); break; case 'S': step.side^=1; CalcSteps(); break; case 'R': step.swap=!step.swap; if (!step.swap) { SetPoint(step.vf[0],step.from->v[0]); SetPoint(step.vf[1],step.from->v[1]); SetPoint(step.vt[0],step.to->v[0]); SetPoint(step.vt[1],step.to->v[1]); } else { SetPoint(step.vf[0],step.from->v[0]); SetPoint(step.vf[1],step.from->v[1]); SetPoint(step.vt[0],step.to->v[1]); SetPoint(step.vt[1],step.to->v[0]); } CalcSteps(); if (step.circular) { step.side^=1; CalcSteps(); step.side^=1; } break; case '+': if (step.noMIN_STEPS) { step.no--; CalcSteps(); if (step.circular) { step.side^=1; CalcSteps(); step.side^=1; } } break; } } static void PickInfo(char *p) { VIDOOM_TRACE; GuiDrawInfoBox(p,GUI_FLUSH_RIGHT,GUI_FLUSH_LOWER,FALSE, "No : %d|" "Step (F) : %d|" "Step (C) : %d|" "Mode : %-s|" "Side : %d", step.no,ABS((int)step.step_f),ABS((int)step.step_c), step.circular ? "Curve " : "Straight", step.side); GuiDrawInfoBox(p,GUI_FLUSH_LEFT,GUI_FLUSH_LOWER,FALSE, "+ - increase number of steps|" "- - decrease number of steps|" "C - change between straight/curve|" "S - change wall being moved in curve mode|" "R - swap vertex matching|" " |" "Press left button to complete. ESC to cancel"); } /* ---------------------------------------- STEP CREATION CODE */ typedef struct { int line; int apply; } TextureMatch; static void ApplyTextureOffset(List li,int tw,int right) { int off; Iterator i; TextureMatch *tm; EditLine *l; Sidedef *s; VIDOOM_TRACE; i=ListIterator(li); off=0; while(i) { tm=IteratorData(i); l=GETLINE(tm->line); if (tm->apply) { if (right) s=l->sr; else s=l->sl; s->x=off; } off=(off+Len(l->v[0]->v.x,l->v[0]->v.y,l->v[1]->v.x,l->v[1]->v.y))%tw; i=IteratorNext(i); } } static int CreateSteps(int i_from, int i_to) { EditSect *s; EditLine *from,*to; DirName upper,lower,middle,floor,ceiling; int s_flag; int type; VIDOOM_TRACE; from=GETLINE(i_from); to=GETLINE(i_to); if ((Len(from->v[0]->v.x,from->v[0]->v.y, from->v[1]->v.x,from->v[1]->v.y)==0)|| (Len(to->v[0]->v.x,to->v[0]->v.y,to->v[1]->v.x,to->v[1]->v.y)==0)) { GuiInfoBox("ERROR","Lines must be non-zero length"); return(FALSE); } if (((from->l.from==to->l.from)&&(to->l.to==to->l.to))|| ((from->l.from==to->l.to)&&(to->l.to==to->l.from))) { GuiInfoBox("ERROR","Lines cannot share a vertex"); return(FALSE); } if (!(LinesCrossV(from->v[0],to->v[0],from->v[1],to->v[1]))) { step.swap=FALSE; SetPoint(step.vf[0],from->v[0]); SetPoint(step.vf[1],from->v[1]); SetPoint(step.vt[0],to->v[0]); SetPoint(step.vt[1],to->v[1]); } else { step.swap=TRUE; SetPoint(step.vf[0],from->v[0]); SetPoint(step.vf[1],from->v[1]); SetPoint(step.vt[0],to->v[1]); SetPoint(step.vt[1],to->v[0]); } step.x=ms.x; step.y=ms.y; step.from=from; step.to=to; step.no=1; step.circular=FALSE; step.side=0; s=GETSECT(from->sr->sector); step.from_c=s->s.ceiling; step.from_f=s->s.floor; s=GETSECT(to->sr->sector); step.to_c=s->s.ceiling; step.to_f=s->s.floor; CalcSteps(); if ((PickPoint("Create steps",&step.x,&step.y,PickDraw,PickKey,PickInfo))&& (ChooseSectorStyle(&s_flag,upper,middle,lower,floor,ceiling))&& ((type=SelectSector(hexen_mode))!=SECTOR_NULLID)) { Object o; EditLine *el; EditVert *ev[2][MAX_STEPS+2]; EditSect *s; Sidedef *ns; TextureMatch tm; List side_sl[2],side_sr[2]; int n; int v[2][MAX_STEPS+2]; int do_ceiling; double fh,ch; int prev_fh,prev_ch; int in_sect[MAX_STEPS],sect,prev_sect; int nl,flags; int offy_c,offy_f,tw,th; int f,r; /* Create lists for mainting lines for afterward texture alignment */ for(f=0;f<2;f++) { side_sl[f]=ListNew(sizeof(TextureMatch)); side_sr[f]=ListNew(sizeof(TextureMatch)); } if ((do_ceiling=GUI_yesno("Move ceiling with steps?"))) ch=step.from_c+step.step_c; else ch=MAX(step.to_c,step.from_c); fh=step.from_f+step.step_f; tw=CalcTextureWidth(upper,middle,lower); TextureSize(middle,NULL,&th); /* Create/get all the vertexes needed */ for(f=0;f<=step.no+1;f++) { if (f==0) { v[0][f]=step.from->l.from; v[1][f]=step.from->l.to; ev[0][f]=step.from->v[0]; ev[1][f]=step.from->v[1]; } else if (f==step.no+1) { if (step.swap) { v[1][f]=step.to->l.from; v[0][f]=step.to->l.to; ev[1][f]=step.to->v[0]; ev[0][f]=step.to->v[1]; } else { v[0][f]=step.to->l.from; v[1][f]=step.to->l.to; ev[0][f]=step.to->v[0]; ev[1][f]=step.to->v[1]; } } else for(r=0;r<2;r++) { ev[r][f]=Grab(sizeof(EditVert)); ev[r][f]->v.x=step.p[r][f].x; ev[r][f]->v.y=step.p[r][f].y; ev[r][f]->l=ListNew(sizeof(int)); o.data=ev[r][f]; o.select=SELECT_NONE; v[r][f]=MapSize(vertex); MapAdd(vertex,-1,&o); } /* While we're here, get all the containing sectors too */ in_sect[f]=SectorInBox(step.p[0][f],step.p[1][f+1]); } /* Delete current left sidedefs on the anchor lines */ if (step.from->l.left!=-1) DeleteLeftSidedef(step.from); if (step.to->l.left!=-1) DeleteLeftSidedef(step.to); /* Set up the flags for the 1st line and all the previous sector and height vars */ prev_fh=step.from_f; prev_ch=step.from_c; prev_sect=step.from->sr->sector; offy_c=0; offy_f=0; /* Create the steps up to the last one */ for(f=0;f<=step.no;f++) { if (in_sect[f]!=-1) s=GETSECT(in_sect[f]); else s=NULL; sect=CreateUnboundSector (type,(int)fh,(int)ch,default_light_level,0,floor,ceiling); /* Create the linedef at the start of the step. For the 1st step we adjust the original linedef. Otherwise create a new one */ if (!f) { el=step.from; el->l.flags|=side2_mask; el->l.flags&=~block_mask; ns=Grab(sizeof(Sidedef)); ns->x=0; ns->y=0; el->l.flags|=upper_peg_mask; el->l.flags&=~lower_peg_mask; if (prev_ch>(int)ch) { strcpy(el->sr->upper,upper); strcpy(ns->upper,empty_texture); } else if (prev_ch<(int)ch) { strcpy(el->sr->upper,empty_texture); strcpy(ns->upper,upper); } else { strcpy(el->sr->upper,empty_texture); strcpy(ns->upper,empty_texture); } if (prev_fh<(int)fh) { strcpy(el->sr->lower,lower); strcpy(ns->lower,empty_texture); } else if (prev_fh>(int)fh) { strcpy(el->sr->lower,empty_texture); strcpy(ns->lower,lower); } else { strcpy(el->sr->lower,empty_texture); strcpy(ns->lower,empty_texture); } strcpy(el->sr->middle,empty_texture); strcpy(ns->middle,empty_texture); ns->sector=sect; o.data=ns; o.select=SELECT_NONE; n=MapSize(sidedef); MapAdd(sidedef,n,&o); el->sl=ns; el->l.left=n; } else { flags=side2_mask|upper_peg_mask; nl=CreateNewLinedef (v[0][f],v[1][f],flags,normal_linedef,0,TRUE, 0,0,prev_sect, (prev_ch > (int)ch) ? upper : empty_texture, empty_texture, (prev_fh < (int)fh) ? lower : empty_texture, 0,0,sect, (prev_ch < (int)ch) ? upper : empty_texture, empty_texture, (prev_fh > (int)fh) ? lower : empty_texture); /* Add the new linedef to the vertex lists */ IntListUniqAdd(ev[0][f]->l,nl); IntListUniqAdd(ev[1][f]->l,nl); } /* Create the linedefs for the walls */ if (th) { offy_f-=(int)fh-prev_fh; while (offy_f<0) offy_f+=th; offy_f%=th; } if (do_ceiling) if (th) { offy_c-=(int)ch-prev_ch; while (offy_c<0) offy_c+=th; offy_c%=th; } for(r=0;r<2;r++) { if (in_sect[f]==-1) { int offy; flags=block_mask; if (!do_ceiling) { flags|=upper_peg_mask; offy=0; } else offy=offy_c; nl=CreateNewLinedef(v[r][f+r],v[r][f+(r^1)], flags,normal_linedef,0,FALSE, 0,offy,sect, empty_texture, middle, empty_texture, 0,0,-1, empty_texture, empty_texture, empty_texture); tm.line=nl; if (r==0) { tm.apply=TRUE; ListAppend(side_sr[r],&tm); tm.apply=FALSE; ListInsert(side_sl[r],&tm); } else { tm.apply=TRUE; ListInsert(side_sr[r],&tm); tm.apply=FALSE; ListAppend(side_sl[r],&tm); } } else { int offy_l,offy_r; flags=side2_mask; flags=side2_mask|lower_peg_mask|upper_peg_mask; if ((int)ch>s->s.ceiling) offy_r=offy_c; else if ((int)fhs.floor) offy_r=offy_c; else offy_r=0; offy_l=0; nl=CreateNewLinedef(v[r][f+r],v[r][f+(r^1)], flags,normal_linedef,0,TRUE, 0,offy_r,sect, ((int)ch > s->s.ceiling) ? upper : empty_texture, empty_texture, ((int)fh < s->s.floor) ? lower : empty_texture, 0,offy_l,in_sect[f], ((int)ch < s->s.ceiling) ? upper : empty_texture, empty_texture, ((int)fh > s->s.floor) ? lower : empty_texture); tm.line=nl; if (r==0) { tm.apply=TRUE; ListAppend(side_sr[r],&tm); tm.apply=TRUE; ListInsert(side_sl[r],&tm); } else { tm.apply=TRUE; ListInsert(side_sr[r],&tm); tm.apply=TRUE; ListAppend(side_sl[r],&tm); } } /* Add the new linedef to the vertex lists */ IntListUniqAdd(ev[r][f]->l,nl); IntListUniqAdd(ev[r][f+1]->l,nl); } prev_fh=(int)fh; prev_ch=(int)ch; fh+=step.step_f; if (do_ceiling) ch+=step.step_c; prev_sect=sect; } /* Adjust the end linedef anchor */ el=step.to; el->l.flags|=side2_mask; el->l.flags&=~block_mask; ns=Grab(sizeof(Sidedef)); ns->x=0; ns->y=0; if ((prev_ch!=step.to_c)&&(s_flag&SSTYLE_UPPER_PEG)) el->l.flags|=upper_peg_mask; else if (!(s_flag&SSTYLE_LEAVE_PEG)) el->l.flags&=~upper_peg_mask; if ((prev_fh!=step.to_f)&&(s_flag&SSTYLE_LOWER_PEG)) el->l.flags|=lower_peg_mask; else if (!(s_flag&SSTYLE_LEAVE_PEG)) el->l.flags&=~lower_peg_mask; if (prev_chsr->upper,upper); strcpy(ns->upper,empty_texture); } else if (prev_ch>step.to_c) { strcpy(el->sr->upper,empty_texture); strcpy(ns->upper,upper); } else { strcpy(el->sr->upper,empty_texture); strcpy(ns->upper,empty_texture); } if (prev_fh>step.to_f) { strcpy(el->sr->lower,lower); strcpy(ns->lower,empty_texture); } else if (prev_fhsr->lower,empty_texture); strcpy(ns->lower,lower); } else { strcpy(el->sr->lower,empty_texture); strcpy(ns->lower,empty_texture); } strcpy(el->sr->middle,empty_texture); strcpy(ns->middle,empty_texture); ns->sector=prev_sect; o.data=ns; o.select=SELECT_NONE; n=MapSize(sidedef); MapAdd(sidedef,n,&o); el->sl=ns; el->l.left=n; /* Calculate the texture offsets for the walls */ for(f=0;f<2;f++) { ApplyTextureOffset(side_sl[f],tw,FALSE); ApplyTextureOffset(side_sr[f],tw,TRUE); ListClear(side_sl[f]); ListClear(side_sr[f]); } SectorCalcContainingAll(); return(TRUE); } else return(FALSE); } /* ---------------------------------------- LINEDEF VECTOR MAP UTILS */ /* Note this is repeated, rather than shared with the sector vector map to save any unexpected later interactions */ static int vno=0; static char *vmap=NULL; static void InitVMAP(void) { VIDOOM_TRACE; if (vnosr->sector; if (!(s=GETSECT(sec_no))) return(ll); vert_no=l->l.to; /* If the start LINEDEF is 2-sided and wholy within the sector, we only track along connected 2-sided lines in the same sector. If the start line is 1-sided then we allow both 1 and 2 sided LINEDEFs in the resulting list, but only 2 sided LINEDEFs that just have this sector on one side of the line. To make this easier we mark the LMAP entries for those lines we wish to ignore before starting. */ if ((l->sl)&&(l->sl->sector==sec_no)) { i=ListIterator(s->all); while(i) { memcpy(&l,IteratorData(i),sizeof(l)); if (!l->sl) lmap[l->no]=TRUE; else if (!((l->sl->sector==sec_no)&&(l->sr->sector==sec_no))) lmap[l->no]=TRUE; i=IteratorNext(i); } } else { i=ListIterator(s->all); while(i) { memcpy(&l,IteratorData(i),sizeof(l)); if ((l->sl)&&(l->sl->sector==sec_no)&&(l->sr->sector==sec_no)) lmap[l->no]=TRUE; i=IteratorNext(i); } } done=FALSE; while(!done) { done=TRUE; i=ListIterator(s->all); while(i) { memcpy(&l,IteratorData(i),sizeof(l)); f=l->no; if (!lmap[f]) { /* We need two seperate checks, as obviously left sidedefs pointing into the sector will need TO, rather than FROM, checking */ if ((l->sr->sector==sec_no)&&(l->l.from==vert_no)) { lmap[f]=TRUE; ListAppend(ll,&f); done=FALSE; vert_no=l->l.to; } else if ((l->sl)&&(l->sl->sector==sec_no)&&(l->l.to==vert_no)) { lmap[f]=TRUE; ListAppend(ll,&f); done=FALSE; vert_no=l->l.from; } } i=IteratorNext(i); } } return(ll); } /* ---------------------------------------- LINEDEF MERGING */ void CheckMergeLinedef(EditVert *v) { Iterator i; int *f; int r; EditLine *ol,*cl; int same_dir; int merge; VIDOOM_TRACE; i=ListIterator(v->l); while(i) { f=IteratorData(i); ol=GETLINE(*f); for(r=0;rl.from==ol->l.from)&&(cl->l.to==ol->l.to))|| ((cl->l.from==ol->l.to)&&(cl->l.to==ol->l.from))) { if ((cl->l.from==ol->l.from)&&(cl->l.to==ol->l.to)) same_dir=TRUE; else same_dir=FALSE; if(merge_linedef==MERGE_ASK) merge=YesNo("Linedefs %d and %d overlap. Merge?",*f,r); else merge=TRUE; if (merge) { if (same_dir) { if (cl->l.left!=-1) { DeleteLeftSidedef(ol); ol->l.left=cl->l.left; ol->sl=GETSIDE(ol->l.left); ol->l.flags|=side2_mask; if (auto_block_linedefs) ol->l.flags&=~block_mask; } } else { DeleteLeftSidedef(ol); ol->l.left=cl->l.right; ol->sl=GETSIDE(ol->l.left); ol->l.flags|=side2_mask; if (auto_block_linedefs) ol->l.flags&=~block_mask; } DeleteLinedef(r); } } } i=IteratorNext(i); } } /* ---------------------------------------- LINEDEF CREATION */ int CreateNewLinedef(int from, int to, int flags, int type, int tag, int two_sided, int sr_ox, int sr_oy, int sr_sector, DirName sr_upper, DirName sr_middle, DirName sr_lower, int sl_ox, int sl_oy, int sl_sector, DirName sl_upper, DirName sl_middle, DirName sl_lower) { Object o; EditLine *l; Sidedef *s; int ln,srn,sln,f; VIDOOM_TRACE; ln=MapSize(linedef); srn=MapSize(sidedef); if (two_sided) sln=srn+1; else sln=-1; o.select=SELECT_NONE; s=Grab(sizeof(Sidedef)); s->x=sr_ox; s->y=sr_oy; s->sector=sr_sector; strcpy(s->upper,sr_upper); strcpy(s->middle,sr_middle); strcpy(s->lower,sr_lower); o.data=s; MapAdd(sidedef,srn,&o); if (sln!=-1) { s=Grab(sizeof(Sidedef)); s->x=sl_ox; s->y=sl_oy; s->sector=sl_sector; strcpy(s->upper,sl_upper); strcpy(s->middle,sl_middle); strcpy(s->lower,sl_lower); o.data=s; MapAdd(sidedef,sln,&o); } l=Grab(sizeof(EditLine)); l->no=ln; l->l.from=from; l->l.to=to; l->l.flags=flags; l->l.type=type; l->l.tag=tag; for(f=0;f<5;f++) l->l.args[f]=0; l->v[0]=GETVERT(l->l.from); l->v[1]=GETVERT(l->l.to); l->l.right=srn; l->l.left=sln; l->sr=GETSIDE(srn); if (sln!=-1) l->sl=GETSIDE(sln); else l->sl=NULL; if (edit_mode==LINEDEF_MODE) switch(insert_select) { case HOVER_NONE: o.select=SELECT_NONE; break; case HOVER_ADD: case HOVER_SINGLE: o.select=SELECT_SELECTED; break; } else o.select=SELECT_NONE; LineCalcBounding(l); o.data=l; MapAdd(linedef,ln,&o); if (edit_mode==LINEDEF_MODE) switch(insert_select) { case HOVER_NONE: break; case HOVER_ADD: ListAppend(selected,&ln); break; case HOVER_SINGLE: ClearSelection(); o.select=SELECT_SELECTED; ListAppend(selected,&ln); FullRedraw(); break; } return(ln); } /* ---------------------------------------- HEXEN TAG RESOLVING */ void SetHexenLinedefTag(EditLine *l) { char *nm; char *a[5]; int f; l->l.tag=0; nm=SpecialName(l->l.type,a); if (!STREQ(nm,"UNKNOWN")) for(f=0;f<5;f++) if (STREQ(a[f],"tag")) { l->l.tag=l->l.args[f]; f=6; } } /* ---------------------------------------- GENERIC LINEDEF FUNCS */ int PositionOnObject_LINEDEF(int x,int y,void *data) { EditLine *l; VIDOOM_TRACE; l=data; /* if (scale<4) box=BOXBOUND_FUZZY(x,y,l->min_x,l->min_y,l->max_x,l->max_y,2); else box=BOXBOUND_FUZZY(x,y,l->min_x,l->min_y,l->max_x,l->max_y,scale/2); if (!box) return(FALSE); */ if(LinesCross(x-(scale*linedef_select),y,x+(scale*linedef_select),y, l->v[0]->v.x,l->v[0]->v.y, l->v[1]->v.x,l->v[1]->v.y)) return(TRUE); return(LinesCross(x,y-(scale*linedef_select),x,y+(scale*linedef_select), l->v[0]->v.x,l->v[0]->v.y, l->v[1]->v.x,l->v[1]->v.y)); } void SelectBox_LINEDEF(int x1,int y1,int x2,int y2) { Object *o; EditLine *l; int f; VIDOOM_TRACE; for(f=0;fdata)) { if ((o->select!=SELECT_SELECTED)&& (BOXBOUND(l->v[0]->v.x,l->v[0]->v.y,x1,y1,x2,y2))&& (BOXBOUND(l->v[1]->v.x,l->v[1]->v.y,x1,y1,x2,y2))) { SetSelect(f,SELECT_SELECTED); ListAppend(selected,&f); } } } } void SelectByType_LINEDEF(void) { Object *o; EditLine *l; int id; int f; if (hexen_mode) { if ((id=SelectSpecial())==SPECIAL_NULLID) id=LINEDEF_NULLID; } else id=SelectLinedef(); if (id!=LINEDEF_NULLID) { ClearSelection(); for(f=0;fdata)&&(l->l.type==id)) { SetSelect(f,SELECT_SELECTED); ListAppend(selected,&f); } } } void DrawObject_LINEDEF(void *data, int selmode) { static List hilite=NULL; static int drawn=FALSE; Iterator i; EditLine *l; EditLine *sl=NULL; EditSect *s; Object *o; int f; VIDOOM_TRACE; if (!hilite) hilite=ListNew(sizeof(int)); l=data; /* If the current object has changed, remove the highlighted tag objects */ if ((current==-1)&&(drawn)) { drawn=FALSE; RemoveHighlights(hilite); ListEmpty(hilite); } /* Draw tagged sectors */ if ((tag_highlight)&&(current==l->no)) { RemoveHighlights(hilite); ListEmpty(hilite); if (l->l.tag) { drawn=TRUE; for(f=0;fs.tag==l->l.tag)&&(SectorOnDisplay(s))) { i=ListIterator(s->all); while(i) { memcpy(&sl,IteratorData(i),sizeof(sl)); o=MapElem(linedef,sl->no); if (o->select==SELECT_NONE) { ListAppend(hilite,&(sl->no)); DrawLinedef(sl,TAGCOL); } i=IteratorNext(i); } } } } /* Only draw the line if it's not been drawn tagged or is selected. */ if ((!tag_highlight)||(selmode==SELECT_SELECTED)|| (!InIntList(hilite,l->no))) DrawLinedef(data,SelColour(selmode)); } void DrawObjectInfo_LINEDEF(void) { EditLine *l; char *p; char *nm=NULL; char *a[5]; VIDOOM_TRACE; if (!draw_current_info) return; if ((current!=-1)&&((l=GETLINE(current)))) { if (hexen_mode) nm=SpecialName(l->l.type,a); if (show_full_linedef_info) { if (hexen_mode) GuiDrawInfoBox("LINEDEF",GUI_CENTRE,GUI_FLUSH_LOWER,FALSE, "Linedef No : %-5d|" "Flags : %s|" "Special : %-35.35s|" "%-10.10s : %-3d|" "%-10.10s : %-3d|" "%-10.10s : %-3d|" "%-10.10s : %-3d|" "%-10.10s : %-3d", current, FlagText(hexen_mode,LINEDEF_FLAGS, l->l.flags), nm, (a[0] ? a[0] : "N/A"),l->l.args[0], (a[1] ? a[1] : "N/A"),l->l.args[1], (a[2] ? a[2] : "N/A"),l->l.args[2], (a[3] ? a[3] : "N/A"),l->l.args[3], (a[4] ? a[4] : "N/A"),l->l.args[4]); else GuiDrawInfoBox("LINEDEF",GUI_CENTRE,GUI_FLUSH_LOWER,FALSE, "Linedef No : %-5d|" "Flags : %s|" "Type [%4.4x]: %-35.35s|" "Tag : %-5d", current, FlagText(hexen_mode, LINEDEF_FLAGS,l->l.flags), l->l.type, (p=GenLineName(hexen_mode,l->l.type, LinedefName)) ? p:LinedefName(l->l.type), l->l.tag); } else { if (hexen_mode) { GuiDrawInfoBox("LINEDEF",GUI_CENTRE,GUI_FLUSH_LOWER,FALSE, "Linedef No : %-5d|" "Flags : %s|" "Special : %-20.20s|" "%-10.10s : %-3d|" "%-10.10s : %-3d|" "%-10.10s : %-3d|" "%-10.10s : %-3d|" "%-10.10s : %-3d", current, FlagText(hexen_mode,LINEDEF_FLAGS, l->l.flags), nm, (a[0] ? a[0] : "N/A"),l->l.args[0], (a[1] ? a[1] : "N/A"),l->l.args[1], (a[2] ? a[2] : "N/A"),l->l.args[2], (a[3] ? a[3] : "N/A"),l->l.args[3], (a[4] ? a[4] : "N/A"),l->l.args[4]); } else { if (GenLineClass(hexen_mode,l->l.type,LinedefClass)) GuiDrawInfoBox("LINEDEF",GUI_CENTRE,GUI_FLUSH_LOWER,FALSE, "Linedef No : %-5d|" "Flags : %s|" "Type [%4.4x]: %-14.14s [Gen]|" "Tag : %-5d", current, FlagText(hexen_mode,LINEDEF_FLAGS, l->l.flags), l->l.type, GenLineClass(hexen_mode, l->l.type,LinedefClass), l->l.tag); else GuiDrawInfoBox("LINEDEF",GUI_CENTRE,GUI_FLUSH_LOWER,FALSE, "Linedef No : %-5d|" "Flags : %s|" "Type [%4.4x]: %-20.20s|" "Tag : %-5d", current, FlagText(hexen_mode,LINEDEF_FLAGS, l->l.flags), l->l.type, LinedefClass(l->l.type), l->l.tag); } } GuiDrawInfoBox("RIGHT SIDEDEF",GUI_FLUSH_RIGHT,GUI_FLUSH_LOWER,FALSE, "Offset : %d,%d|" "Upper : %-10s|" "Middle : %-10s|" "Lower : %-10s|" "Sector : %d", l->sr->x,l->sr->y, l->sr->upper,l->sr->middle,l->sr->lower, l->sr->sector); if (l->sl) GuiDrawInfoBox("LEFT SIDEDEF",GUI_FLUSH_LEFT,GUI_FLUSH_LOWER,FALSE, "Offset : %d,%d|" "Upper : %-10s|" "Middle : %-10s|" "Lower : %-10s|" "Sector : %d", l->sl->x,l->sl->y, l->sl->upper,l->sl->middle,l->sl->lower, l->sl->sector); else GuiDrawInfoBox("NO LEFT SIDEDEF",GUI_FLUSH_LEFT, GUI_FLUSH_LOWER,FALSE, " |" " %-10s|" " |" " |" " ",""); } else { if (show_full_linedef_info) if (hexen_mode) GuiDrawInfoBox("LINEDEF",GUI_CENTRE,GUI_FLUSH_LOWER,FALSE, "Linedef No : -|" "Flags : -|" "Special : %35s|" "- : -|" "- : -|" "- : -|" "- : -|" "- : -",""); else GuiDrawInfoBox("LINEDEF",GUI_CENTRE,GUI_FLUSH_LOWER,FALSE, "Linedef No : -|" "Flags : -|" "Type : %35s|" "Tag : -",""); else if (hexen_mode) GuiDrawInfoBox("LINEDEF",GUI_CENTRE,GUI_FLUSH_LOWER,FALSE, "Linedef No : -|" "Flags : -|" "Special : %20s|" "- : -|" "- : -|" "- : -|" "- : -|" "- : -",""); else GuiDrawInfoBox("LINEDEF",GUI_CENTRE,GUI_FLUSH_LOWER,FALSE, "Linedef No : -|" "Flags : -|" "Type : %20s|" "Tag : -",""); GuiDrawInfoBox("RIGHT SIDEDEF",GUI_FLUSH_RIGHT,GUI_FLUSH_LOWER,FALSE, " |" " %-10s|" " |" " |" " ",""); GuiDrawInfoBox("LEFT SIDEDEF",GUI_FLUSH_LEFT,GUI_FLUSH_LOWER,FALSE, " |" " %-10s|" " |" " |" " ",""); } } void DrawObjectHeader_LINEDEF(void) { if (tag_highlight) GFX_print(0,FH,WHITE,"Tag highlight: ON"); else GFX_print(0,FH,WHITE,"Tag highlight: OFF"); } void MoveObject_LINEDEF(void) { GFXEvent ev; int omx,omy; int dmx,dmy; int done; Iterator i; Iterator i2; EditLine *l; List orig; int *f,r; Point p; unsigned char *vmap; int cancel; VIDOOM_TRACE; omx=SnapX(XToMap(ms.x)); omy=SnapY(YToMap(ms.y)); GFX_bounce(); done=FALSE; cancel=FALSE; orig=ListNew(sizeof(Point)); vmap=Grab(MapSize(vertex)); i=ListIterator(selected); while(i) { f=IteratorData(i); l=GETLINE(*f); p.x=l->v[0]->v.x; p.y=l->v[0]->v.y; ListAppend(orig,&p); p.x=l->v[1]->v.x; p.y=l->v[1]->v.y; ListAppend(orig,&p); i=IteratorNext(i); } while(!done) { GuiDrawInfoBox("Move LINEDEF",GUI_FLUSH_RIGHT,GUI_FLUSH_LOWER,FALSE, "Left mouse button to place|ESC to cancel"); GFX_redraw(); GFX_await_input_full(&ev); switch(ev.type) { case GFX_KEY_EVENT: HandleMoveKey(ev.key,FALSE); /* Cancel the move */ if (ev.key.code==GFX_ESC) { cancel=TRUE; i=ListIterator(selected); i2=ListIterator(orig); while(i2) { f=IteratorData(i); l=GETLINE(*f); memcpy(&p,IteratorData(i2),sizeof(Point)); l->v[0]->v.x=p.x; l->v[0]->v.y=p.y; i2=IteratorNext(i2); memcpy(&p,IteratorData(i2),sizeof(Point)); l->v[1]->v.x=p.x; l->v[1]->v.y=p.y; i2=IteratorNext(i2); i=IteratorNext(i); } done=TRUE; dmx=0; dmy=0; } else { dmx=SnapX(XToMap(ms.x))-omx; dmy=SnapY(YToMap(ms.y))-omy; } break; case GFX_MOUSE_EVENT: memcpy(&ms,&ev.mouse,sizeof(ev.mouse)); if (ms.b&GFX_BUTLEFT) done=TRUE; dmx=SnapX(XToMap(ms.x))-omx; dmy=SnapY(YToMap(ms.y))-omy; break; default: dmx=0; dmy=0; break; } if ((dmx)||(dmy)) { for(r=0;rl.from]) { l->v[0]->v.x+=dmx; l->v[0]->v.y+=dmy; vmap[l->l.from]=TRUE; } if (!vmap[l->l.to]) { l->v[1]->v.x+=dmx; l->v[1]->v.y+=dmy; vmap[l->l.to]=TRUE; } i=IteratorNext(i); } omx=SnapX(XToMap(ms.x)); omy=SnapY(YToMap(ms.y)); FullRedraw(); } } ListClear(orig); Release(vmap); if ((clear_on_move)&&(!cancel)) ClearSelection(); FullRedraw(); } void RotateObject_LINEDEF(double angle) { int cx,cy; int x,y; int *f; Iterator i; EditLine *l; VIDOOM_TRACE; InitVMAP(); LineSelectionCentre(&cx,&cy); i=ListIterator(selected); while(i) { f=IteratorData(i); l=GETLINE(*f); if (!vmap[l->l.from]) { vmap[l->l.from]=TRUE; x=l->v[0]->v.x; y=l->v[0]->v.y; Rotate(cx,cy,&x,&y,angle); l->v[0]->v.x=x; l->v[0]->v.y=y; } if (!vmap[l->l.to]) { vmap[l->l.to]=TRUE; x=l->v[1]->v.x; y=l->v[1]->v.y; Rotate(cx,cy,&x,&y,angle); l->v[1]->v.x=x; l->v[1]->v.y=y; } i=IteratorNext(i); } } void ScaleObject_LINEDEF(double scale) { int cx,cy; int x,y; int *f; Iterator i; EditLine *l; VIDOOM_TRACE; LineSelectionCentre(&cx,&cy); i=ListIterator(selected); while(i) { f=IteratorData(i); l=GETLINE(*f); x=l->v[0]->v.x; y=l->v[0]->v.y; Scale(cx,cy,&x,&y,scale); l->v[0]->v.x=x; l->v[0]->v.y=y; x=l->v[1]->v.x; y=l->v[1]->v.y; Scale(cx,cy,&x,&y,scale); l->v[1]->v.x=x; l->v[1]->v.y=y; i=IteratorNext(i); } } void SetTagObject_LINEDEF(int tag) { int *f; Iterator i; EditLine *l; VIDOOM_TRACE; if (hexen_mode) return; i=ListIterator(selected); while(i) { f=IteratorData(i); l=GETLINE(*f); l->l.tag=tag; i=IteratorNext(i); } } void LocateObject_LINEDEF(void *obj) { int cx,cy; EditLine *l; VIDOOM_TRACE; l=obj; cx=l->min_x+(l->max_x-l->min_x)/2; cy=l->min_y+(l->max_y-l->min_y)/2; ox=cx-scale*SCRW/2; oy=cy+scale*SCRH/2; } void ObjectInsert_LINEDEF(void) { int type; int fl,two; DirName sr_upper,sr_lower,sr_middle; DirName sl_upper,sl_lower,sl_middle; int ok; int from,to; int nl; EditVert *v; VIDOOM_TRACE; if (MapSize(vertex)<2) GuiInfoBox("ERROR","Need more that 2 vertices|to make a line"); else { vertex_dialog[D_VERTEX_FROM].data.i=MapSize(vertex)-2; vertex_dialog[D_VERTEX_TO].data.i=MapSize(vertex)-1; ok=GUI_dialog("Create LINEDEF",D_VERTEX_NO,vertex_dialog); if (ok) { from=vertex_dialog[D_VERTEX_FROM].data.i; to=vertex_dialog[D_VERTEX_TO].data.i; if ((from>=MapSize(vertex))||(from<0)||(!GETVERT(from))) { GuiInfoBox("ERROR","%d (from) is an invalid vertex",from); ok=FALSE; } else if ((to>=MapSize(vertex))||(to<0)||(!GETVERT(to))) { GuiInfoBox("ERROR","%d (to) is an invalid vertex",to); ok=FALSE; } } if (ok) if (GetLinedefValues(TRUE,0,0,0,0, &type,&fl,&two, sr_upper,sr_middle,sr_lower, sl_upper,sl_middle,sl_lower)) { nl=CreateNewLinedef(from,to, fl,type,0,two, 0,0,0, sr_upper,sr_middle,sr_lower, 0,0,0, sl_upper,sl_middle,sl_lower); v=GETVERT(from); IntListUniqAdd(v->l,from); v=GETVERT(to); IntListUniqAdd(v->l,from); FullRedraw(); } } } void ObjectDelete_LINEDEF(void) { Iterator i; int *f; VIDOOM_TRACE; i=ListIterator(selected); while(i) { f=IteratorData(i); DeleteLinedef(*f); i=IteratorNext(i); } SectorCalcContainingAll(); ClearSelection(); FullRedraw(); } void ObjectMenu_LINEDEF(void) { static int ax=0; static int ay=0; EditLine *l; int f,*s; Iterator i; int cancel; int opt; VIDOOM_TRACE; s=NULL; cancel=FALSE; GFX_redraw(); if (hexen_mode) opt=GUI_menu("Linedef/sidedef",ms.x,ms.y, linedef_popup_hexen,GUI_CANCEL); else opt=GUI_menu("Linedef/sidedef",ms.x,ms.y,linedef_popup,GUI_CANCEL); switch(opt) { case TM_TYPE: { f=SelectLinedef(); if (f!=LINEDEF_NULLID) { i=ListIterator(selected); while(i) { s=IteratorData(i); l=GETLINE(*s); l->l.type=f; i=IteratorNext(i); } FullRedraw(); } break; } case TM_SPECIAL: { Short id; id=(Short)SelectSpecial(); if (id!=SPECIAL_NULLID) { i=ListIterator(selected); while(i) { s=IteratorData(i); l=GETLINE(*s); l->l.type=id; SetHexenLinedefTag(l); i=IteratorNext(i); } FullRedraw(); } break; } case TM_GEN_TYPE: if (GenLineNoClasses(hexen_mode)) { l=SelHead(); f=SelectGenLine(hexen_mode,l->l.type); if (f!=GENLINE_NULLID) { i=ListIterator(selected); while(i) { s=IteratorData(i); l=GETLINE(*s); l->l.type=f; i=IteratorNext(i); } FullRedraw(); } } else GuiInfoBox("ERROR","No generalised linedefs defined|in config"); break; case TM_VAL: { static int id; if (GetNumber("Enter LINEDEF type",&id)) { i=ListIterator(selected); while(i) { s=IteratorData(i); l=GETLINE(*s); l->l.type=id; i=IteratorNext(i); } FullRedraw(); } } break; case TM_FLAGS: l=SelHead(); f=l->l.flags; if (SelectFlags(hexen_mode,LINEDEF_FLAGS,&f)) { int new_side; List new; new=ListNew(sizeof(int)); new_side=FALSE; i=ListIterator(selected); while(i) { int new_f; new_f=f; s=IteratorData(i); l=GETLINE(*s); /* If the line was 2 sided, and is now one - removed the left sidedef */ if ((l->l.flags&side2_mask)&&(!(new_f&side2_mask))) { DeleteLeftSidedef(l); if (auto_block_linedefs) new_f|=block_mask; } /* If the line was 1 sided, and is now two - add the left sidedef */ if ((!(l->l.flags&side2_mask))&&(new_f&side2_mask)) { Object o; Sidedef *ns; int n; ns=Grab(sizeof(Sidedef)); ns->x=0; ns->y=0; strcpy(ns->upper,empty_texture); strcpy(ns->middle,empty_texture); strcpy(ns->lower,empty_texture); ns->sector=-1; o.data=ns; o.select=SELECT_NONE; n=MapSize(sidedef); MapAdd(sidedef,n,&o); l->sl=ns; l->l.left=n; new_side=TRUE; ListAppend(new,s); if (auto_block_linedefs) new_f&=~block_mask; } l->l.flags=new_f; i=IteratorNext(i); } if (new_side) switch(new_2sided_select) { case NEWSELECT_NEVER: GuiInfoBox("NOTICE","Left SIDEDEFS have been added|" "to some or all LINEDEFS"); break; /* WARNING: This case cascades into the following one */ case NEWSELECT_ASK: if (!YesNo("New left SIDEDEFS created. " "Select those LINEDEFS?")) break; case NEWSELECT_SELECT: { Object *o; new_selection=TRUE; ClearSelection(); i=ListIterator(new); while(i) { s=IteratorData(i); o=MapElem(linedef,*s); SetSelect(*s,SELECT_SELECTED); ListAppend(selected,s); i=IteratorNext(i); } break; } } ListClear(new); FullRedraw(); } break; case TM_SWAP_SIDES: { int tmp; Sidedef *tmp_s; i=ListIterator(selected); GUI_start_yesno_all(); while(i) { s=IteratorData(i); l=GETLINE(*s); if (l->l.left!=-1) { tmp=l->l.left; tmp_s=l->sl; l->sl=l->sr; l->l.left=l->l.right; l->sr=tmp_s; l->l.right=tmp; tmp=l->l.from; l->l.from=l->l.to; l->l.to=tmp; l->v[0]=GETVERT(l->l.from); l->v[1]=GETVERT(l->l.to); } else if (YesNoAll("Swap one-sided linedefs?")) { tmp=l->l.from; l->l.from=l->l.to; l->l.to=tmp; l->v[0]=GETVERT(l->l.from); l->v[1]=GETVERT(l->l.to); } i=IteratorNext(i); } SectorCalcContainingAll(); FullRedraw(); break; } case TM_SPLIT_LINE: { List new; int no; Object *o; new=ListNew(sizeof(int)); i=ListIterator(selected); while(i) { s=IteratorData(i); no=SplitLinedef(*s); ListAppend(new,&no); i=IteratorNext(i); } i=ListIterator(new); while(i) { s=IteratorData(i); o=MapElem(linedef,*s); SetSelect(*s,SELECT_SELECTED); ListAppend(selected,s); i=IteratorNext(i); } ListClear(new); SectorCalcContainingAll(); FullRedraw(); break; } case TM_TRACK_LINE: { List new; Object *o; i=ListIterator(selected); while(i) { s=IteratorData(i); i=IteratorNext(i); } if ((new=TrackLinedef(*s))) { ClearSelection(); i=ListIterator(new); while(i) { s=IteratorData(i); o=MapElem(linedef,*s); SetSelect(*s,SELECT_SELECTED); ListAppend(selected,s); i=IteratorNext(i); } ListClear(new); new_selection=TRUE; FullRedraw(); } break; } case TM_STEPS: if (ListSize(selected)!=2) GuiInfoBox("ERROR","Just select 2 linedefs|for this function"); else { int *l1,*l2; i=ListIterator(selected); l1=IteratorData(i); i=IteratorNext(i); l2=IteratorData(i); IteratorClear(i); cancel=CreateSteps(*l1,*l2); FullRedraw(); } break; case TM_OFFSET_R: l=SelHead(); offset_dialog[D_OFFSET_OFFX].data.i=l->sr->x; offset_dialog[D_OFFSET_OFFY].data.i=l->sr->y; if (GUI_dialog("Right offset",D_OFFSET_NO,offset_dialog)) { i=ListIterator(selected); while(i) { s=IteratorData(i); l=GETLINE(*s); if (l->sr) { l->sr->x=offset_dialog[D_OFFSET_OFFX].data.i; l->sr->y=offset_dialog[D_OFFSET_OFFY].data.i; } i=IteratorNext(i); } i=IteratorClear(i); FullRedraw(); } break; case TM_ADJUST_R: l=SelHead(); offset_dialog[D_OFFSET_OFFX].data.i=ax; offset_dialog[D_OFFSET_OFFY].data.i=ay; if (GUI_dialog("Right offset adjustment",D_OFFSET_NO,offset_dialog)) { ax=offset_dialog[D_OFFSET_OFFX].data.i; ay=offset_dialog[D_OFFSET_OFFY].data.i; i=ListIterator(selected); while(i) { s=IteratorData(i); l=GETLINE(*s); if (l->sr) { l->sr->x+=ax; l->sr->y+=ay; } i=IteratorNext(i); } i=IteratorClear(i); FullRedraw(); } break; case TM_SECTOR_R: l=SelHead(); sectorn_dialog[D_SECTORN_SECNO].data.i=l->sr->sector; if (GUI_dialog("Right sector",D_SECTORN_NO,sectorn_dialog)) { i=ListIterator(selected); while(i) { s=IteratorData(i); l=GETLINE(*s); if (l->sr) l->sr->sector=sectorn_dialog[D_SECTORN_SECNO].data.i; i=IteratorNext(i); } i=IteratorClear(i); SectorCalcContainingAll(); FullRedraw(); } break; case TM_UPPER_T_R: { DirName d; if ((GetTexture("Upper Right texture",d))) { i=ListIterator(selected); while(i) { s=IteratorData(i); l=GETLINE(*s); strcpy(l->sr->upper,d); i=IteratorNext(i); } FullRedraw(); } break; } case TM_MIDDLE_T_R: { DirName d; if ((GetTexture("Middle Right texture",d))) { i=ListIterator(selected); while(i) { s=IteratorData(i); l=GETLINE(*s); strcpy(l->sr->middle,d); i=IteratorNext(i); } FullRedraw(); } break; } case TM_LOWER_T_R: { DirName d; if ((GetTexture("Lower Right texture",d))) { i=ListIterator(selected); while(i) { s=IteratorData(i); l=GETLINE(*s); strcpy(l->sr->lower,d); i=IteratorNext(i); } FullRedraw(); } break; } case TM_ALIGN_R: { int tw; int ox; l=SelHead(); tw=CalcTextureWidth(l->sr->upper,l->sr->middle,l->sr->lower); if (tw==0) GuiInfoBox("ERROR","First selected linedef has no texture"); else { ox=l->sr->x; i=ListIterator(selected); while(i) { s=IteratorData(i); l=GETLINE(*s); l->sr->x=ox; ox=(ox+Len(l->v[0]->v.x,l->v[0]->v.y, l->v[1]->v.x,l->v[1]->v.y))%tw; i=IteratorNext(i); } FullRedraw(); } } break; case TM_TEXTURES_R: l=SelHead(); strcpy(textures_dialog[D_TEXTURE_U].data.s,l->sr->upper); strcpy(textures_dialog[D_TEXTURE_M].data.s,l->sr->middle); strcpy(textures_dialog[D_TEXTURE_L].data.s,l->sr->lower); if (GUI_dialog("Right textures",D_TEXTURES_NO,textures_dialog)) { i=ListIterator(selected); while(i) { s=IteratorData(i); l=GETLINE(*s); strcpy(l->sr->upper,textures_dialog[D_TEXTURE_U].data.s); strcpy(l->sr->middle,textures_dialog[D_TEXTURE_M].data.s); strcpy(l->sr->lower,textures_dialog[D_TEXTURE_L].data.s); i=IteratorNext(i); } i=IteratorClear(i); FullRedraw(); } break; case TM_OFFSET_L: l=SelHead(); if (l->sl) { offset_dialog[D_OFFSET_OFFX].data.i=l->sl->x; offset_dialog[D_OFFSET_OFFY].data.i=l->sl->y; } else { offset_dialog[D_OFFSET_OFFX].data.i=0; offset_dialog[D_OFFSET_OFFY].data.i=0; } if (GUI_dialog("Left offset",D_OFFSET_NO,offset_dialog)) { i=ListIterator(selected); while(i) { s=IteratorData(i); l=GETLINE(*s); if (l->sl) { l->sl->x=offset_dialog[D_OFFSET_OFFX].data.i; l->sl->y=offset_dialog[D_OFFSET_OFFY].data.i; } i=IteratorNext(i); } i=IteratorClear(i); FullRedraw(); } break; case TM_ADJUST_L: l=SelHead(); offset_dialog[D_OFFSET_OFFX].data.i=ax; offset_dialog[D_OFFSET_OFFY].data.i=ay; if (GUI_dialog("Left offset adjustment",D_OFFSET_NO,offset_dialog)) { ax=offset_dialog[D_OFFSET_OFFX].data.i; ay=offset_dialog[D_OFFSET_OFFY].data.i; i=ListIterator(selected); while(i) { s=IteratorData(i); l=GETLINE(*s); if (l->sl) { l->sl->x+=ax; l->sl->y+=ay; } i=IteratorNext(i); } i=IteratorClear(i); FullRedraw(); } break; case TM_SECTOR_L: l=SelHead(); if (l->sl) sectorn_dialog[D_SECTORN_SECNO].data.i=l->sl->sector; else sectorn_dialog[D_SECTORN_SECNO].data.i=-1; if (GUI_dialog("Left sector",D_SECTORN_NO,sectorn_dialog)) { i=ListIterator(selected); while(i) { s=IteratorData(i); l=GETLINE(*s); if (l->sl) l->sl->sector=sectorn_dialog[D_SECTORN_SECNO].data.i; i=IteratorNext(i); } i=IteratorClear(i); SectorCalcContainingAll(); FullRedraw(); } break; case TM_UPPER_T_L: { DirName d; if ((GetTexture("Upper Left texture",d))) { i=ListIterator(selected); while(i) { s=IteratorData(i); l=GETLINE(*s); if (l->sl) strcpy(l->sl->upper,d); i=IteratorNext(i); } FullRedraw(); } break; } case TM_MIDDLE_T_L: { DirName d; if ((GetTexture("Middle Left texture",d))) { i=ListIterator(selected); while(i) { s=IteratorData(i); l=GETLINE(*s); if (l->sl) strcpy(l->sl->middle,d); i=IteratorNext(i); } FullRedraw(); } break; } case TM_LOWER_T_L: { DirName d; if ((GetTexture("Lower Left texture",d))) { i=ListIterator(selected); while(i) { s=IteratorData(i); l=GETLINE(*s); if (l->sl) strcpy(l->sl->lower,d); i=IteratorNext(i); } FullRedraw(); } break; } case TM_ALIGN_L: { int tw; int ox; int ok; ok=TRUE; i=ListIterator(selected); while(i) { s=IteratorData(i); l=GETLINE(*s); if (!(l->sl)) ok=FALSE; i=IteratorNext(i); } if (!ok) GuiInfoBox("ERROR","Some linedefs with no left sidedef"); else { l=SelHead(); tw=CalcTextureWidth(l->sl->upper,l->sl->middle,l->sl->lower); if (tw==0) GuiInfoBox("ERROR","First selected linedef has no texture"); else { ox=l->sl->x; i=ListIterator(selected); while(i) { s=IteratorData(i); l=GETLINE(*s); l->sl->x=ox; ox=(ox+Len(l->v[0]->v.x,l->v[0]->v.y, l->v[1]->v.x,l->v[1]->v.y))%tw; i=IteratorNext(i); } FullRedraw(); } } } break; case TM_TEXTURES_L: l=SelHead(); if (l->sl) { strcpy(textures_dialog[D_TEXTURE_U].data.s,l->sl->upper); strcpy(textures_dialog[D_TEXTURE_M].data.s,l->sl->middle); strcpy(textures_dialog[D_TEXTURE_L].data.s,l->sl->lower); } else { strcpy(textures_dialog[D_TEXTURE_U].data.s,""); strcpy(textures_dialog[D_TEXTURE_M].data.s,""); strcpy(textures_dialog[D_TEXTURE_L].data.s,""); } if (GUI_dialog("Left textures",D_TEXTURES_NO,textures_dialog)) { i=ListIterator(selected); while(i) { s=IteratorData(i); l=GETLINE(*s); if (l->sl) { strcpy(l->sl->upper, textures_dialog[D_TEXTURE_U].data.s); strcpy(l->sl->middle, textures_dialog[D_TEXTURE_M].data.s); strcpy(l->sl->lower, textures_dialog[D_TEXTURE_L].data.s); } i=IteratorNext(i); } i=IteratorClear(i); FullRedraw(); } break; case TM_TAG: l=SelHead(); tag_dialog[D_TAG].data.i=l->l.tag; if (GUI_dialog("Tag",D_TAG_NO,tag_dialog)) { SetTagObject_LINEDEF(tag_dialog[D_TAG].data.i); FullRedraw(); } break; case TM_DELETE: ObjectDelete_LINEDEF(); break; case TM_MOVE: MoveObject_LINEDEF(); break; case TM_ARGS: { int a[5]; int id; int diff; l=SelHead(); id=l->l.type; i=ListIterator(selected); i=IteratorNext(i); diff=FALSE; while((i)&&(!diff)) { s=IteratorData(i); l=GETLINE(*s); if (l->l.type!=id) diff=TRUE; i=IteratorNext(i); } if ((diff)&&(YesNo("Selected linedefs have|different types| |" "Set individually?"))) { cancel=TRUE; i=ListIterator(selected); while(i) { s=IteratorData(i); l=GETLINE(*s); id=l->l.type; for(f=0;f<5;f++) a[f]=l->l.args[f]; if (SpecialArgDialog(NULL,id,a)) { for(f=0;f<5;f++) l->l.args[f]=a[f]; SetHexenLinedefTag(l); cancel=FALSE; } i=IteratorNext(i); } } else { l=SelHead(); for(f=0;f<5;f++) a[f]=l->l.args[f]; if (SpecialArgDialog(NULL,id,a)) { i=ListIterator(selected); while(i) { s=IteratorData(i); l=GETLINE(*s); for(f=0;f<5;f++) l->l.args[f]=a[f]; SetHexenLinedefTag(l); i=IteratorNext(i); } } else cancel=TRUE; } FullRedraw(); break; } case TM_ZERO: i=ListIterator(selected); while(i) { s=IteratorData(i); l=GETLINE(*s); for(f=0;f<5;f++) l->l.args[f]=0; l->l.tag=0; i=IteratorNext(i); } FullRedraw(); break; case TM_ARGS_RANGE: { int a[5]; int d[5]; int id; int diff; l=SelHead(); id=l->l.type; i=ListIterator(selected); i=IteratorNext(i); diff=FALSE; while(i) { s=IteratorData(i); l=GETLINE(*s); if (l->l.type!=id) { diff=TRUE; i=IteratorClear(i); } else i=IteratorNext(i); } l=SelHead(); for(f=0;f<5;f++) { a[f]=l->l.args[f]; d[f]=0; } if ((!diff)||(YesNo("Selected linedefs have|different types| |" "Continue?"))) if ((SpecialArgDialog("Start values",id,a))&& (SpecialArgDialog("Increments",id,d))) { i=ListIterator(selected); while(i) { s=IteratorData(i); l=GETLINE(*s); for(f=0;f<5;f++) { l->l.args[f]=(Byte)a[f]; a[f]+=d[f]; } SetHexenLinedefTag(l); i=IteratorNext(i); } FullRedraw(); } break; } default: cancel=TRUE; break; } if ((!cancel)&&(!new_selection)&&(clear_on_menu)) { ClearSelection(); FullRedraw(); } } void ObjectKey_LINEDEF(GFXKey k) { char s[128]; EditLine *l; int f; int change; List li; Iterator i; EditSect *sr,*sl; DirName d; Object *o; int apply; VIDOOM_TRACE; switch(k.code) { case GFX_F12: li=ListNew(sizeof(int)); change=FALSE; if (ListSize(selected)==0) { GuiInfoBox("ERROR","No linedefs selected"); return; } i=ListIterator(selected); GUI_start_yesno_all(); while(i) { memcpy(&f,IteratorData(i),sizeof(f)); if ((l=GETLINE(f))) { VIDOOM_TRACE; if (check_1side_lower) if ((!l->sl)&&(strcmp(l->sr->lower,empty_texture))) if ((check_line_assume_yes)|| (YesNoAll ("LINEDEF %d is one-sided with a " "lower texture. Remove?",f))) { strcpy(l->sr->lower,empty_texture); change=TRUE; IntListUniqAdd(li,f); } VIDOOM_TRACE; if (check_1side_middle) if ((!l->sl)&&(!strcmp(l->sr->middle,empty_texture))) if ((check_line_assume_yes)|| (YesNoAll ("LINEDEF %d is one-sided with no " "middle texture. Add?",f))) { sprintf(s,"Pick MIDDLE texture " "for LINEDEF %d",f); if (strcmp(linedef_check_default,empty_texture)) { apply=TRUE; strcpy(d,linedef_check_default); } else apply=GetTexture(s,d); if (apply) { strcpy(l->sr->middle,d); change=TRUE; IntListUniqAdd(li,f); } } VIDOOM_TRACE; if (check_1side_upper) if ((!l->sl)&&(strcmp(l->sr->upper,empty_texture))) if ((check_line_assume_yes)|| (YesNoAll ("LINEDEF %d is one-sided with a " "upper texture. Remove?",f))) { strcpy(l->sr->upper,empty_texture); change=TRUE; IntListUniqAdd(li,f); } VIDOOM_TRACE; if ((check_2side_lower)&&(l->sl)&&(l->sl->sector!=-1)&& (l->sr->sector!=-1)) { sr=GETSECT(l->sr->sector); sl=GETSECT(l->sl->sector); if ((sr->s.floors.floor)&& (!strcmp(l->sr->lower,empty_texture))) if ((check_line_assume_yes)|| (YesNoAll ("LINEDEF %d is two-sided with no " "lower texture. Add?",f))) { sprintf(s,"Pick LOWER texture " "for LINEDEF %d",f); if (strcmp(linedef_check_default,empty_texture)) { apply=TRUE; strcpy(d,linedef_check_default); } else apply=GetTexture(s,d); if (apply) { strcpy(l->sr->lower,d); strcpy(l->sl->lower,empty_texture); change=TRUE; IntListUniqAdd(li,f); } } if ((sr->s.floor>sl->s.floor)&& (!strcmp(l->sl->lower,empty_texture))) if ((check_line_assume_yes)|| (YesNoAll ("LINEDEF %d is two-sided with no " "lower texture. Add?",f))) { sprintf(s,"Pick LOWER texture " "for LINEDEF %d",f); if (strcmp(linedef_check_default,empty_texture)) { apply=TRUE; strcpy(d,linedef_check_default); } else apply=GetTexture(s,d); if (apply) { strcpy(l->sl->lower,d); strcpy(l->sr->lower,empty_texture); change=TRUE; IntListUniqAdd(li,f); } } if ((sr->s.floor==sl->s.floor)&& ((strcmp(l->sl->lower,empty_texture))|| (strcmp(l->sr->lower,empty_texture)))) if ((check_line_assume_yes)|| (YesNoAll ("LINEDEF %d is two-sided with " "pointless lower texture. Remove?",f))) { strcpy(l->sl->lower,empty_texture); strcpy(l->sr->lower,empty_texture); change=TRUE; IntListUniqAdd(li,f); } } VIDOOM_TRACE; if ((check_2side_middle)&&(l->sl)&&(l->sl->sector!=-1)) { if (((strcmp(l->sr->middle,empty_texture))|| (strcmp(l->sl->middle,empty_texture)))&& (l->sl->sector!=l->sr->sector)) if ((check_line_assume_yes)|| (YesNoAll ("LINEDEF %d is two-sided with a " "middle texture. Remove?",f))) { strcpy(l->sr->middle,empty_texture); strcpy(l->sl->middle,empty_texture); change=TRUE; IntListUniqAdd(li,f); } } VIDOOM_TRACE; if ((check_2side_upper)&&(l->sl)&&(l->sl->sector!=-1)&& (l->sr->sector!=-1)) { sr=GETSECT(l->sr->sector); sl=GETSECT(l->sl->sector); if ((sr->s.ceiling>sl->s.ceiling)&& (!strcmp(l->sr->upper,empty_texture))) if ((check_line_assume_yes)|| (YesNoAll ("LINEDEF %d is two-sided with no " "upper texture. Add?",f))) { sprintf(s,"Pick UPPER texture " "for LINEDEF %d",f); if (strcmp(linedef_check_default,empty_texture)) { apply=TRUE; strcpy(d,linedef_check_default); } else apply=GetTexture(s,d); if (apply) { strcpy(l->sr->upper,d); strcpy(l->sl->upper,empty_texture); change=TRUE; IntListUniqAdd(li,f); } } if ((sr->s.ceilings.ceiling)&& (!strcmp(l->sl->upper,empty_texture))) if ((check_line_assume_yes)|| (YesNoAll ("LINEDEF %d is two-sided with no " "upper texture. Add?",f))) { sprintf(s,"Pick UPPER texture " "for LINEDEF %d",f); if (strcmp(linedef_check_default,empty_texture)) { apply=TRUE; strcpy(d,linedef_check_default); } else apply=GetTexture(s,d); if (apply) { strcpy(l->sl->upper,d); strcpy(l->sr->upper,empty_texture); change=TRUE; IntListUniqAdd(li,f); } } if ((sr->s.ceiling==sl->s.ceiling)&& ((strcmp(l->sl->upper,empty_texture))|| (strcmp(l->sr->upper,empty_texture)))) if ((check_line_assume_yes)|| (YesNoAll ("LINEDEF %d is two-sided with " "pointless upper texture. Remove?",f))) { strcpy(l->sl->upper,empty_texture); strcpy(l->sr->upper,empty_texture); change=TRUE; IntListUniqAdd(li,f); } } VIDOOM_TRACE; if ((check_2side_same_sector)&&(l->sl)&&(l->sl->sector!=-1)) { if ((l->sl->sector==l->sr->sector)&& ((strcmp(l->sr->lower,empty_texture))|| (strcmp(l->sr->middle,empty_texture))|| (strcmp(l->sr->upper,empty_texture))|| (strcmp(l->sl->lower,empty_texture))|| (strcmp(l->sl->middle,empty_texture))|| (strcmp(l->sl->upper,empty_texture)))) if ((check_line_assume_yes)|| (YesNoAll ("LINEDEF %d is two-sided within a " "sector and is textured. " "Remove all?",f))) { strcpy(l->sr->lower,empty_texture); strcpy(l->sl->lower,empty_texture); strcpy(l->sr->middle,empty_texture); strcpy(l->sl->middle,empty_texture); strcpy(l->sr->upper,empty_texture); strcpy(l->sl->upper,empty_texture); change=TRUE; IntListUniqAdd(li,f); } } } i=IteratorNext(i); } if (change) { if (YesNo("Select altered LINEDEFs?")) { ClearSelection(); i=ListIterator(li); while(i) { memcpy(&f,IteratorData(i),sizeof(int)); o=MapElem(linedef,f); SetSelect(f,SELECT_SELECTED); ListAppend(selected,&f); i=IteratorNext(i); } FullRedraw(); } } else GuiInfoBox("NOTICE","No problems found"); ListClear(li); break; default: case GFX_ASCII: switch(toupper(k.ascii)) { case 'H': tag_highlight=!tag_highlight; FullRedraw(); break; default: break; } break; } } int ObjectHasTag_LINEDEF(void *obj, int tag) { EditLine *l; return ((l=obj)&&(l->l.tag==tag)); } ObjDesc *ObjectOverlaid_LINEDEF(int x, int y, int *no) { int n; EditLine *l; ObjDesc *od; int f; VIDOOM_TRACE; od=NULL; n=0; for(f=0;fl.type),60), l->l.tag); } } *no=n; return(od); } /* END OF FILE */