/* Copyright (C) 1996-1997 Id Software, Inc. 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. */ // r_bulleten.c // //draws new stuff onto the mip each frame! //text set each frame #include "quakedef.h" #ifdef PEXT_BULLETENS #ifdef GLQUAKE #include "glquake.h"//hack #endif /* Effects: 0: standard plain background 1: normal texture + scrolling text 2: Sparkling 3: Simply a scrolling texture 4: Ripples (for water) - it is treated as a bulleten, because that way, we can easily hook into it. There is never any text though. 5: 6: 7: 8: 9: */ cvar_t bul_text1 = SCVAR("bul_text1", "0Cheesy Forethoug\\nht entertainment"); cvar_t bul_text2 = SCVAR("bul_text2", "2"); cvar_t bul_text3 = SCVAR("bul_text3", "0Join Shubs Army\\nFight for Fear"); cvar_t bul_text4 = SCVAR("bul_text4", "0Need a gun?\\nGoto bobs place!"); cvar_t bul_text5 = SCVAR("bul_text5", "0Beware the fans\\nThey can hurt."); cvar_t bul_text6 = SCVAR("bul_text6", "2Quake B Arena"); cvar_t bul_scrollspeedx = SCVAR("bul_scrollspeedx", "-20"); //pixels per second cvar_t bul_scrollspeedy = SCVAR("bul_scrollspeedy", "-10"); //pixels per second cvar_t bul_backcol = SCVAR("bul_backcolour", "1"); cvar_t bul_textpalette = SCVAR("bul_textpalette", "0"); cvar_t bul_norender = SCVAR("bul_norender", "0"); cvar_t bul_sparkle = SCVAR("bul_sparkle", "7"); cvar_t bul_forcemode = SCVAR("bul_forcemode", "-1"); cvar_t bul_ripplespeed = SCVAR("bul_ripplespeed", "32"); cvar_t bul_rippleamount = SCVAR("bul_rippleamount", "2"); cvar_t bul_nowater = SCVAR("bul_nowater", "1"); int bultextpallete = 0; bulletentexture_t *bulletentexture; int nlstrlen(char *str, int *lines) //strlen, but for longest line in string { int cl = 0, ol = 0; if (*str >= '0' && *str <= '9') //used to set an effect str++; *lines = 1; for (;*str;str++,cl++) { if (*str == '\\') { str++; if (*str == 'n') { if (ol < cl) ol = cl; cl = 0; *lines += 1; } } } if (cl > ol) return cl; return ol; } void WipeBulletenTextures(void) { bulletentexture_t *a; // return; for (a = bulletentexture; a; a=a->next) { a->texture = NULL; } bulletentexture = NULL; } qboolean R_AddBulleten (texture_t *textur) { bulletentexture_t *a; int len; int lines; int type; char *text=""; #ifndef CLIENTONLY if (isDedicated) return false; #endif if (!Q_strncmp(textur->name,"b_lead",6)) { type = 0; // name winner text = bul_text1.string; } else if (!Q_strncmp(textur->name,"b_loose",7)) { type = 1; // name looser text = bul_text2.string; } else if (!Q_strncmp(textur->name, "b_text\0",7)) { type = 2 + rand() % 6; //random advert (all of these end up the same (first found)) // ; } else if (!Q_strncmp(textur->name, "b_text_1",8)) { type = 2; // advert 1 text = bul_text1.string; } else if (!Q_strncmp(textur->name, "b_text_2", 8)) { type = 3; // advert 2 text = bul_text2.string; } else if (!Q_strncmp(textur->name, "b_text_3", 8)) { type = 4; // advert 3 text = bul_text3.string; } else if (!Q_strncmp(textur->name, "b_text_4", 8)) { type = 5; // advert 4 text = bul_text4.string; } else if (!Q_strncmp(textur->name, "b_text_5", 8)) { type = 6; text = bul_text5.string; } else if (!Q_strncmp(textur->name, "b_text_6", 8)) { type = 7; text = bul_text6.string; } // water ripples else if (!Q_strncmp(textur->name,"*", 1) && !bul_nowater.value) { type = -1; text = ""; } else if (!Q_strncmp(textur->name,"bul_", 4)) { type = atoi(textur->name+4); text = ""; } else // not a bulleten return false; for (a = bulletentexture; a; a=a->next) { if (a->texture == textur) return true; //texture address already used } if (a == NULL) { //not found it, create a new texture a = Hunk_AllocName(sizeof(struct bulletentexture_s) + ((textur->width) * (textur->height)), "bulleten"); a->next = bulletentexture; //add in first bulletentexture = a; len = nlstrlen(text, &lines); a->texture = textur; a->bultextleft = (a->texture->width - (len*8)) / 2; a->bultexttop = (a->texture->height - (lines*8)) / 2; a->type = type; a->normaltexture = (qbyte *) a + sizeof(struct bulletentexture_s); memcpy(a->normaltexture, (qbyte *) textur + textur->offsets[0], textur->width * textur->height); return true; } return false; } //user wants to force a world texture into a bulleten board. void R_BulletenForce_f (void) { extern model_t mod_known[]; extern int mod_numknown; model_t *mod; texture_t *tx; char *match = Cmd_Argv(1); int i, m, s; for (m=0 , mod=mod_known ; m<mod_numknown ; m++, mod++) { if (mod->type == mod_brush && !mod->needload) { for (i = 0; i < mod->numtextures; i++) { tx = mod->textures[i]; if (!tx) continue; //happens on e1m2 if (!stricmp(tx->name, match)) { char *text = ""; int len, lines; bulletentexture_t *a; for (a = bulletentexture; a; a=a->next) { if (a->texture == tx) { a->type = atoi(Cmd_Argv(2)); break; //texture address already used } } if (a == NULL) { //not found it, create a new texture a = Hunk_AllocName(sizeof(struct bulletentexture_s) + ((tx->width) * (tx->height)), "bulleten"); a->next = bulletentexture; //add in first bulletentexture = a; len = nlstrlen(text, &lines); a->texture = tx; a->bultextleft = (a->texture->width - (len*8)) / 2; a->bultexttop = (a->texture->height - (lines*8)) / 2; a->type = atoi(Cmd_Argv(2)); a->normaltexture = (qbyte *) a + sizeof(struct bulletentexture_s); memcpy(a->normaltexture, (qbyte *) tx + tx->offsets[0], tx->width * tx->height); for (s = 0; s < mod->numsurfaces; s++) { mod->surfaces[s].flags |= SURF_BULLETEN; } } } } } } } void R_SetupBulleten (void) { bulletentexture_t *a; int len; int lines; player_info_t *s; char text[256]; if (bul_norender.value || cl.paused || !bulletentexture) //don't scroll when paused return; bultextpallete = bul_textpalette.value * 16; if (bultextpallete < 0) //not the negatives bultextpallete = 0; if (bultextpallete > 255 - vid.fullbright) // don't allow shifting into the fullbrights, (compensate for pallete scale bultextpallete = 0; Sbar_SortFrags (false); //find who's winning and who's loosing for (a = bulletentexture; a; a=a->next) { if (a->texture != NULL) { switch (a->type) { case -1: sprintf(text, "4"); //negative values have no text break; case 0: //leader s = &cl.players[fragsort[0]]; if (!s->name[0]) { sprintf(text, "0%s", bul_text1.string); break; } if (s->frags == 1) sprintf(text, "0%s is leading\nwith 1 frag!", s->name); else sprintf(text, "0%s is leading\nwith %i frags!", s->name, s->frags); break; case 1: //looser s = &cl.players[fragsort[scoreboardlines-1]]; if (!s->name[0]) { sprintf(text, bul_text2.string); break; } if (s->frags == 1) sprintf(text, "0%s is behind\nwith 1 frag!", s->name); else sprintf(text, "0%s is behind\nwith %i frags!", s->name, s->frags); break; case 2: //an add sprintf(text, bul_text1.string); break; case 3: //another add sprintf(text, bul_text2.string); break; case 4: //yet another add sprintf(text, bul_text3.string); break; case 5: sprintf(text, bul_text4.string); break; case 6: sprintf(text, bul_text5.string); break; case 7: sprintf(text, bul_text6.string); break; case 8: *text = 0; break; default: sprintf(text, "Unrecognised Bulleten"); break; } len = nlstrlen(text, &lines); #if 1 if (lines*8 <= a->texture->height) a->bultexttop = (a->texture->height - lines*8)/2; else a->bultexttop = ((int)(cl.time * bul_scrollspeedy.value) % (a->texture->height + lines * 8)) - lines * 8; #else if (lines*8 <= a->texture->height) a->bultexttop = (a->texture->height - (lines*8)) / 2; else { a->bultexttop += bul_scrollspeedy.value; if (a->bultexttop < lines * -8) a->bultexttop = (signed int) a->texture->height; if (a->bultexttop > (signed int) a->texture->height) a->bultexttop = lines * -8; } #endif #if 1 if (len*8 <= a->texture->width) a->bultextleft = a->texture->width/2 - len*4; else a->bultextleft = ((int)(cl.time * bul_scrollspeedx.value) % (a->texture->width + len * 8)) - len * 8; #else if (len*8 <= a->texture->width) a->bultextleft = (a->texture->width - (len*8)) / 2; else { a->bultextleft += bul_scrollspeedx.value; if (a->bultextleft < len * -8) a->bultextleft = (signed int) a->texture->width; if (a->bultextleft > (signed int) a->texture->width) a->bultextleft = len * -8; } #endif R_MakeBulleten(a->texture, a->bultextleft, a->bultexttop, text, a->normaltexture); #ifdef GLQUAKE if (qrenderer == QR_OPENGL) { GL_Bind(a->texture->tn.base); GL_Upload8 ("bulleten", (qbyte *)a->texture + a->texture->offsets[0], a->texture->width, a->texture->height, IF_NOMIPMAP|IF_NOALPHA|IF_NOGAMMA, 0); } #endif } } // PR_SwitchProgs(mainprogs); } void Draw_CharToMip (int num, qbyte *mip, int x, int y, int width, int height) { int row, col; qbyte *source; int drawline; int i; int s, e; s = 0; e = 8; if (x<0) s = s - x; if (x > width - e) e = width - x; if (s > e) return; if (y >= height) return; if (y < -8) return; if (!draw_chars) return; if (y <= 0) mip += x; else mip += (width*y) + x; row = num>>4; col = num&15; source = draw_chars + (row<<10) + (col<<3); if (y < 0) source -= 128*y; drawline = height-y; if (drawline > 8) drawline = 8; if (y < 0) drawline += y; while (drawline--) { for (i=s ; i<e ; i++) if (source[i] != 255 && source[i]) mip[i] = source[i] + bultextpallete; source += 128; mip += width; } } void Draw_StringToMip(char *str, qbyte *mip, int x, int y, int width, int height) { int nx = x; for (; *str; str++, nx+=8) { if (*str == '\\') { str++; if (!*str) break; switch (*str) { case 'n': nx = x-8; // compensate for the 'for' increment y+=8; continue; case '\\': break; default: nx = x-8; // compensate for the 'for' increment continue; } } Draw_CharToMip(*str, mip, nx, y, width, height); } } void R_MakeBulleten (texture_t *textur, int lefttext, int toptext, char *text, qbyte *background) { qbyte bc; int x; int y; int effect; int progress; qbyte *mip; if (*text >= '0' && *text <= '9') { effect = *text - '0'; text++; } else effect = 0; if (bul_forcemode.value != -1.0) effect = bul_forcemode.value; switch (effect) { default: //solid block colour bc = bul_backcol.value; mip = (qbyte *) textur + textur->offsets[0]; Q_memset (mip, bc, textur->width*textur->height); break; case 1: //maintain background mip = (qbyte *) textur + textur->offsets[0]; memcpy (mip, background, textur->width*textur->height); break; case 2: //put in a wierd sparkly effect - interference bc = bul_sparkle.value; mip = (qbyte *) textur + textur->offsets[0]; for (x=0; x<textur->width*textur->height; x++, mip++) *mip = rand() & bc; break; case 3: //scrolling mip progress = (int) (realtime*-bul_scrollspeedx.value) % (textur->width); mip = (qbyte *) textur + textur->offsets[0] + progress * textur->height; for (x=0; x<progress; x++, mip++, background++) *mip = *background; mip = (qbyte *) textur + textur->offsets[0]; for (; x<textur->width*textur->height; x++, mip++, background++) *mip = *background; break; case 4: //water distortions mip = (qbyte *) textur + textur->offsets[0]; memcpy (mip, background, textur->width*textur->height); mip = (qbyte *) textur + textur->offsets[0]; for (y = 0; y < textur->height; y++) { for (x = 0; x < textur->width; x++) { progress = *mip & 0x0F; /* assume no full brights. if ((*mip & 0xF0) == 0xf0) { continue; } else */ if (*mip >= 0x80 && *mip < 0xe0) //backwards ranges progress = progress - (sin(((x+(sin((y/2+cl.time))*3) + (cl.time*bul_ripplespeed.value))/textur->width) * (2 * 3.14))*bul_rippleamount.value); else progress = progress + (sin(((x+(sin((y/2+cl.time))*3) + (cl.time*bul_ripplespeed.value))/textur->width) * (2 * 3.14))*bul_rippleamount.value); if (progress > 15) progress = 15; if (progress < 0) progress = 0; *mip = progress | (*mip & 0xF0); mip++; } } break; } if (*text == '\0') return; mip = (qbyte *) textur + textur->offsets[0]; Draw_StringToMip (text, mip, lefttext, toptext, textur->width, textur->height); } void Bul_ParseMessage(void) { cvar_t *cv; int num; char *str; num = MSG_ReadByte (); str = MSG_ReadString (); switch (num) { default: case 1: cv = &bul_text1; break; case 2: cv = &bul_text2; break; case 3: cv = &bul_text3; break; case 4: cv = &bul_text4; break; case 5: cv = &bul_text5; break; case 6: cv = &bul_text6; break; } Cvar_Set(cv, str); } #endif //usebulletens