diff --git a/source/src/console.cpp b/source/src/console.cpp index 9275d632..ed428930 100644 --- a/source/src/console.cpp +++ b/source/src/console.cpp @@ -28,30 +28,49 @@ struct console : consolebuffer else fullconsole = ++fullconsole % 3; } - void addline(const char *sf, bool highlight) { consolebuffer::addline(sf, highlight, totalmillis); } + void addline(const char *sf) { consolebuffer::addline(sf, totalmillis); } vector visible; void render() { - if(fullconsole) + int conwidth = VIRTW*2 - 2*CONSPAD - 2*FONTH/3, h = VIRTH*2 - 2*CONSPAD - 2*FONTH/3, + conheight = min(fullconsole ? (h*(fullconsole==1 ? altconsize : fullconsize))/100 : FONTH*consize, h); + + if(fullconsole) blendbox(CONSPAD, CONSPAD, conwidth+CONSPAD+2*FONTH/3, conheight+CONSPAD+2*FONTH/3, true); + + int numl = conlines.length(), offset = min(conskip, numl); + + if(!fullconsole && confade) { - int w = VIRTW*2, h = VIRTH*2 - 2*CONSPAD - 2*FONTH/3; - int numl = ((h*(fullconsole==1 ? altconsize : fullconsize))/100)/(FONTH*5/4); - if(numl <= 0) return; - int offset = min(conskip, max(conlines.length() - numl, 0)); - blendbox(CONSPAD, CONSPAD, w-CONSPAD, 2*CONSPAD+numl*(FONTH*5/4)+2*FONTH/3, true); - loopi(numl) draw_text(offset+i>=conlines.length() ? "" : conlines[offset+i].cref, CONSPAD+FONTH/3, CONSPAD+(FONTH*5/4)*(numl-i-1)+FONTH/3); - } - else if(consize) - { - visible.setsizenodelete(0); - loopv(conlines) if(conskip ? i>=conskip-1 || i>=conlines.length()-consize : (!confade || totalmillis-conlines[i].millis=consize) break; + numl = 0; + loopvrev(conlines) if(totalmillis-conlines[i].millis < confade*1000) { numl = i+1; break; } } - loopvj(visible) draw_text(visible[j], CONSPAD+FONTH/3, CONSPAD+(FONTH*5/4)*(visible.length()-j-1)+FONTH/3); + else offset--; + } + + int y = 0; + loopi(numl) //determine visible height + { + // shuffle backwards to fill if necessary + int idx = offset+i < numl ? offset+i : --offset; + char *line = conlines[idx].line; + int width, height; + text_bounds(line, width, height, conwidth); + y += height; + if(y > conheight) { numl = i; if(offset == idx) ++offset; break; } + } + y = CONSPAD+FONTH/3; + loopi(numl) + { + int idx = offset + numl-i-1; + char *line = conlines[idx].line; + draw_text(line, CONSPAD+FONTH/3, y, 0xFF, 0xFF, 0xFF, 0xFF, -1, conwidth); + int width, height; + text_bounds(line, width, height, conwidth); + y += height; } } @@ -77,19 +96,17 @@ void conoutf(const char *s, ...) string sp; filtertext(sp, sf); puts(sp); - s = sf; - vector lines; - text_block(s, curfont ? VIRTW*2-2*CONSPAD-2*FONTH/3 : 0, lines); - loopv(lines) con.addline(lines[i], false); - lines.deletecontentsa(); + con.addline(sf); } -void rendercommand(int x, int y) +int rendercommand(int x, int y, int w) { s_sprintfd(s)("> %s", cmdline.buf); - int offset = text_width(s, cmdline.pos>=0 ? cmdline.pos+2 : -1); - rendercursor(x+offset, y, char_width(cmdline.pos>=0 ? cmdline.buf[cmdline.pos] : '_')); - draw_text(s, x, y); + int width, height; + text_bounds(s, width, height, w); + y -= height - FONTH; + draw_text(s, x, y, 0xFF, 0xFF, 0xFF, 0xFF, cmdline.pos>=0 ? cmdline.pos+2 : strlen(s), w); + return height; } // keymap is defined externally in keymap.cfg diff --git a/source/src/console.h b/source/src/console.h index 28a558f3..413fecfb 100644 --- a/source/src/console.h +++ b/source/src/console.h @@ -1,26 +1,16 @@ -struct cline { char *cref; int millis; }; +struct cline { char *line; int millis; }; struct consolebuffer { vector conlines; - void addline(const char *sf, bool highlight, int millis) // add a line to the console buffer + void addline(const char *sf, int millis) // add a line to the console buffer { cline cl; - cl.cref = conlines.length()>100 ? conlines.pop().cref : newstringbuf(""); // constrain the buffer size + cl.line = conlines.length()>100 ? conlines.pop().line : newstringbuf(""); // constrain the buffer size cl.millis = millis; // for how long to keep line on screen conlines.insert(0,cl); - if(highlight) // show line in a different colour, for chat etc. - { - cl.cref[0] = '\f'; - cl.cref[1] = '0'; - cl.cref[2] = 0; - s_strcat(cl.cref, sf); - } - else - { - s_strcpy(cl.cref, sf); - } + s_strcpy(cl.line, sf); } virtual ~consolebuffer() {} diff --git a/source/src/docs.cpp b/source/src/docs.cpp index 503c69bd..4f0a8ce4 100644 --- a/source/src/docs.cpp +++ b/source/src/docs.cpp @@ -309,7 +309,7 @@ int numargs(char *args) return argidx; } -void renderdoc(int x, int y) +void renderdoc(int x, int y, int doch) { if(!docvisible) return; @@ -328,10 +328,11 @@ void renderdoc(int x, int y) docident *ident = docidents.access(cmd); if(ident) { - const int linemax = VIRTW*4/3; - cvector doclines; + vector doclines; - char *label = doclines.add(newstringbuf(ident->name)); // label + char *label = newstringbuf(); // label + doclines.add(label); + s_sprintf(label)("~%s", ident->name); loopvj(ident->arguments) { s_strcat(label, " "); @@ -339,9 +340,7 @@ void renderdoc(int x, int y) } doclines.add(NULL); - cvector desc; - text_block(ident->desc, linemax, desc); // desc - loopvj(desc) doclines.add(desc[j]); + doclines.add(ident->desc); doclines.add(NULL); if(ident->arguments.length() > 0) // args @@ -379,8 +378,7 @@ void renderdoc(int x, int y) { docargument *a = &ident->arguments[j]; if(!a) continue; - char *argstr = doclines.add(new string); - s_sprintf(argstr)("\f%d%-8s%s %s%s%s", j == arg ? 4 : 5, a->token, a->desc, + s_sprintf(doclines.add(newstringbuf()))("~\f%d%-8s%s %s%s%s", j == arg ? 4 : 5, a->token, a->desc, a->values ? "(" : "", a->values ? a->values : "", a->values ? ")" : ""); } @@ -389,35 +387,28 @@ void renderdoc(int x, int y) if(ident->remarks.length()) // remarks { - loopvj(ident->remarks) - { - cvector remarks; - text_block(ident->remarks[j], linemax, remarks); - loopvk(remarks) doclines.add(remarks[k]); - } + loopvj(ident->remarks) doclines.add(ident->remarks[j]); doclines.add(NULL); } if(ident->examples.length()) // examples { - doclines.add(newstring(ident->examples.length() == 1 ? "Example:" : "Examples:")); + doclines.add(ident->examples.length() == 1 ? "Example:" : "Examples:"); loopvj(ident->examples) { - cvector lines; - text_block(ident->examples[j].code, linemax, lines); - text_block(ident->examples[j].explanation, linemax, lines); - loopvk(lines) doclines.add(lines[k]); + doclines.add(ident->examples[j].code); + doclines.add(ident->examples[j].explanation); } doclines.add(NULL); } if(ident->keys.length()) // default keys { - doclines.add(newstring(ident->keys.length() == 1 ? "Default key:" : "Default keys:")); + doclines.add(ident->keys.length() == 1 ? "Default key:" : "Default keys:"); loopvj(ident->keys) { dockey &k = ident->keys[j]; - s_sprintfd(line)("%-10s %s", k.name ? k.name : k.alias, k.desc ? k.desc : ""); + s_sprintfd(line)("~%-10s %s", k.name ? k.name : k.alias, k.desc ? k.desc : ""); doclines.add(newstring(line)); } doclines.add(NULL); @@ -437,25 +428,57 @@ void renderdoc(int x, int y) loopj(sizeof(categories)/sizeof(category)) { if(!strlen(categories[j].refs)) continue; - char *line = doclines.add(newstringbuf(categories[j].label)); - s_strcat(line, ": "); - s_strcat(line, categories[j].refs); + s_sprintf(doclines.add(newstringbuf()))("~%s: %s", categories[j].label, categories[j].refs); } } - int screenlines = (VIRTH*2/3/FONTH)-1; - if(docskip) docskip = min(docskip, doclines.length() - screenlines); // normalize - bool more = docskip < doclines.length() - screenlines; - - for(int j = docskip; j < min(doclines.length(), docskip+screenlines); j++) - { - if(doclines[j]) draw_textf("%s", x, y+j*FONTH, doclines[j]); - } - doclines.deletecontentsa(); + while(doclines.length() && !doclines.last()) doclines.pop(); - if(more) draw_textf("\f4more (F3)", x, y+screenlines*FONTH); // footer - if(docskip > 0) draw_textf("\f4less (F2)", x, y+(screenlines+1)*FONTH); - draw_textf("\f4disable doc reference (F1)", x, y+(screenlines+2)*FONTH); + doch -= 3*FONTH + FONTH/2; + + int offset = min(docskip, doclines.length()-1), maxl = offset, cury = 0; + for(int j = offset; j < doclines.length(); j++) + { + const char *str = doclines[j]; + int width = 0, height = FONTH; + if(str) text_bounds(*str=='~' ? str+1 : str, width, height, *str=='~' ? -1 : VIRTW*4/3); + if(cury + height > doch) break; + cury += height; + maxl = j+1; + } + if(offset > 0 && maxl >= doclines.length()) + { + for(int j = offset-1; j >= 0; j--) + { + const char *str = doclines[j]; + int width = 0, height = FONTH; + if(str) text_bounds(*str=='~' ? str+1 : str, width, height, *str=='~' ? -1 : VIRTW*4/3); + if(cury + height > doch) break; + cury += height; + offset = j; + } + } + + + cury = y; + for(int j = offset; j < maxl; j++) + { + const char *str = doclines[j]; + if(str) + { + const char *text = *str=='~' ? str+1 : str; + draw_text(text, x, cury, 0xFF, 0xFF, 0xFF, 0xFF, -1, str==text ? VIRTW*4/3 : -1); + int width, height; + text_bounds(text, width, height, str==text ? VIRTW*4/3 : -1); + cury += height; + if(str!=text) delete[] str; + } + else cury += FONTH; + } + + if(maxl < doclines.length()) draw_text("\f4more (F3)", x, y+doch); // footer + if(offset > 0) draw_text("\f4less (F2)", x, y+doch+FONTH); + draw_text("\f4disable doc reference (F1)", x, y+doch+2*FONTH); return; } } diff --git a/source/src/geom.h b/source/src/geom.h index 6c1c540e..2b694373 100644 --- a/source/src/geom.h +++ b/source/src/geom.h @@ -137,6 +137,29 @@ static inline uint hthash(const ivec &k) return k.x^k.y^k.z; } +struct bvec +{ + union + { + struct { uchar x, y, z; }; + uchar v[3]; + }; + + bvec() {} + bvec(uchar x, uchar y, uchar z) : x(x), y(y), z(z) {} + bvec(const vec &v) : x((uchar)((v.x+1)*255/2)), y((uchar)((v.y+1)*255/2)), z((uchar)((v.z+1)*255/2)) {} + + uchar &operator[](int i) { return v[i]; } + uchar operator[](int i) const { return v[i]; } + + bool operator==(const bvec &v) const { return x==v.x && y==v.y && z==v.z; } + bool operator!=(const bvec &v) const { return x!=v.x || y!=v.y || z!=v.z; } + + bool iszero() const { return x==0 && y==0 && z==0; } + + vec tovec() const { return vec(x*(2.0f/255.0f)-1.0f, y*(2.0f/255.0f)-1.0f, z*(2.0f/255.0f)-1.0f); } +}; + struct glmatrixf { float v[16]; diff --git a/source/src/menus.cpp b/source/src/menus.cpp index 4c12af53..aff2baca 100644 --- a/source/src/menus.cpp +++ b/source/src/menus.cpp @@ -205,9 +205,7 @@ struct mitemtextinput : mitemtext int tw = VIRTW/4; if(selection) renderbg(x+w-tw, y-FONTH/6, tw, NULL); draw_text(text, x, y); - int offset = text_width(input.buf, input.pos>=0 ? input.pos : -1); - if(selection) rendercursor(x+w-tw+offset, y, char_width(input.pos>=0 ? input.buf[input.pos] : '_')); - draw_text(input.buf, x+w-tw, y); + draw_text(input.buf, x+w-tw, y, 255, 255, 255, 255, selection ? (input.pos>=0 ? input.pos : strlen(input.buf)) : -1); } virtual void focus(bool on) @@ -364,11 +362,11 @@ struct mitemkeyinput : mitem DELETEA(bindcmd); } - virtual int width() { return text_width(text)+(keyname ? text_width(keyname) : char_width('_')); } + virtual int width() { return text_width(text)+text_width(keyname ? keyname : " "); } virtual void render(int x, int y, int w) { - int tk = (keyname ? text_width(keyname) : char_width('_')); + int tk = text_width(keyname ? keyname : " "); static color capturec(0.4f, 0, 0); if(isselection()) blendbox(x+w-tk-FONTH, y-FONTH/6, x+w+FONTH, y+FONTH+FONTH/6, false, -1, capture ? &capturec : NULL); draw_text(text, x, y); diff --git a/source/src/protos.h b/source/src/protos.h index 648a3045..6aff5eed 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -69,7 +69,7 @@ extern int execcontext; // console extern void keypress(int code, bool isdown, int cooked, SDLMod mod = KMOD_NONE); -extern void rendercommand(int x, int y); +extern int rendercommand(int x, int y, int w); extern void renderconsole(); extern char *getcurcommand(); extern char *addreleaseaction(const char *s); @@ -447,12 +447,13 @@ extern int VIRTW; // virtual screen size for text & HUD extern font *curfont; extern bool setfont(const char *name); -extern void draw_text(const char *str, int left, int top); +extern void draw_text(const char *str, int left, int top, int r = 255, int g = 255, int b = 255, int a = 255, int cursor = -1, int maxwidth = -1); extern void draw_textf(const char *fstr, int left, int top, ...); -extern int char_width(int c, int x = 0); -extern int text_width(const char *str, int limit = -1); +extern int text_width(const char *str); extern int text_visible(const char *str, int max); -extern void text_block(const char *str, int max, vector &lines); +extern void text_bounds(const char *str, int &width, int &height, int maxwidth = -1); +extern int text_visible(const char *str, int hitx, int hity, int maxwidth); +extern void text_pos(const char *str, int cursor, int &cx, int &cy, int maxwidth); extern void text_startcolumns(); extern void text_endcolumns(); @@ -595,7 +596,7 @@ extern void checkitems(playerent *d); extern void perlinarea(block &b, int scale, int seed, int psize); // doc -extern void renderdoc(int x, int y); +extern void renderdoc(int x, int y, int doch); extern void renderdocmenu(void *menu, bool init); extern void toggledoc(); extern void scrolldoc(int i); diff --git a/source/src/renderhud.cpp b/source/src/renderhud.cpp index 4e8694e8..d7e49bf8 100644 --- a/source/src/renderhud.cpp +++ b/source/src/renderhud.cpp @@ -262,12 +262,12 @@ void drawradarent(float x, float y, float yaw, int col, int row, float iconsize, struct hudmessages : consolebuffer { - void addline(const char *sf) { consolebuffer::addline(sf, false, totalmillis); } + void addline(const char *sf) { consolebuffer::addline(sf, totalmillis); } void editlastline(const char *sf) { if(!conlines.length()) return; conlines[0].millis = totalmillis; - s_strcpy(conlines[0].cref, sf); + s_strcpy(conlines[0].line, sf); } void render() { @@ -278,8 +278,8 @@ struct hudmessages : consolebuffer loopi(min(conlines.length(), 3)) if(totalmillis-conlines[i].millis VIRTW*0.8f ? 0 : (VIRTW*0.8f-tw)/2), int(((VIRTH*0.8f)/4*3)+FONTH*i+pow((totalmillis-c.millis)/(float)dispmillis, 4)*VIRTH*0.8f/4.0f)); + int tw = text_width(c.line); + draw_text(c.line, int(tw > VIRTW*0.8f ? 0 : (VIRTW*0.8f-tw)/2), int(((VIRTH*0.8f)/4*3)+FONTH*i+pow((totalmillis-c.millis)/(float)dispmillis, 4)*VIRTH*0.8f/4.0f)); } } }; @@ -477,7 +477,8 @@ void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwat glMatrixMode(GL_PROJECTION); char *infostr = editinfo(); - if(command) rendercommand(20, 1570); + int commandh = 1570 + FONTH; + if(command) commandh -= rendercommand(20, 1570, VIRTW); else if(infostr) draw_text(infostr, 20, 1570); else if(targetplayer && !spectating) draw_text(colorname(targetplayer), 20, 1570); @@ -486,7 +487,7 @@ void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwat if(!hideconsole) renderconsole(); if(menu) rendermenu(); - else if(command) renderdoc(40, VIRTH); + else if(command) renderdoc(40, VIRTH, max(commandh*2 - VIRTH, 0)); if(!hidestats) { const int left = (VIRTW-225-10)*2, top = (VIRTH*7/8)*2; diff --git a/source/src/rendertext.cpp b/source/src/rendertext.cpp index 439fea8c..1f54d615 100644 --- a/source/src/rendertext.cpp +++ b/source/src/rendertext.cpp @@ -54,17 +54,62 @@ bool setfont(const char *name) return true; } -int char_width(int c, int x) +int text_width(const char *str) { - if(!curfont) return x; - else if(c=='\t') x = (x+PIXELTAB)/PIXELTAB*PIXELTAB; - else if(c==' ') x += curfont->defaultw; - else if(curfont->chars.inrange(c-33)) - { - c -= 33; - x += curfont->chars[c].w + 1; + int width, height; + text_bounds(str, width, height); + return width; +} + +void draw_textf(const char *fstr, int left, int top, ...) +{ + s_sprintfdlv(str, top, fstr); + draw_text(str, left, top); +} + +static int draw_char(int c, int x, int y) +{ + font::charinfo &info = curfont->chars[c-33]; + float tc_left = (info.x + curfont->offsetx) / float(curfont->tex->xs); + float tc_top = (info.y + curfont->offsety) / float(curfont->tex->ys); + float tc_right = (info.x + info.w + curfont->offsetw) / float(curfont->tex->xs); + float tc_bottom = (info.y + info.h + curfont->offseth) / float(curfont->tex->ys); + + glTexCoord2f(tc_left, tc_top ); glVertex2f(x, y); + glTexCoord2f(tc_right, tc_top ); glVertex2f(x + info.w, y); + glTexCoord2f(tc_right, tc_bottom); glVertex2f(x + info.w, y + info.h); + glTexCoord2f(tc_left, tc_bottom); glVertex2f(x, y + info.h); + + xtraverts += 4; + return info.w; +} + +//stack[sp] is current color index +static void text_color(char c, char *stack, int size, int &sp, bvec color, int a) +{ + if(c=='s') // save color + { + c = stack[sp]; + if(sp 0) ? --sp : sp]; // restore color + else stack[sp] = c; + switch(c) + { + case '0': color = bvec(64, 255, 128); break; // green: player talk + case '1': color = bvec(96, 160, 255); break; // blue: team chat + case '2': color = bvec(255, 192, 64); break; // yellow: gameplay action messages, only actions done by players + case '3': color = bvec(255, 64, 64); break; // red: important errors and notes + case '4': color = bvec(128, 128, 128); break; // gray + case '5': color = bvec(255, 255, 255); break; // white + case '6': color = bvec(96, 48, 0); break; // dark brown + case '7': color = bvec(128, 48, 48); break; // dark red: dead admin + // white (provided color): everything else + } + glColor4ub(color.x, color.y, color.z, a); + } } static vector *columns = NULL; @@ -79,170 +124,173 @@ void text_endcolumns() DELETEP(columns); } -int text_width(const char *str, int limit) -{ - if(!str) return 0; - int x = 0, col = 0, colx = 0; - for(int i = 0; str[i] && (limit<0 || i=columns->length()) columns->add(0); - int w = char_width('\t', x) - colx; - w = max(w, (*columns)[col]); - (*columns)[col] = w; - col++; - colx += w; - x = colx; +#define TEXTGETCOLUMN \ + if(columns && collength()) \ + { \ + colx += (*columns)[col++]; \ + x = colx; \ + } \ + else x = TABALIGN(x); + +#define TEXTSETCOLUMN \ + if(columns) \ + { \ + while(col>=columns->length()) columns->add(0); \ + int w = TABALIGN(x) - colx; \ + w = max(w, (*columns)[col]); \ + (*columns)[col] = w; \ + col++; \ + colx += w; \ + x = colx; \ + } \ + else x = TABALIGN(x); + + +#define TEXTSKELETON \ + int y = 0, x = 0, col = 0, colx = 0;\ + int i;\ + for(i = 0; str[i]; i++)\ + {\ + TEXTINDEX(i)\ + int c = str[i];\ + if(c=='\t') { TEXTTAB(i); TEXTWHITE(i) }\ + else if(c==' ') { x += curfont->defaultw; TEXTWHITE(i) }\ + else if(c=='\n') { TEXTLINE(i) x = 0; y += FONTH; }\ + else if(c=='\f') { if(str[i+1]) { i++; TEXTCOLOR(i) }}\ + else if(curfont->chars.inrange(c-33))\ + {\ + if(maxwidth != -1)\ + {\ + int j = i;\ + int w = curfont->chars[c-33].w;\ + for(; str[i+1]; i++)\ + {\ + int c = str[i+1];\ + if(c=='\f') { if(str[i+2]) i++; continue; }\ + if(i-j > 16) break;\ + if(!curfont->chars.inrange(c-33)) break;\ + int cw = curfont->chars[c-33].w + 1;\ + if(w + cw >= maxwidth) break;\ + w += cw;\ + }\ + if(x + w >= maxwidth && j!=0) { TEXTLINE(j-1) x = 0; y += FONTH; }\ + TEXTWORD\ + }\ + else\ + { TEXTCHAR(i) }\ + }\ + } + +//all the chars are guaranteed to be either drawable or color commands +#define TEXTWORDSKELETON \ + for(; j <= i; j++)\ + {\ + TEXTINDEX(j)\ + int c = str[j];\ + if(c=='\f') { if(str[j+1]) { j++; TEXTCOLOR(j) }}\ + else { TEXTCHAR(j) }\ } - else x = char_width('\t', x); - break; - default: - x = char_width(str[i], x); - break; - } - } - return x; -} - -int text_visible(const char *str, int max) +int text_visible(const char *str, int hitx, int hity, int maxwidth) { - int i = 0, x = 0; - while(str[i]) - { - if(str[i]=='\f') - { - i += 2; - continue; - } - x = char_width(str[i], x); - if(x > max) return i; - ++i; - } + #define TEXTINDEX(idx) + #define TEXTTAB(idx) TEXTGETCOLUMN + #define TEXTWHITE(idx) if(y+FONTH > hity && x >= hitx) return idx; + #define TEXTLINE(idx) if(y+FONTH > hity) return idx; + #define TEXTCOLOR(idx) + #define TEXTCHAR(idx) x += curfont->chars[c-33].w+1; TEXTWHITE(idx) + #define TEXTWORD TEXTWORDSKELETON + TEXTSKELETON + #undef TEXTINDEX + #undef TEXTTAB + #undef TEXTWHITE + #undef TEXTLINE + #undef TEXTCOLOR + #undef TEXTCHAR + #undef TEXTWORD return i; } -// cut strings to fit on screen -void text_block(const char *str, int max, vector &lines) +//inverse of text_visible +void text_pos(const char *str, int cursor, int &cx, int &cy, int maxwidth) { - if(!str) return; - int visible; - cvector colordata; - while((visible = max ? text_visible(str, max) : (int)strlen(str))) - { - const char *newline = (const char *)memchr(str, '\n', visible); - if(newline) visible = newline+1-str; - else if(str[visible]) // wrap words - { - int v = visible; - while(v > 0 && str[v] != ' ') v--; - if(v) visible = v+1; - } - size_t colorsize = colordata.length()*2; - char *t = lines.add(newstring((size_t)visible+colorsize)); - char *color = t; - loopv(colordata) // add colors from previous line - { - s_strncpy(color, colordata[i], 3); - color += 2; - } - s_strncpy(color, str, visible+1); - colordata.setsize(0); - for(char *c = strchr(t, '\f'); c && c[1]; c = strchr(c+1, '\f')) colordata.add(c); // store colors of this line - str += visible; - } + #define TEXTINDEX(idx) if(idx == cursor) { cx = x; cy = y; break; } + #define TEXTTAB(idx) TEXTGETCOLUMN + #define TEXTWHITE(idx) + #define TEXTLINE(idx) + #define TEXTCOLOR(idx) + #define TEXTCHAR(idx) x += curfont->chars[c-33].w + 1; + #define TEXTWORD TEXTWORDSKELETON if(i >= cursor) break; + cx = INT_MIN; + cy = 0; + TEXTSKELETON + if(cx == INT_MIN) { cx = x; cy = y; } + #undef TEXTINDEX + #undef TEXTTAB + #undef TEXTWHITE + #undef TEXTLINE + #undef TEXTCOLOR + #undef TEXTCHAR + #undef TEXTWORD } -void draw_textf(const char *fstr, int left, int top, ...) +void text_bounds(const char *str, int &width, int &height, int maxwidth) { - s_sprintfdlv(str, top, fstr); - draw_text(str, left, top); + #define TEXTINDEX(idx) + #define TEXTTAB(idx) TEXTSETCOLUMN + #define TEXTWHITE(idx) + #define TEXTLINE(idx) if(x > width) width = x; + #define TEXTCOLOR(idx) + #define TEXTCHAR(idx) x += curfont->chars[c-33].w + 1; + #define TEXTWORD x += w + 1; + width = 0; + TEXTSKELETON + height = y + FONTH; + TEXTLINE(_) + #undef TEXTINDEX + #undef TEXTTAB + #undef TEXTWHITE + #undef TEXTLINE + #undef TEXTCOLOR + #undef TEXTCHAR + #undef TEXTWORD } -void draw_text(const char *str, int left, int top) +void draw_text(const char *str, int left, int top, int r, int g, int b, int a, int cursor, int maxwidth) { - if(!curfont) return; - - glBlendFunc(GL_ONE, GL_ONE); + #define TEXTINDEX(idx) if(idx == cursor) { cx = x; cy = y; cc = str[idx]; } + #define TEXTTAB(idx) TEXTGETCOLUMN + #define TEXTWHITE(idx) + #define TEXTLINE(idx) + #define TEXTCOLOR(idx) text_color(str[idx], colorstack, sizeof(colorstack), colorpos, color, a); + #define TEXTCHAR(idx) x += draw_char(c, left+x, top+y)+1; + #define TEXTWORD TEXTWORDSKELETON + char colorstack[10]; + bvec color(r, g, b); + int colorpos = 0, cx = INT_MIN, cy = 0, cc = ' '; + colorstack[0] = 'c'; //indicate user color + glBlendFunc(GL_SRC_ALPHA, curfont->tex->bpp==32 ? GL_ONE_MINUS_SRC_ALPHA : GL_ONE); glBindTexture(GL_TEXTURE_2D, curfont->tex->id); - - static float colorstack[8][4]; - int colorpos = 0, x = left, y = top, col = 0, colx = 0; - glBegin(GL_QUADS); - // ATI bug -- initial color must be set after glBegin - glColor3ub(255, 255, 255); - for(int i = 0; str[i]; i++) - { - int c = str[i]; - switch(c) - { - case '\t': - if(columns && collength()) - { - colx += (*columns)[col++]; - x = left + colx; - } - else x = (x-left+PIXELTAB)/PIXELTAB*PIXELTAB+left; - continue; - - case '\f': - switch(str[++i]) - { - case '0': glColor3ub(64, 255, 128); continue; // green: player talk - case '1': glColor3ub(96, 160, 255); continue; // blue: team chat - case '2': glColor3ub(255, 192, 64); continue; // yellow: gameplay action messages, only actions done by players - case '3': glColor3ub(255, 64, 64); continue; // red: important errors and notes - case '4': glColor3ub(128, 128, 128); continue; // gray - case '5': glColor3ub(255, 255, 255); continue; // white: everything else - case '6': glColor3ub(96, 48, 0); continue; // dark brown - case '7': glColor3ub(128, 48, 48); continue; // dark red: dead admin - case 's': // save color - if((size_t)colorpos0) - glColor4fv(colorstack[--colorpos]); - continue; - default: continue; - } - - case ' ': - x += curfont->defaultw; - continue; - } - - c -= 33; - if(!curfont->chars.inrange(c)) continue; - - font::charinfo &info = curfont->chars[c]; - float tc_left = (info.x + curfont->offsetx) / float(curfont->tex->xs); - float tc_top = (info.y + curfont->offsety) / float(curfont->tex->ys); - float tc_right = (info.x + info.w + curfont->offsetw) / float(curfont->tex->xs); - float tc_bottom = (info.y + info.h + curfont->offseth) / float(curfont->tex->ys); - - glTexCoord2f(tc_left, tc_top ); glVertex2f(x, y); - glTexCoord2f(tc_right, tc_top ); glVertex2f(x + info.w, y); - glTexCoord2f(tc_right, tc_bottom); glVertex2f(x + info.w, y + info.h); - glTexCoord2f(tc_left, tc_bottom); glVertex2f(x, y + info.h); - - xtraverts += 4; - x += info.w + 1; - } + glColor4ub(color.x, color.y, color.z, a); + TEXTSKELETON glEnd(); + if(cursor >= 0) + { + if(cx == INT_MIN) { cx = x; cy = y; } + if(maxwidth != -1 && cx >= maxwidth) { cx = 0; cy += FONTH; } + int cw = curfont->chars.inrange(cc-33) ? curfont->chars[cc-33].w + 1 : curfont->defaultw; + rendercursor(left+cx, top+cy, cw); + } + #undef TEXTINDEX + #undef TEXTTAB + #undef TEXTWHITE + #undef TEXTLINE + #undef TEXTCOLOR + #undef TEXTCHAR + #undef TEXTWORD } void reloadfonts() diff --git a/source/src/tools.h b/source/src/tools.h index d761735d..364c13ee 100644 --- a/source/src/tools.h +++ b/source/src/tools.h @@ -508,6 +508,7 @@ template struct hashtable inline char *newstring(size_t l) { return new char[l+1]; } inline char *newstring(const char *s, size_t l) { return s_strncpy(newstring(l), s, l+1); } inline char *newstring(const char *s) { return newstring(s, strlen(s)); } +inline char *newstringbuf() { return newstring(_MAXDEFSTR-1); } inline char *newstringbuf(const char *s) { return newstring(s, _MAXDEFSTR-1); } extern char *path(char *s);