diff --git a/cl_ents.c b/cl_ents.c index 848a6c4a6..6ba8f1baf 100644 --- a/cl_ents.c +++ b/cl_ents.c @@ -1107,6 +1107,16 @@ void CL_LinkPacketEntities(void) continue; } + ent.renderfx &= ~(RF_BACKPACK_FLAGS); + if (cls.mvdplayback && model->modhint == MOD_BACKPACK) { + if (cent->contents == IT_ROCKET_LAUNCHER) { + ent.renderfx |= RF_ROCKETPACK; + } + else if (cent->contents == IT_LIGHTNING) { + ent.renderfx |= RF_LGPACK; + } + } + CL_AddEntity (&ent); } } diff --git a/cl_parse.c b/cl_parse.c index a5d7c7d4a..60b27e07b 100644 --- a/cl_parse.c +++ b/cl_parse.c @@ -2998,9 +2998,41 @@ void CL_ParseStufftext (void) return; } } - else if (!strncmp(s, "//ktx race ", sizeof("//ktx race ") - 1)) { - if (!strncmp(s, "//ktx race pm ", sizeof("//ktx race pm ") - 1)) { - cl.race_pacemaker_ent = atoi(s + sizeof("//ktx race pm ") - 1); + else if (!strncmp(s, "//ktx ", sizeof("//ktx ") - 1)) { + if (!strncmp(s, "//ktx race ", sizeof("//ktx race ") - 1)) { + if (!strncmp(s, "//ktx race pm ", sizeof("//ktx race pm ") - 1)) { + cl.race_pacemaker_ent = atoi(s + sizeof("//ktx race pm ") - 1); + } + } + else if (!strcmp(s, "//ktx matchstart\n")) { + if (cls.mvdplayback) { + MVDAnnouncer_MatchStart(); + } + } + else if (!strncmp(s, "//ktx took ", sizeof("//ktx took ") - 1)) { + if (cls.mvdplayback) { + MVDAnnouncer_ItemTaken(s + 2); + } + } + else if (!strncmp(s, "//ktx timer ", sizeof("//ktx timer ") - 1)) { + if (cls.mvdplayback) { + MVDAnnouncer_StartTimer(s + 2); + } + } + else if (!strncmp(s, "//ktx drop ", sizeof("//ktx drop ") - 1)) { + if (cls.mvdplayback) { + MVDAnnouncer_PackDropped(s + 2); + } + } + else if (!strncmp(s, "//ktx expire ", sizeof("//ktx expire ") - 1)) { + if (cls.mvdplayback) { + MVDAnnouncer_Expired(s + 2); + } + } + else if (!strncmp(s, "//ktx bp ", sizeof("//ktx bp ") - 1)) { + if (cls.mvdplayback) { + MVDAnnouncer_BackpackPickup(s + 2); + } } } diff --git a/client.h b/client.h index 32bebffcf..9ec4453cd 100644 --- a/client.h +++ b/client.h @@ -208,6 +208,8 @@ typedef struct int old_vw_index; // player entities only int old_vw_frame; // player entities only + + int contents; } centity_t; #define CENT_TRAILDRAWN 1 diff --git a/gl_draw.c b/gl_draw.c index 6f48a692c..d9ec774d0 100644 --- a/gl_draw.c +++ b/gl_draw.c @@ -1057,6 +1057,8 @@ static void Draw_StringBase (int x, int y, const wchar *text, clrinfo_t *color, if (!*text) return; + Draw_SetColor(color_white, alpha); + // Turn on alpha transparency. if (gl_alphafont.value || (overall_alpha < 1.0)) { diff --git a/gl_rmain.c b/gl_rmain.c index 971e52774..bf8fd53cc 100644 --- a/gl_rmain.c +++ b/gl_rmain.c @@ -228,6 +228,8 @@ typedef struct custom_model_color_s { cvar_t fullbright_cvar; cvar_t* amf_cvar; int model_hint; + int renderfx; + qbool disable_texturing; } custom_model_color_t; custom_model_color_t custom_model_colors[] = { @@ -236,28 +238,52 @@ custom_model_color_t custom_model_colors[] = { { "gl_custom_lg_color", "", CVAR_COLOR }, { "gl_custom_lg_fullbright", "1" }, &amf_lightning, - MOD_THUNDERBOLT + MOD_THUNDERBOLT, + 0, + true }, // Rockets { { "gl_custom_rocket_color", "", CVAR_COLOR }, { "gl_custom_rocket_fullbright", "1" }, NULL, - MOD_ROCKET + MOD_ROCKET, + 0, + true }, // Grenades { { "gl_custom_grenade_color", "", CVAR_COLOR }, { "gl_custom_grenade_fullbright", "1" }, NULL, - MOD_GRENADE + MOD_GRENADE, + 0, + true }, // Spikes { { "gl_custom_spike_color", "", CVAR_COLOR }, { "gl_custom_spike_fullbright", "1" }, &amf_part_spikes, - MOD_SPIKE + MOD_SPIKE, + 0, + true + }, + { + { "gl_custom_rlpack_color", "255 64 64", CVAR_COLOR }, + { "", "0" }, + NULL, + MOD_BACKPACK, + RF_ROCKETPACK, + false + }, + { + { "gl_custom_lgpack_color", "64 64 255", CVAR_COLOR }, + { "", "0" }, + NULL, + MOD_BACKPACK, + RF_LGPACK, + false } }; @@ -608,8 +634,9 @@ void GL_DrawAliasFrame(aliashdr_t *paliashdr, int pose1, int pose2, qbool mtex, glEnable(GL_BLEND); if (custom_model) { - glDisable(GL_TEXTURE_2D); - glColor4ub(custom_model->color_cvar.color[0], custom_model->color_cvar.color[1], custom_model->color_cvar.color[2], r_modelalpha * 255); + if (custom_model->disable_texturing) { + glDisable(GL_TEXTURE_2D); + } } for ( ;; ) @@ -664,19 +691,31 @@ void GL_DrawAliasFrame(aliashdr_t *paliashdr, int pose1, int pose2, qbool mtex, else glColor4f(r_modelcolor[0] * lc[0], r_modelcolor[1] * lc[1], r_modelcolor[2] * lc[2], r_modelalpha); // forced } - else if (custom_model == NULL) - { + else if (custom_model == NULL) { if (r_modelcolor[0] < 0) { glColor4f(l, l, l, r_modelalpha); // normal color - } else { + } + else { glColor4f(r_modelcolor[0] * l, r_modelcolor[1] * l, r_modelcolor[2] * l, r_modelalpha); // forced } } + else { + if (custom_model->fullbright_cvar.name[0] && custom_model->fullbright_cvar.integer) { + l = 1; + } + + // model color + glColor4ub( + l * custom_model->color_cvar.color[0], + l * custom_model->color_cvar.color[1], + l * custom_model->color_cvar.color[2], + r_modelalpha * 255 + ); + } VectorInterpolate(verts1->v, lerpfrac, verts2->v, interpolated_verts); glVertex3fv(interpolated_verts); - verts1++; verts2++; } while (--count); @@ -684,11 +723,14 @@ void GL_DrawAliasFrame(aliashdr_t *paliashdr, int pose1, int pose2, qbool mtex, glEnd(); } - if (r_modelalpha < 1) + if (r_modelalpha < 1) { glDisable(GL_BLEND); + } if (custom_model) { - glEnable(GL_TEXTURE_2D); + if (custom_model->disable_texturing) { + glEnable(GL_TEXTURE_2D); + } custom_model = NULL; } } @@ -777,9 +819,9 @@ void R_AliasSetupLighting(entity_t *ent) clmodel = ent->model; custom_model = NULL; - for (i = 0; i < sizeof (custom_model_colors) / sizeof (custom_model_colors[0]); ++i) { + for (i = 0; i < sizeof(custom_model_colors) / sizeof(custom_model_colors[0]); ++i) { custom_model_color_t* test = &custom_model_colors[i]; - if (test->model_hint == clmodel->modhint) { + if (test->model_hint && test->model_hint == clmodel->modhint && (test->renderfx == 0 || test->renderfx == ent->renderfx)) { if (test->color_cvar.string[0] && (test->amf_cvar == NULL || test->amf_cvar->integer == 0)) { custom_model = &custom_model_colors[i]; } @@ -787,7 +829,7 @@ void R_AliasSetupLighting(entity_t *ent) } } - if (custom_model && custom_model->fullbright_cvar.integer) { + if (custom_model && custom_model->fullbright_cvar.name[0] && custom_model->fullbright_cvar.integer) { ambientlight = 4096; shadelight = 0; full_light = true; @@ -811,7 +853,6 @@ void R_AliasSetupLighting(entity_t *ent) full_light = false; ambientlight = shadelight = R_LightPoint (ent->origin); - /* FIXME: dimman... cache opt from fod */ //VULT COLOURED MODEL LIGHTS if (amf_lighting_colour.value) diff --git a/hud_common.c b/hud_common.c index b79b5b5fc..4ab585449 100644 --- a/hud_common.c +++ b/hud_common.c @@ -5382,7 +5382,7 @@ void SCR_HUD_DrawItemsClock(hud_t *hud) extern const char* MVD_AnnouncerString(int line, int total, float* alpha); int width, height; int x, y; - int i; + static cvar_t *hud_itemsclock_timelimit = NULL, *hud_itemsclock_style = NULL, @@ -7106,3 +7106,13 @@ static void SCR_Hud_GameSummary(hud_t* hud) } } } + +const char* HUD_FirstTeam(void) +{ + if (n_teams) { + return sorted_teams[0].name; + } + else { + return ""; + } +} diff --git a/mvd_utils.c b/mvd_utils.c index 50bb6361f..c6ff718be 100644 --- a/mvd_utils.c +++ b/mvd_utils.c @@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // core module of the group of MVD tools: mvd_utils, mvd_xmlstats, mvd_autotrack #include "quakedef.h" +#include #include "parser.h" #include "localtime.h" #include "gl_model.h" @@ -46,6 +47,9 @@ static char announcer_line_strings[MAX_ANNOUNCER_LINES][256]; static double announcer_line_times[MAX_ANNOUNCER_LINES]; static int announcer_lines; +static qbool mvd_ktx_markers = false; +static const char* MVD_AnnouncerTeamPlayerName(player_info_t* info); + // Can contain 'pack' #define MVD_ANNOUNCER_ITEM_LENGTH 9 @@ -63,28 +67,43 @@ mvd_gt_info_t mvd_gt_info[mvd_gt_types] = { mvd_cg_info_s mvd_cg_info; mvd_wp_info_t mvd_wp_info[mvd_info_types] = { - {AXE_INFO,"axe",IT_AXE,"axe", 0, 0, NULL, "&cf0f" }, - {SG_INFO,"sg",IT_SHOTGUN,"sg", 0, 0, NULL, "&cf0f" }, - {SSG_INFO,"ssg",IT_SUPER_SHOTGUN,"&cf0fssg&r", 0, 0, &tp_name_ssg, "&cf0f"}, - {NG_INFO,"ng",IT_NAILGUN,"&cf0fng&r", 0, 0, &tp_name_ng, "&cf0f" }, - {SNG_INFO,"sng",IT_SUPER_NAILGUN,"&cf0fsng&r", 0, 0, &tp_name_sng, "&cf0f" }, - {GL_INFO,"gl",IT_GRENADE_LAUNCHER,"&cf0fgl&r", 0, 0, &tp_name_gl, "&cf0f" }, - {RL_INFO,"rl",IT_ROCKET_LAUNCHER,"&cf0frl&r", MOD_ROCKETLAUNCHER, 0, &tp_name_rl, "&cf0f" }, - {LG_INFO,"lg",IT_LIGHTNING,"&cf0flg&r", MOD_LIGHTNINGGUN, 0, &tp_name_lg, "&cf0f" }, - {RING_INFO,"ring",IT_INVISIBILITY,"&cff0ring&r", MOD_RING, 0, &tp_name_ring, "&cff0" }, - {QUAD_INFO,"quad",IT_QUAD,"&c00fquad&r", MOD_QUAD, 0, &tp_name_quad, "&c00f" }, - {PENT_INFO,"pent",IT_INVULNERABILITY,"&cf00pent&r", MOD_PENT, 0, &tp_name_pent, "&cf00" }, - {GA_INFO,"ga",IT_ARMOR1,"&c0f0ga&r", MOD_ARMOR, 0, &tp_name_ga, "&c0f0" }, - {YA_INFO,"ya",IT_ARMOR2,"&cff0ya&r", MOD_ARMOR, 1, &tp_name_ya, "&cff0" }, - {RA_INFO,"ra",IT_ARMOR3,"&cf00ra&r", MOD_ARMOR, 2, &tp_name_ra, "&cf00" }, - {MH_INFO,"mh",IT_SUPERHEALTH,"&c00fmh&r", MOD_MEGAHEALTH, 0, &tp_name_mh, "&c00f" }, + {AXE_INFO,"axe",IT_AXE,"axe","axe", 0, 0, NULL, "&cf0f", false, false }, + {SG_INFO,"sg",IT_SHOTGUN,"sg","sg", 0, 0, NULL, "&cf0f", false, false }, + {SSG_INFO,"ssg",IT_SUPER_SHOTGUN,"&cf0fssg&r", "&cf0fssg pack&r", 0, 0, &tp_name_ssg, "&cf0f", false, false }, + {NG_INFO,"ng",IT_NAILGUN,"&cf0fng&r", "&cf0fng pack&r", 0, 0, &tp_name_ng, "&cf0f", false, false }, + {SNG_INFO,"sng",IT_SUPER_NAILGUN,"&cf0fsng&r", "&cf0fsng pack&r", 0, 0, &tp_name_sng, "&cf0f", false, false }, + {GL_INFO,"gl",IT_GRENADE_LAUNCHER,"&cf0fgl&r", "&cf0fgl pack&r", 0, 0, &tp_name_gl, "&cf0f", false, false }, + {RL_INFO,"rl",IT_ROCKET_LAUNCHER,"&cf0frl&r", "&cf0frl pack&r", MOD_ROCKETLAUNCHER, 0, &tp_name_rl, "&cf0f", true, false }, + {LG_INFO,"lg",IT_LIGHTNING,"&cf0flg&r", "&cf0flg pack&r", MOD_LIGHTNINGGUN, 0, &tp_name_lg, "&cf0f", true, false }, + {RING_INFO,"ring",IT_INVISIBILITY,"&cff0ring&r", "", MOD_RING, 0, &tp_name_ring, "&cff0", true, true }, + {QUAD_INFO,"quad",IT_QUAD,"&c00fquad&r", "", MOD_QUAD, 0, &tp_name_quad, "&c44f", true, true }, + {PENT_INFO,"pent",IT_INVULNERABILITY,"&cf00pent&r", "", MOD_PENT, 0, &tp_name_pent, "&cf44", true, true }, + {GA_INFO,"ga",IT_ARMOR1,"&c0f0ga&r", "", MOD_ARMOR, 0, &tp_name_ga, "&c0f0", true, false }, + {YA_INFO,"ya",IT_ARMOR2,"&cff0ya&r", "", MOD_ARMOR, 1, &tp_name_ya, "&cff0", true, false }, + {RA_INFO,"ra",IT_ARMOR3,"&cf00ra&r", "", MOD_ARMOR, 2, &tp_name_ra, "&cf00", true, false }, + {MH_INFO,"mh",IT_SUPERHEALTH,"&c00fmh&r", "", MOD_MEGAHEALTH, 0, &tp_name_mh, "&c7ff", true, false }, }; static int item_counts[mvd_info_types]; +#define MVDCLOCK_PERSISTENT 1 +#define MVDCLOCK_BACKPACK 2 +#define MVDCLOCK_BACKPACK_REMOVED 4 + +#define ITEMSCLOCK_TAKEN_PAUSE 4 // in seconds + typedef struct mvd_clock_t { int itemtype; // RA, Quad, RL, ... double clockval; // time when the clock expires char location[MAX_MACRO_STRING]; // Player location when the object was picked up + int flags; // flags + int entity; // entity number + int last_taken_by; // player entity item was last taken by (0 for unknown) + int dropped_by; // player entity who dropped it (0 for unknown) + double last_taken; // time when item was last taken + double old_clockval; // used to briefly keep items in same order as they're taken + float hold_clockval; // time when the player's hold-time will expire + int order; // for static ordering + struct mvd_clock_t *next; // next item in the linked list struct mvd_clock_t *prev; // prev item in the linked list } mvd_clock_t; @@ -177,6 +196,7 @@ int quad_mentioned = 0; int powerup_cam_active = 0; int cam_1,cam_2,cam_3,cam_4; static qbool was_standby = true; +static int fixed_ordering = 0; extern cvar_t tp_name_none, tp_weapon_order; @@ -222,6 +242,8 @@ cvar_t mvd_pc_view_3 = {"mvd_pc_view_3",""}; cvar_t mvd_pc_view_4 = {"mvd_pc_view_4",""}; cvar_t mvd_moreinfo = {"mvd_moreinfo","0"}; +cvar_t mvd_autoadd_items = { "mvd_autoadd_items", "1" }; +cvar_t mvd_sortitems = { "mvd_sortitems", "1" }; typedef struct bp_var_s{ int id; @@ -425,16 +447,80 @@ static void MVD_ClockStart(int itemtype, vec3_t origin) if (origin) { strlcpy(newclock->location, TP_LocationName(origin), sizeof(newclock->location)); } + newclock->order = 0; + MVD_ClockList_Insert(newclock); +} + +static mvd_clock_t* MVD_ClockStartEntity(int entity, int itemtype, int flags) +{ + mvd_clock_t* newclock = (mvd_clock_t*) Q_malloc(sizeof(mvd_clock_t)); + newclock->clockval = 0; + newclock->itemtype = itemtype; + strlcpy(newclock->location, TP_LocationName(cl_entities[entity].baseline.origin), sizeof(newclock->location)); + newclock->flags = flags; + newclock->entity = entity; + newclock->order = ++fixed_ordering; MVD_ClockList_Insert(newclock); + return newclock; +} + +static mvd_clock_t* MVD_ClockFindEntity(int entity) +{ + mvd_clock_t *current; + + for (current = mvd_clocklist; current; current = current->next) { + if (current->entity == entity) { + return current; + } + } + + return NULL; } void MVD_ClockList_RemoveExpired(void) { - mvd_clock_t *current = mvd_clocklist; + mvd_clock_t *current; + + for (current = mvd_clocklist; current; ) { + // We don't remove persistent counters + if (!(current->flags & MVDCLOCK_PERSISTENT)) { + // Expired + if (current->clockval + 1 < cls.demotime) { + current = MVD_ClockList_Remove(current); + continue; + } - while (current && current->clockval + 1 < cls.demotime) { - // we keep the item there for 1 second so that "spawn" is displayed - current = MVD_ClockList_Remove(current); + // + if (current->entity && !(current->flags & MVDCLOCK_BACKPACK_REMOVED)) { + int mod = cl_entities[current->entity].current.modelindex; + if (mod <= 0 || mod >= sizeof(cl.model_precache) / sizeof(cl.model_precache[0])) { + if (current->last_taken) { + // Backpack has been picked up, disconnect from entity + current->flags &= ~(MVDCLOCK_BACKPACK); + current->flags |= MVDCLOCK_BACKPACK_REMOVED; + current->entity = 0; + } + else { + current = MVD_ClockList_Remove(current); + } + continue; + } + + if (cl_entities[current->entity].sequence < cl.validsequence) { + // Backpack has been picked up or disappeared, disconnect from entity and let expire + current->flags &= ~(MVDCLOCK_BACKPACK); + current->flags |= MVDCLOCK_BACKPACK_REMOVED; + current->entity = 0; + } + else if (cl.model_precache[mod] == NULL || cl.model_precache[mod]->modhint != MOD_BACKPACK) { + // No longer a backpack, remove + current = MVD_ClockList_Remove(current); + continue; + } + } + } + + current = current->next; } } @@ -455,13 +541,111 @@ int MVD_ClockList_GetLongestName(void) return longest; } +static double MVD_ClockList_SortTime(mvd_clock_t* c) +{ + double t = c->clockval; + + if (c->last_taken && cls.demotime - c->last_taken < ITEMSCLOCK_TAKEN_PAUSE) { + // item has just been taken, keep to the start of the list + t = -1 - c->old_clockval; + } + else if (c->last_taken && t == -1) { + // megahealth still held by player, put to end of list + t = cls.demotime + c->last_taken + 1000; + } + + return t; +} + +// Moves persistent +static int MVD_ClockList_Compare(mvd_clock_t* c, mvd_clock_t* n) +{ + double c_time = MVD_ClockList_SortTime(c); + double n_time = MVD_ClockList_SortTime(n); + qbool c_persistent = c->flags & MVDCLOCK_PERSISTENT; + qbool n_persistent = n->flags & MVDCLOCK_PERSISTENT; + + if (c_persistent && !n_persistent) { + return 1; + } + if (!c_persistent && n_persistent) { + return -1; + } + + if (mvd_sortitems.integer) { + return c_time - n_time; + } + else { + return c->order - n->order; + } +} + +static void MVD_ClockList_Sort(void) +{ + qbool any_change = true; + + while (any_change) { + mvd_clock_t *c = mvd_clocklist; + + any_change = false; + while (c) { + mvd_clock_t *n = c->next; + int comparison; + + if (!n) { + break; + } + + comparison = MVD_ClockList_Compare(c, n); + if (comparison > 0) { + c->next = n->next; + n->prev = c->prev; + c->prev = n; + n->next = c; + + if (n->prev) { + n->prev->next = n; + } + if (c->next) { + c->next->prev = c; + } + + if (c == mvd_clocklist) { + mvd_clocklist = n; + } + + any_change = true; + } + else { + c = n; + } + } + } +} + void MVD_ClockList_TopItems_DimensionsGet(double time_limit, int style, int *width, int *height, float scale) { int lines = 0; mvd_clock_t *current = mvd_clocklist; + int length = 0; + int persistent = 0, temporary = 0; - while (current && current->clockval - cls.demotime < time_limit) { - lines++; + if (style == 5) { + MVD_ClockList_Sort(); + + current = mvd_clocklist; + } + + while (current) { + if (current->entity || current->clockval - cls.demotime < time_limit) { + if (current->flags & MVDCLOCK_PERSISTENT) { + ++persistent; + } + else { + ++temporary; + } + lines++; + } current = current->next; } @@ -472,94 +656,222 @@ void MVD_ClockList_TopItems_DimensionsGet(double time_limit, int style, int *wid else if (style == 3) { *width = LETTERWIDTH * (2 + sizeof(" spawn") - 1) * scale; } + else if (style == 4) { + *width = LETTERWIDTH * (2 + sizeof(" spawn") - 1) * scale; + } else { *width = LETTERWIDTH * (sizeof("QUAD spawn") - 1) * scale; } + if (persistent && temporary) { + lines++; + } + *height = LETTERHEIGHT * lines * scale * (style == 3 ? 2 : 1); } +static qbool MVD_ClockIsHeld(mvd_clock_t* current, qbool test_held, float* alpha) +{ + double time_since; + + if (alpha) { + *alpha = 1.0f; + } + + if (!current->entity || (test_held && current->hold_clockval <= cls.demotime)) { + return false; + } + + if (current->last_taken_by <= 0 || current->last_taken_by > MAX_CLIENTS || !cl.players[current->last_taken_by - 1].name[0]) { + return false; + } + + if (test_held && (cl.players[current->last_taken_by - 1].stats[STAT_ITEMS] & mvd_wp_info[current->itemtype].it)) { + return true; + } + + if (!current->last_taken || current->last_taken > cls.demotime) { + return false; + } + + time_since = (cls.demotime - current->last_taken); + if (alpha) { + if (time_since > ITEMSCLOCK_TAKEN_PAUSE - 1 && time_since < ITEMSCLOCK_TAKEN_PAUSE) { + *alpha = (ITEMSCLOCK_TAKEN_PAUSE - time_since); + } + else if (time_since >= ITEMSCLOCK_TAKEN_PAUSE && time_since < ITEMSCLOCK_TAKEN_PAUSE + 1) { + *alpha = (time_since - ITEMSCLOCK_TAKEN_PAUSE); + } + } + return time_since < ITEMSCLOCK_TAKEN_PAUSE; +} + void MVD_ClockList_TopItems_Draw(double time_limit, int style, int x, int y, float scale, int filter) { mvd_clock_t *current = mvd_clocklist; char clockitem[128]; - char temp[16]; + char temp[128]; + qbool was_persistent = true; + int base_x = x; - while (current && current->clockval - cls.demotime < time_limit) { - int time = (int) ((current->clockval - cls.demotime) + 1); - int texture = Mod_SimpleTextureForHint(mvd_wp_info[current->itemtype].model_hint, mvd_wp_info[current->itemtype].skin_number); + while (current) { + x = base_x; + float alpha = 1.0f; - if (filter & mvd_wp_info[current->itemtype].it) { - current = current->next; - continue; - } + if (current->entity || current->clockval - cls.demotime < time_limit) { + int time = (int)((current->clockval - cls.demotime) + 1); + int texture = Mod_SimpleTextureForHint(mvd_wp_info[current->itemtype].model_hint, mvd_wp_info[current->itemtype].skin_number); - if (style == 1) { - // tp_name_* - strlcpy(clockitem, TP_ItemName(mvd_wp_info[current->itemtype].it), sizeof(clockitem)); - } - else if (style == 2) { - // brown + white - strlcpy(clockitem, mvd_wp_info[current->itemtype].name, sizeof(clockitem)); - CharsToBrown(clockitem, clockitem + strlen(clockitem)); - } - else if (style == 3 && texture) { - // simpleitem - strlcpy(clockitem, " ", sizeof(clockitem)); - Draw_2dAlphaTexture(x, y, 2 * LETTERWIDTH * scale, 2 * LETTERHEIGHT * scale, texture, 1.0f); - y += LETTERHEIGHT * scale / 2; - } - else if (style == 4) { - char item_name[MAX_MACRO_STRING]; + if (filter & mvd_wp_info[current->itemtype].it) { + current = current->next; + continue; + } - if (mvd_wp_info[current->itemtype].name_cvar && mvd_wp_info[current->itemtype].name_cvar->string[0]) { - Util_SkipChars(mvd_wp_info[current->itemtype].name_cvar->string, "{}", item_name, sizeof(item_name)); + if ((current->flags & MVDCLOCK_PERSISTENT) && !was_persistent) { + y += LETTERHEIGHT * scale; } - else { - strlcpy(item_name, mvd_wp_info[current->itemtype].colored_name, sizeof(item_name)); + was_persistent = (current->flags & MVDCLOCK_PERSISTENT); + + if (style == 1) { + // tp_name_* + strlcpy(clockitem, TP_ItemName(mvd_wp_info[current->itemtype].it), sizeof(clockitem)); + } + else if (style == 2) { + // brown + white + strlcpy(clockitem, mvd_wp_info[current->itemtype].name, sizeof(clockitem)); + CharsToBrown(clockitem, clockitem + strlen(clockitem)); + } + else if (style == 3 && texture) { + // simpleitem + strlcpy(clockitem, " ", sizeof(clockitem)); + Draw_2dAlphaTexture(x, y, 2 * LETTERWIDTH * scale, 2 * LETTERHEIGHT * scale, texture, 1.0f); + y += LETTERHEIGHT * scale / 2; } + else if (style == 4) { + char item_name[MAX_MACRO_STRING]; - snprintf(clockitem, sizeof(clockitem), "%*s", MVD_ANNOUNCER_ITEM_LENGTH + (strlen(item_name) - strlen_color(item_name)), item_name); + if (mvd_wp_info[current->itemtype].name_cvar && mvd_wp_info[current->itemtype].name_cvar->string[0]) { + Util_SkipChars(mvd_wp_info[current->itemtype].name_cvar->string, "{}", item_name, sizeof(item_name)); + } + else { + strlcpy(item_name, mvd_wp_info[current->itemtype].colored_name, sizeof(item_name)); + } - strlcat(clockitem, " \034", sizeof(clockitem)); - } - else { - // built-in color(GL) or simple white (software) - if (mvd_wp_info[current->itemtype].name_cvar) { - strlcpy(clockitem, mvd_wp_info[current->itemtype].colored_name, sizeof(clockitem)); + snprintf(clockitem, sizeof(clockitem), "%*s", MVD_ANNOUNCER_ITEM_LENGTH + (strlen(item_name) - strlen_color(item_name)), item_name); + + strlcat(clockitem, " \034", sizeof(clockitem)); + } + else if (style == 5) { + char item_name[MAX_MACRO_STRING]; + + if (current->flags & (MVDCLOCK_BACKPACK | MVDCLOCK_BACKPACK_REMOVED)) { + const char* name = mvd_wp_info[current->itemtype].colored_packname; + + snprintf(item_name, sizeof(item_name) - 1, "%*s", MVD_ANNOUNCER_ITEM_LENGTH + (strlen(name) - strlen_color(name)), name); + } + else { + const char* color = mvd_wp_info[current->itemtype].color_string; + + snprintf(item_name, sizeof(item_name) - 1, "%s%*s&r", color ? color : "", MVD_ANNOUNCER_ITEM_LENGTH + (strlen(current->location) - strlen_color(current->location)), current->location); + } + + strlcpy(clockitem, item_name, sizeof(clockitem)); + strlcat(clockitem, " \034", sizeof(clockitem)); + Draw_SString(x, y, clockitem, scale); + + x += strlen_color(clockitem) * 8; + clockitem[0] = '\0'; } else { - strlcpy(clockitem, mvd_wp_info[current->itemtype].colored_name, sizeof(clockitem)); + // built-in color(GL) or simple white (software) + if (mvd_wp_info[current->itemtype].name_cvar) { + strlcpy(clockitem, mvd_wp_info[current->itemtype].colored_name, sizeof(clockitem)); + } + else { + strlcpy(clockitem, mvd_wp_info[current->itemtype].colored_name, sizeof(clockitem)); + } } - } - if (time > 0) { - snprintf(temp, sizeof(temp), " %d", time); - strlcat(clockitem, temp, sizeof(clockitem)); - } - else { - strlcat(clockitem, " spawn", sizeof(clockitem)); - } + if (current->flags & MVDCLOCK_BACKPACK_REMOVED) { + if (current->location[0]) { + strlcat(clockitem, " ", sizeof(clockitem)); + strlcat(clockitem, current->location, sizeof(clockitem)); + } - if (style == 4 && item_counts[current->itemtype] != 1 && current->location[0]) { - strlcat(clockitem, " \020", sizeof(clockitem)); - strlcat(clockitem, current->location, sizeof(clockitem)); - strlcat(clockitem, "\021", sizeof(clockitem)); - } + if (current->dropped_by && current->last_taken_by) { + strlcat(clockitem, " (", sizeof(clockitem)); + strlcat(clockitem, MVD_AnnouncerTeamPlayerName(&cl.players[current->dropped_by - 1]), sizeof(clockitem)); + strlcat(clockitem, " \015 ", sizeof(clockitem)); + strlcat(clockitem, MVD_AnnouncerTeamPlayerName(&cl.players[current->last_taken_by - 1]), sizeof(clockitem)); + strlcat(clockitem, ")", sizeof(clockitem)); + } + else { + current = MVD_ClockList_Remove(current); + continue; + } + } + else if (time > 0) { + if (current->flags & MVDCLOCK_BACKPACK) { + strlcpy(current->location, TP_LocationName(cl_entities[current->entity].current.origin), sizeof(current->location)); - Draw_SString (x, y, clockitem, scale); + snprintf(temp, sizeof(temp), " %s", current->location); - current = current->next; - y += LETTERHEIGHT * scale; - if (style == 3) { - y += LETTERHEIGHT * scale / 2; + if (current->dropped_by) { + strlcat(temp, " (", sizeof(temp)); + strlcat(temp, MVD_AnnouncerTeamPlayerName(&cl.players[current->dropped_by - 1]), sizeof(temp)); + strlcat(temp, ")", sizeof(temp)); + } + } + else if (MVD_ClockIsHeld(current, mvd_wp_info[current->itemtype].show_held, &alpha)) { + snprintf(temp, sizeof(temp), " %s", MVD_AnnouncerTeamPlayerName(&cl.players[current->last_taken_by - 1])); + } + else if (style == 5) { + if (time >= 60) { + snprintf(temp, sizeof(temp), " %d:%02d", time / 60, time % 60); + } + else { + snprintf(temp, sizeof(temp), " %d", time); + } + } + else { + snprintf(temp, sizeof(temp), " %*d", time_limit >= 10 ? 2 : 1, time); + } + strlcat(clockitem, temp, sizeof(clockitem)); + } + else if (current->flags & MVDCLOCK_PERSISTENT) { + strlcpy(temp, " up", sizeof(temp)); + + // If a player is holding the item, use their name instead + if (MVD_ClockIsHeld(current, true, NULL)) { + snprintf(temp, sizeof(temp), " %s", MVD_AnnouncerTeamPlayerName(&cl.players[current->last_taken_by - 1])); + } + + strlcat(clockitem, temp, sizeof(clockitem)); + } + else { + strlcat(clockitem, " spawn", sizeof(clockitem)); + } + + if (style == 4 && item_counts[current->itemtype] != 1 && current->location[0]) { + strlcat(clockitem, " \020", sizeof(clockitem)); + strlcat(clockitem, current->location, sizeof(clockitem)); + strlcat(clockitem, "\021", sizeof(clockitem)); + } + + Draw_SStringAlpha(x, y, clockitem, scale, alpha); + + y += LETTERHEIGHT * scale; + if (style == 3) { + y += LETTERHEIGHT * scale / 2; + } } + current = current->next; } } static void MVD_Took(int player, int item, qbool addclock) { - if (mvd_new_info[player].mvdinfo.initialized) { + if (mvd_new_info[player].mvdinfo.initialized && !mvd_ktx_markers) { if (addclock) { MVD_ClockStart(item, mvd_new_info[player].p_state ? mvd_new_info[player].p_state->origin : NULL); } @@ -778,36 +1090,48 @@ const char* MVD_AnnouncerString(int line, int total, float* alpha) } } -static const char* MVD_AnnouncerPlayerName(int i) +static const char* MVD_AnnouncerTeamPlayerName(player_info_t* info) { if (cl.teamplay) { - int player_color = Sbar_BottomColor(mvd_new_info[i].p_info); + int player_color = Sbar_BottomColor(info); byte red = host_basepal[player_color * 3]; byte green = host_basepal[player_color * 3 + 1]; byte blue = host_basepal[player_color * 3 + 2]; + char* buf; - if (mvd_new_info[i].p_info->bottomcolor == 3) { + if (info->bottomcolor == 3) { red = 99; green = 255; blue = 120; } - else if (mvd_new_info[i].p_info->bottomcolor == 12) { + else if (info->bottomcolor == 12) { red = green = 255; blue = 0; } - return va("&c%x%x%x%s&r", (unsigned int)(red / 16), (unsigned int)(green / 16), (unsigned int)(blue / 16), mvd_new_info[i].p_info->name); + buf = va("&c%x%x%x%s&r", (unsigned int)(red / 16), (unsigned int)(green / 16), (unsigned int)(blue / 16), info->name); + CharsToWhite(buf, buf + strlen(buf)); + return buf; } else { - return mvd_new_info[i].p_info->name; + return info->name; } } +static const char* MVD_AnnouncerPlayerName(int i) +{ + return MVD_AnnouncerTeamPlayerName(mvd_new_info[i].p_info); +} + void MVD_Status_Announcer(int i, int z){ //char *pn = mvd_new_info[i].p_info->name; vec3_t *pl = &mvd_new_info[i].p_state->origin; + if (mvd_ktx_markers) { + return; + } + if (mvd_new_info[i].mvdinfo.itemstats[z].mention > 0) { int mention = mvd_new_info[i].mvdinfo.itemstats[z].mention; @@ -917,11 +1241,12 @@ void MVD_Status_WP(int i, int *taken){ } -void MVD_Stats_Cleanup (void){ - quad_is_active=0; - pent_is_active=0; - powerup_cam_active=0; - cam_1=cam_2=cam_3=cam_4=0; +void MVD_Stats_Cleanup(void) +{ + quad_is_active = 0; + pent_is_active = 0; + powerup_cam_active = 0; + cam_1 = cam_2 = cam_3 = cam_4 = 0; was_standby = true; while (mvd_clocklist) { MVD_ClockList_Remove(mvd_clocklist); @@ -929,6 +1254,7 @@ void MVD_Stats_Cleanup (void){ memset(&mvd_new_info, 0, sizeof(mvd_new_info_t)); memset(&mvd_cg_info, 0, sizeof(mvd_cg_info_s)); + fixed_ordering = 0; } void MVD_Set_Armor_Stats(int z,int i){ @@ -1145,7 +1471,9 @@ static void MVD_Stats_Gather_AlivePlayer(int player_index) int megas = mvd_new_info[i].mvdinfo.itemstats[MH_INFO].has; while (megas > 0) { --megas; - MVD_ClockStart(MH_INFO, megas < MAX_MEGAS_PER_PLAYER ? mvd_new_info[i].mega_locations[megas] : NULL); + if (!mvd_ktx_markers) { + MVD_ClockStart(MH_INFO, megas < MAX_MEGAS_PER_PLAYER ? mvd_new_info[i].mega_locations[megas] : NULL); + } } mvd_new_info[i].mvdinfo.itemstats[MH_INFO].has = 0; } @@ -1689,6 +2017,151 @@ void MVD_Powerup_Cams(void){ } } +static void MVDAnnouncer_HelpListItems(void) +{ + int i; + + Con_Printf("Valid types: "); + for (i = 0; i < sizeof(mvd_wp_info) / sizeof(mvd_wp_info[0]); ++i) { + if (i) { + Con_Printf(","); + } + Con_Printf("%s", mvd_wp_info[i].name); + } + Con_Printf("\n"); +} + +static int MVDAnnouncer_FindEntity(const vec3_t pos, int type) +{ + int j; + + for (j = 0; j < CL_MAX_EDICTS; j++) { + int modindex = cl_entities[j].baseline.modelindex; + int skin = cl_entities[j].baseline.skinnum; + + if (modindex >= 0 && modindex < sizeof(cl.model_precache) / sizeof(cl.model_precache[0]) && cl.model_precache[modindex]) { + if (cl.model_precache[modindex]->modhint == mvd_wp_info[type].model_hint && skin == mvd_wp_info[type].skin_number) { + vec3_t distance; + VectorSubtract(cl_entities[j].baseline.origin, pos, distance); + if (VectorLength(distance) < 50) { + return j; + } + } + } + } + + return 0; +} + +static void MVDAnnouncer_RemoveItem(void) +{ + vec3_t pos; + const char* type; + int i; + mvd_clock_t* clock_entry; + + if (Cmd_Argc() < 5) { + Con_Printf("Removes a persistent item clock for item at given position\n"); + Con_Printf("Usage: %s \n", Cmd_Argv(0)); + MVDAnnouncer_HelpListItems(); + return; + } + + pos[0] = atof(Cmd_Argv(1)); + pos[1] = atof(Cmd_Argv(2)); + pos[2] = atof(Cmd_Argv(3)); + + type = Cmd_Argv(4); + + for (i = 0; i < sizeof(mvd_wp_info) / sizeof(mvd_wp_info[0]); ++i) { + if (!strcmp(type, mvd_wp_info[i].name)) { + int ent = MVDAnnouncer_FindEntity(pos, i); + + if (ent) { + for (clock_entry = mvd_clocklist; clock_entry; clock_entry = clock_entry->next) { + if ((clock_entry->flags & MVDCLOCK_PERSISTENT) && clock_entry->entity == ent) { + MVD_ClockList_Remove(clock_entry); + return; + } + } + + // Didn't exist, so... never mind + return; + } + + Con_Printf("No item found at specified location\n"); + return; + } + } + + Con_Printf("Invalid type specified\n"); + return; +} + +static void MVDAnnouncer_ListItems(void) +{ + mvd_clock_t* clock_entry; + + for (clock_entry = mvd_clocklist; clock_entry; clock_entry = clock_entry->next) { + if ((clock_entry->flags & MVDCLOCK_PERSISTENT) && clock_entry->entity) { + Con_Printf("mvd_name_item %d %d %d %s \"%s\" // entity %d\n", + (int)cl_entities[clock_entry->entity].baseline.origin[0], + (int)cl_entities[clock_entry->entity].baseline.origin[1], + (int)cl_entities[clock_entry->entity].baseline.origin[2], + mvd_wp_info[clock_entry->itemtype].name, + clock_entry->location, + clock_entry->entity + ); + } + } +} + +static void MVDAnnouncer_NameItem(void) +{ + vec3_t pos; + const char* type; + const char* label; + int i; + + if (Cmd_Argc() < 6) { + Con_Printf("Creates a persistent item clock for item at given position\n"); + Con_Printf("Usage: %s