]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'Mario/config_updates'
authorMario <zacjardine@y7mail.com>
Thu, 13 Nov 2014 14:08:26 +0000 (01:08 +1100)
committerMario <zacjardine@y7mail.com>
Thu, 13 Nov 2014 14:08:26 +0000 (01:08 +1100)
60 files changed:
.DS_Store [new file with mode: 0644]
_hud_descriptions.cfg
defaultXonotic.cfg
gamemodes.cfg
gfx/.DS_Store [new file with mode: 0644]
gfx/hud/.DS_Store [new file with mode: 0644]
hud_luminos.cfg
hud_luminos_minimal.cfg
hud_luminos_minimal_xhair.cfg
hud_luminos_old.cfg
hud_nexuiz.cfg
models/.DS_Store [new file with mode: 0644]
models/player/.DS_Store [new file with mode: 0644]
models/weapons/.DS_Store [new file with mode: 0644]
mutators.cfg
qcsrc/.DS_Store [new file with mode: 0644]
qcsrc/client/.DS_Store [new file with mode: 0644]
qcsrc/client/Main.qc
qcsrc/client/autocvars.qh
qcsrc/client/hud.qc
qcsrc/client/hud.qh
qcsrc/client/mapvoting.qc
qcsrc/client/movetypes.qc
qcsrc/client/progs.src
qcsrc/client/waypointsprites.qc
qcsrc/common/.DS_Store [new file with mode: 0644]
qcsrc/common/buffs.qc [new file with mode: 0644]
qcsrc/common/buffs.qh [new file with mode: 0644]
qcsrc/common/constants.qh
qcsrc/common/deathtypes.qh
qcsrc/common/mapinfo.qc
qcsrc/common/mapinfo.qh
qcsrc/common/notifications.qh
qcsrc/common/stats.qh [new file with mode: 0644]
qcsrc/common/weapons/.DS_Store [new file with mode: 0644]
qcsrc/menu/.DS_Store [new file with mode: 0644]
qcsrc/menu/classes.c
qcsrc/menu/xonotic/.DS_Store [new file with mode: 0644]
qcsrc/menu/xonotic/dialog_hudpanel_buffs.c [new file with mode: 0644]
qcsrc/menu/xonotic/mainwindow.c
qcsrc/menu/xonotic/playermodel.c
qcsrc/menu/xonotic/skinlist.c
qcsrc/server/.DS_Store [new file with mode: 0644]
qcsrc/server/autocvars.qh
qcsrc/server/cl_client.qc
qcsrc/server/cl_physics.qc
qcsrc/server/cl_weaponsystem.qc
qcsrc/server/g_world.qc
qcsrc/server/mapvoting.qc [new file with mode: 0644]
qcsrc/server/mapvoting.qh [new file with mode: 0644]
qcsrc/server/miscfunctions.qc
qcsrc/server/mutators/base.qh
qcsrc/server/mutators/mutator_buffs.qc [new file with mode: 0644]
qcsrc/server/mutators/mutator_buffs.qh [new file with mode: 0644]
qcsrc/server/mutators/mutators.qh
qcsrc/server/progs.src
qcsrc/server/t_quake3.qc
scripts/.DS_Store [new file with mode: 0644]
textures/.DS_Store [new file with mode: 0644]
xonotic-credits.txt

diff --git a/.DS_Store b/.DS_Store
new file mode 100644 (file)
index 0000000..09de22f
Binary files /dev/null and b/.DS_Store differ
index c9a0861556893d5aa382bfe98ec2e4626db42a9f..270bd2bd9294693bcbf787666f7b3b2d341e2141 100644 (file)
@@ -297,3 +297,13 @@ seta hud_panel_centerprint_fade_subsequent_passtwo "" "division factor for the s
 seta hud_panel_centerprint_fade_subsequent_passtwo_minalpha "" "minimum factor that the second pass can fade to"
 seta hud_panel_centerprint_fade_subsequent_minfontsize "" "minimum factor for the font size from the subsequent fading effects"
 seta hud_panel_centerprint_fade_minfontsize "" "minimum factor for the font size from the fading in/out effects"
+
+seta hud_panel_buffs "" "enable/disable this panel"
+seta hud_panel_buffs_pos "" "position of this panel"
+seta hud_panel_buffs_size "" "size of this panel"
+seta hud_panel_buffs_bg "" "if set to something else than \"\" = override default background"
+seta hud_panel_buffs_bg_color "" "if set to something else than \"\" = override default panel background color"
+seta hud_panel_buffs_bg_color_team "" "override panel color with team color in team based games"
+seta hud_panel_buffs_bg_alpha "" "if set to something else than \"\" = override default panel background alpha"
+seta hud_panel_buffs_bg_border "" "if set to something else than \"\" = override default size of border around the background"
+seta hud_panel_buffs_bg_padding "" "if set to something else than \"\" = override default padding of contents from border"
index de15b2482a1f6f5d6d7ebc64a68b2c75153a281e..18af3e8951af7491b789083dc8c097f249327cc2 100644 (file)
@@ -813,6 +813,12 @@ seta g_maplist_votable_nodetail 1  "nodetail only shows total count instead of al
 seta g_maplist_votable_abstain 0       "when 1, you can abstain from your vote"
 seta g_maplist_votable_screenshot_dir "maps levelshots"        "where to look for map screenshots"
 
+set sv_vote_gametype 0 "show a vote screen for gametypes before map vote screen"
+set sv_vote_gametype_keeptwotime 10 "show only 2 options for this amount of time during gametype vote screen"
+set sv_vote_gametype_options "dm ctf ca lms tdm ft"
+set sv_vote_gametype_timeout 20
+set sv_vote_gametype_default_current 1 "Keep the current gametype if no one votes"
+
 set g_chat_flood_spl 3 "normal chat: seconds between lines to not count as flooding"
 set g_chat_flood_lmax 2        "normal chat: maximum number of lines per chat message at once"
 set g_chat_flood_burst 2       "normal chat: allow bursts of so many chat lines"
index dfc240536e655cc16867fbee950cb57046e0c681..2b8874cd2e10fa22fa273e6aaebdf2bce8f5ed21 100644 (file)
@@ -62,6 +62,28 @@ alias sv_hook_gamerestart
 alias sv_hook_gameend
 
 
+// =====================
+//  gametype vote hooks
+// =====================
+// these are called when the mode is switched via gametype vote screen, earlier than gamestart hooks (useful for enabling per-gamemode mutators)
+alias sv_vote_gametype_hook_all 
+alias sv_vote_gametype_hook_as
+alias sv_vote_gametype_hook_ca
+alias sv_vote_gametype_hook_ctf
+alias sv_vote_gametype_hook_cts
+alias sv_vote_gametype_hook_dm
+alias sv_vote_gametype_hook_dom
+alias sv_vote_gametype_hook_ft
+alias sv_vote_gametype_hook_inv
+alias sv_vote_gametype_hook_ka
+alias sv_vote_gametype_hook_kh
+alias sv_vote_gametype_hook_lms
+alias sv_vote_gametype_hook_nb
+alias sv_vote_gametype_hook_ons
+alias sv_vote_gametype_hook_rc
+alias sv_vote_gametype_hook_tdm
+
+
 // ===========
 //  leadlimit
 // ===========
diff --git a/gfx/.DS_Store b/gfx/.DS_Store
new file mode 100644 (file)
index 0000000..d052ce6
Binary files /dev/null and b/gfx/.DS_Store differ
diff --git a/gfx/hud/.DS_Store b/gfx/hud/.DS_Store
new file mode 100644 (file)
index 0000000..127a7c9
Binary files /dev/null and b/gfx/hud/.DS_Store differ
index 2736524c6485bfda302d67eb69869da4110d02b4..f3bc914e6a48be696bd9e3b8879eb90d9ab0254f 100644 (file)
@@ -296,4 +296,14 @@ seta hud_panel_centerprint_fade_subsequent_passtwo_minalpha "0.5"
 seta hud_panel_centerprint_fade_subsequent_minfontsize "0.75"
 seta hud_panel_centerprint_fade_minfontsize "0"
 
+seta hud_panel_buffs 1
+seta hud_panel_buffs_pos "0.450000 0.855000"
+seta hud_panel_buffs_size "0.050000 0.070000"
+seta hud_panel_buffs_bg "0"
+seta hud_panel_buffs_bg_color ""
+seta hud_panel_buffs_bg_color_team ""
+seta hud_panel_buffs_bg_alpha ""
+seta hud_panel_buffs_bg_border ""
+seta hud_panel_buffs_bg_padding ""
+
 menu_sync
index fdb57a37ee521cb3cd75bd29e51e5909435f65e6..050689b38caf95d525f07e83f378649fc49dafff 100644 (file)
@@ -296,4 +296,14 @@ seta hud_panel_centerprint_fade_subsequent_passtwo_minalpha "0.5"
 seta hud_panel_centerprint_fade_subsequent_minfontsize "0.75"
 seta hud_panel_centerprint_fade_minfontsize "0"
 
+seta hud_panel_buffs 1
+seta hud_panel_buffs_pos "0.450000 0.855000"
+seta hud_panel_buffs_size "0.050000 0.070000"
+seta hud_panel_buffs_bg "0"
+seta hud_panel_buffs_bg_color ""
+seta hud_panel_buffs_bg_color_team ""
+seta hud_panel_buffs_bg_alpha ""
+seta hud_panel_buffs_bg_border ""
+seta hud_panel_buffs_bg_padding ""
+
 menu_sync
index e0345f822ee25da1e154aee0d54109ebb0f8b495..8fb6cbe93da88fd67f099c634126551449435fdf 100644 (file)
@@ -296,4 +296,14 @@ seta hud_panel_centerprint_fade_subsequent_passtwo_minalpha "0.5"
 seta hud_panel_centerprint_fade_subsequent_minfontsize "0.75"
 seta hud_panel_centerprint_fade_minfontsize "0"
 
+seta hud_panel_buffs 1
+seta hud_panel_buffs_pos "0.450000 0.855000"
+seta hud_panel_buffs_size "0.050000 0.070000"
+seta hud_panel_buffs_bg "0"
+seta hud_panel_buffs_bg_color ""
+seta hud_panel_buffs_bg_color_team ""
+seta hud_panel_buffs_bg_alpha ""
+seta hud_panel_buffs_bg_border ""
+seta hud_panel_buffs_bg_padding ""
+
 menu_sync
index 6a94d2b3ee0dc01cbe185fde9bda4adbe6877585..9d71e2e2872cc4f4d6e0624a2d2b5e74064fa5cf 100644 (file)
@@ -296,4 +296,14 @@ seta hud_panel_centerprint_fade_subsequent_passtwo_minalpha "0.5"
 seta hud_panel_centerprint_fade_subsequent_minfontsize "0.75"
 seta hud_panel_centerprint_fade_minfontsize "0"
 
+seta hud_panel_buffs 1
+seta hud_panel_buffs_pos "0.450000 0.855000"
+seta hud_panel_buffs_size "0.050000 0.070000"
+seta hud_panel_buffs_bg "0"
+seta hud_panel_buffs_bg_color ""
+seta hud_panel_buffs_bg_color_team ""
+seta hud_panel_buffs_bg_alpha ""
+seta hud_panel_buffs_bg_border ""
+seta hud_panel_buffs_bg_padding ""
+
 menu_sync
index 5d8ee1fb967f25fab964ecd29fb099a84894f86e..9e4678293d627419612564b5d12d91a4365d56fc 100644 (file)
@@ -296,4 +296,14 @@ seta hud_panel_centerprint_fade_subsequent_passtwo_minalpha "0.5"
 seta hud_panel_centerprint_fade_subsequent_minfontsize "0.75"
 seta hud_panel_centerprint_fade_minfontsize "0"
 
+seta hud_panel_buffs 1
+seta hud_panel_buffs_pos "0.450000 0.855000"
+seta hud_panel_buffs_size "0.050000 0.070000"
+seta hud_panel_buffs_bg "0"
+seta hud_panel_buffs_bg_color ""
+seta hud_panel_buffs_bg_color_team ""
+seta hud_panel_buffs_bg_alpha ""
+seta hud_panel_buffs_bg_border ""
+seta hud_panel_buffs_bg_padding ""
+
 menu_sync
diff --git a/models/.DS_Store b/models/.DS_Store
new file mode 100644 (file)
index 0000000..62f119c
Binary files /dev/null and b/models/.DS_Store differ
diff --git a/models/player/.DS_Store b/models/player/.DS_Store
new file mode 100644 (file)
index 0000000..cb95dc8
Binary files /dev/null and b/models/player/.DS_Store differ
diff --git a/models/weapons/.DS_Store b/models/weapons/.DS_Store
new file mode 100644 (file)
index 0000000..966cb13
Binary files /dev/null and b/models/weapons/.DS_Store differ
index c762ef9dec047aaa26cebd9320da67e40aad6aef..8ee7751e3f7e6e1fda870bb73e5e2296ba99cab5 100644 (file)
@@ -162,3 +162,50 @@ set g_campcheck_interval 10
 set g_campcheck_damage 100
 set g_campcheck_distance 1800
 
+
+// =======
+//  buffs
+// =======
+set cl_buffs_autoreplace 1 "automatically drop current buff when picking up another"
+set g_buffs 0 "enable buffs (requires buff items or powerups)"
+set g_buffs_waypoint_distance 1024 "maximum distance at which buff waypoint can be seen from item"
+set g_buffs_randomize 1 "randomize buff type when player drops buff"
+set g_buffs_random_lifetime 30 "re-spawn the buff again if it hasn't been touched after this time in seconds"
+set g_buffs_random_location 0 "randomize buff location on start and when reset"
+set g_buffs_random_location_attempts 10 "number of random locations a single buff will attempt to respawn at before giving up"
+set g_buffs_spawn_count 5 "how many buffs to spawn on the map if none exist already"
+set g_buffs_replace_powerups 1 "replace powerups on the map with random buffs"
+set g_buffs_cooldown_activate 5 "cooldown period when buff is first activated"
+set g_buffs_cooldown_respawn 3 "cooldown period when buff is reloading"
+set g_buffs_ammo 1 "ammo buff: infinite ammunition"
+set g_buffs_resistance 1 "resistance buff: greatly reduces damage taken"
+set g_buffs_resistance_blockpercent 0.7 "damage reduction multiplier, higher values mean less damage"
+set g_buffs_medic 1 "medic buff: increased regeneration speed, extra health, chance to survive a fatal attack"
+set g_buffs_medic_survive_chance 0.6 "multiplier chance of player surviving a fatal hit"
+set g_buffs_medic_survive_health 5 "amount of health player survives with after taking a fatal hit"
+set g_buffs_medic_rot 0.7 "health rot rate multiplier"
+set g_buffs_medic_max 1.5 "stable health medic limit multiplier"
+set g_buffs_medic_regen 1.7 "health medic rate multiplier"
+set g_buffs_vengeance 1 "vengeance buff: attackers also take damage"
+set g_buffs_vengeance_damage_multiplier 0.6 "amount of damage dealt the attacker takes when hitting a target with vengeance"
+set g_buffs_bash 1 "bash buff: increased knockback force and immunity to knockback"
+set g_buffs_bash_force 2 "bash force multiplier"
+set g_buffs_bash_force_self 1.2 "bash self force multiplier"
+set g_buffs_disability 1 "disability buff: attacks to players and monsters deal slowness (decreased movement/attack speed) for a few seconds"
+set g_buffs_disability_time 3 "time in seconds for target disability"
+set g_buffs_disability_speed 0.5 "player speed multiplier while disabled"
+set g_buffs_disability_rate 1.7 "player weapon rate multiplier while disabled"
+set g_buffs_speed 1 "speed buff: increased movement/attack/health regeneration speed, carrier takes slightly more damage"
+set g_buffs_speed_speed 1.7 "player speed multiplier while holding speed buff"
+set g_buffs_speed_rate 0.8 "player weapon rate multiplier while holding speed buff"
+set g_buffs_speed_damage_take 1.2 "damage taken multiplier while holding speed buff"
+set g_buffs_speed_regen 1.2 "regeneration speed multiplier while holding speed buff"
+set g_buffs_vampire 1 "vampire buff: attacks to players and monsters heal the carrier"
+set g_buffs_vampire_damage_steal 0.6 "damage stolen multiplier while holding vampire buff"
+set g_buffs_jump 1 "jump buff: greatly increased jump height"
+set g_buffs_jump_height 600 "jump height while holding jump buff"
+set g_buffs_flight 1 "flight buff: greatly decreased gravity"
+set g_buffs_flight_gravity 0.3 "player gravity multiplier while holding flight buff"
+set g_buffs_invisible 1 "invisible buff: carrier becomes invisible"
+set g_buffs_invisible_alpha 0.4 "player invisibility multiplier while holding invisible buff"
+
diff --git a/qcsrc/.DS_Store b/qcsrc/.DS_Store
new file mode 100644 (file)
index 0000000..8a725c5
Binary files /dev/null and b/qcsrc/.DS_Store differ
diff --git a/qcsrc/client/.DS_Store b/qcsrc/client/.DS_Store
new file mode 100644 (file)
index 0000000..a904049
Binary files /dev/null and b/qcsrc/client/.DS_Store differ
index 4ea750ce5dbc5c465713037d84ada9ad97b579c0..c948c9a70e1e7f9c34d7f1ba8d6009fd221691a9 100644 (file)
@@ -109,6 +109,7 @@ void CSQC_Init(void)
        CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
        CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes);
        CALL_ACCUMULATED_FUNCTION(RegisterHUD_Panels);
+       CALL_ACCUMULATED_FUNCTION(RegisterBuffs);
 
        WaypointSprite_Load();
 
index 678b5f49fea3dc21cd1a7b9205c642702861341a..8516d32f6debeaf4faaa7fa539f5483a6e17eb86 100644 (file)
@@ -291,6 +291,8 @@ float autocvar_hud_panel_powerups_baralign;
 float autocvar_hud_panel_powerups_flip;
 float autocvar_hud_panel_powerups_iconalign;
 float autocvar_hud_panel_powerups_progressbar;
+float autocvar_hud_panel_buffs;
+//float autocvar_hud_panel_buffs_iconalign;
 string autocvar_hud_panel_powerups_progressbar_shield;
 string autocvar_hud_panel_powerups_progressbar_strength;
 string autocvar_hud_panel_powerups_progressbar_superweapons;
index 9faf594451175f0bc2e97deb416ec6800d52488a..e1c95025892e272c556f33edbc86275df630a21a 100644 (file)
@@ -4372,6 +4372,66 @@ void HUD_CenterPrint (void)
        }
 }
 
+// Buffs (#18)
+//
+void HUD_Buffs(void)
+{
+       float buffs = getstati(STAT_BUFFS, 0, 24);
+       if(!autocvar__hud_configure)
+       {
+               if(!autocvar_hud_panel_buffs) return;
+               if(spectatee_status == -1) return;
+               if(getstati(STAT_HEALTH) <= 0) return;
+               if(!buffs) return;
+       }
+       else
+       {
+               buffs = Buff_Type_first.items; // force first buff
+       }
+       
+       float b = 0; // counter to tell other functions that we have buffs
+       entity e;
+       string s = "";
+       for(e = Buff_Type_first; e; e = e.enemy) if(buffs & e.items)
+       {
+               ++b;
+               string o = strcat(rgb_to_hexcolor(Buff_Color(e.items)), Buff_PrettyName(e.items));
+               if(s == "")
+                       s = o;
+               else
+                       s = strcat(s, " ", o);
+       }
+
+       HUD_Panel_UpdateCvars();
+
+       draw_beginBoldFont();
+
+       vector pos, mySize;
+       pos = panel_pos;
+       mySize = panel_size;
+
+       HUD_Panel_DrawBg(bound(0, b, 1));
+       if(panel_bg_padding)
+       {
+               pos += '1 1 0' * panel_bg_padding;
+               mySize -= '2 2 0' * panel_bg_padding;
+       }
+
+       //float panel_ar = mySize_x/mySize_y;
+       //float is_vertical = (panel_ar < 1);
+       //float buff_iconalign = autocvar_hud_panel_buffs_iconalign;
+       vector buff_offset = '0 0 0';
+       
+       for(e = Buff_Type_first; e; e = e.enemy) if(buffs & e.items)
+       {
+               //DrawNumIcon(pos + buff_offset, mySize, shield, "shield", is_vertical, buff_iconalign, '1 1 1', 1);
+               drawcolorcodedstring_aspect(pos + buff_offset, s, mySize, panel_fg_alpha * 0.5, DRAWFLAG_NORMAL);
+       }
+
+       draw_endBoldFont();
+}
+
+
 /*
 ==================
 Main HUD system
index dcaa81a87153d4a71efdd66514a3ef4fa42b3b0f..55d4bd0db8c9fdd71265993fea3dd372549c83c9 100644 (file)
@@ -114,7 +114,8 @@ float current_player;
        HUD_PANEL(ENGINEINFO   , HUD_EngineInfo   , engineinfo) \
        HUD_PANEL(INFOMESSAGES , HUD_InfoMessages , infomessages) \
        HUD_PANEL(PHYSICS      , HUD_Physics      , physics) \
-       HUD_PANEL(CENTERPRINT  , HUD_CenterPrint  , centerprint)
+       HUD_PANEL(CENTERPRINT  , HUD_CenterPrint  , centerprint) \
+       HUD_PANEL(BUFFS        , HUD_Buffs        , buffs) 
 
 #define HUD_PANEL(NAME,draw_func,name) \
        float HUD_PANEL_##NAME; \
index 8caeb01d524064f51c3191440555c0cd814ce0e3..a354bacf28d9ecbec0b259bb5055f2989c889a98 100644 (file)
@@ -6,17 +6,26 @@ string mv_pics[MAPVOTE_COUNT];
 string mv_pk3[MAPVOTE_COUNT];
 float mv_preview[MAPVOTE_COUNT];
 float mv_votes[MAPVOTE_COUNT];
+float mv_avail[MAPVOTE_COUNT];
+float mv_avail_start[MAPVOTE_COUNT];
 entity mv_pk3list;
 float mv_abstain;
 float mv_ownvote;
 float mv_detail;
 float mv_timeout;
-float mv_maps_mask;
 float mv_top2_time;
 float mv_top2_alpha;
 
 vector mv_mousepos;
 float mv_selection;
+float mv_columns;
+float mv_mouse_selection;
+float mv_selection_keyboard;
+
+float gametypevote;
+string mapvote_chosenmap;
+vector gtv_text_size;
+vector gtv_text_size_small;
 
 string MapVote_FormatMapItem(float id, string map, float count, float maxwidth, vector fontsize)
 {
@@ -26,7 +35,7 @@ string MapVote_FormatMapItem(float id, string map, float count, float maxwidth,
        {
                if(count == 1)
                        post = _(" (1 vote)");
-               else if(count >= 0)
+               else if(count >= 0 && mv_avail[id] == GTV_AVAILABLE)
                        post = sprintf(_(" (%d votes)"), count);
                else
                        post = "";
@@ -38,9 +47,14 @@ string MapVote_FormatMapItem(float id, string map, float count, float maxwidth,
        return strcat(pre, map, post);
 }
 
-vector MapVote_RGB(float id, float count)
+string GameTypeVote_DescriptionByID(float id)
+{
+       return MapInfo_Type_Description(MapInfo_Type_FromString(mv_maps[id]));
+}
+
+vector MapVote_RGB(float id)
 {
-       if(count < 0)
+       if(mv_avail[id] != GTV_AVAILABLE)
                return '1 1 1';
        if(id == mv_ownvote)
                return '0 1 0';
@@ -50,6 +64,100 @@ vector MapVote_RGB(float id, float count)
                return '1 1 1';
 }
 
+void GameTypeVote_DrawGameTypeItem(vector pos, float maxh, float tsize, string gtype, string pic, float count, float id)
+{
+       float alpha;
+       float desc_padding = gtv_text_size_x * 3;
+       float rect_margin = hud_fontsize_y / 2;
+       vector rect_pos = pos - '0.5 0.5 0' * rect_margin;
+       vector rect_size = '1 1 0';
+       rect_size_x = tsize + rect_margin;
+       rect_size_y = maxh + rect_margin;
+       vector rgb = MapVote_RGB(id);
+       vector offset = pos;
+       float nlines = 0;
+       
+       if(mv_avail_start[id] != GTV_AVAILABLE)
+               alpha = 0.2;
+       else if ( mv_avail[id] != GTV_AVAILABLE && mv_top2_alpha)
+               alpha = mv_top2_alpha;
+       else
+               alpha = 1;
+       
+       if(id == mv_selection && mv_avail[id] == GTV_AVAILABLE)
+       {
+               drawfill(rect_pos, rect_size, '1 1 1', 0.1, DRAWFLAG_NORMAL);
+       }
+       if(id == mv_ownvote)
+       {
+               drawfill(rect_pos, rect_size, rgb, 0.1*alpha, DRAWFLAG_NORMAL);
+               drawborderlines(autocvar_scoreboard_border_thickness, rect_pos, rect_size, rgb, alpha, DRAWFLAG_NORMAL);
+       }
+       
+       entity title;
+       title = spawn();
+       title.message = MapVote_FormatMapItem(id, MapInfo_Type_ToText(MapInfo_Type_FromString(gtype)), 
+                                                                                 count, tsize, gtv_text_size);
+       title.origin = pos-offset;
+       
+       pos_y += gtv_text_size_small_y;
+       pos_y += gtv_text_size_y/2;
+       
+       maxh -= gtv_text_size_y;
+       
+       entity picent = spawn();
+       picent.origin = pos-offset;
+       picent.maxs = '1 1 0 ' * min(maxh, desc_padding) * 0.8;
+       
+       pos_x += desc_padding;
+       tsize -= desc_padding;
+       
+       string thelabel = GameTypeVote_DescriptionByID(id), ts;
+       entity last = title;
+       entity next = world;
+       if( thelabel != "") 
+       {
+               float i,n = tokenizebyseparator(thelabel, "\n");
+               for(i = 0; i < n && maxh > (nlines+1)*gtv_text_size_small_y; ++i)
+               {
+                       getWrappedLine_remaining = argv(i);
+                       while(getWrappedLine_remaining && maxh > (nlines+1)*gtv_text_size_small_y)
+                       {
+                               ts = getWrappedLine(tsize, gtv_text_size_small, stringwidth_colors);
+                               if (ts != "")
+                               {
+                                       next = spawn();
+                                       next.message = ts;
+                                       next.origin = pos-offset;
+                                       last.chain = next;
+                                       last = next;
+                                       pos_y += gtv_text_size_small_y;
+                                       nlines++;
+                               }
+                       }
+               }
+       }
+       
+       maxh -= max(nlines*gtv_text_size_small_y,picent.maxs_y);
+       if ( maxh > 0 )
+               offset_y += maxh/2;
+       drawstring(title.origin+offset, title.message, gtv_text_size, rgb, alpha, DRAWFLAG_NORMAL); 
+       
+       if(pic != "")
+               drawpic(picent.origin+offset, pic, picent.maxs, '1 1 1', alpha, DRAWFLAG_NORMAL);
+       
+       for ( last = title.chain; last ; )
+       {
+               drawstring(last.origin+offset, last.message, gtv_text_size_small, '1 1 1', alpha, DRAWFLAG_NORMAL);
+               next = last;
+               last = last.chain;
+               remove(next);
+       }
+       
+       remove(picent);
+       remove(title);
+}
+
 void MapVote_DrawMapItem(vector pos, float isize, float tsize, string map, string pic, float count, float id)
 {
        vector img_size = '0 0 0';
@@ -59,7 +167,7 @@ void MapVote_DrawMapItem(vector pos, float isize, float tsize, string map, strin
 
        isize -= hud_fontsize_y; // respect the text when calculating the image size
 
-       rgb = MapVote_RGB(id, count);
+       rgb = MapVote_RGB(id);
 
        img_size_y = isize;
        img_size_x = isize / 0.75; // 4:3 x can be stretched easily, height is defined in isize
@@ -71,7 +179,7 @@ void MapVote_DrawMapItem(vector pos, float isize, float tsize, string map, strin
        text_size = stringwidth(label, false, hud_fontsize);
 
        float theAlpha;
-       if (count < 0 && mv_top2_alpha)
+       if (mv_avail[id] != GTV_AVAILABLE && mv_top2_alpha)
                theAlpha = mv_top2_alpha;
        else
                theAlpha = 1;
@@ -101,7 +209,7 @@ void MapVote_DrawMapItem(vector pos, float isize, float tsize, string map, strin
        else
                drawborderlines(autocvar_scoreboard_border_thickness, pos, img_size, '0 0 0', theAlpha, DRAWFLAG_NORMAL);
 
-       if(id == mv_selection && count >= 0)
+       if(id == mv_selection && mv_avail[id] == GTV_AVAILABLE)
                drawfill(pos, img_size, '1 1 1', 0.1, DRAWFLAG_NORMAL);
 }
 
@@ -111,7 +219,7 @@ void MapVote_DrawAbstain(vector pos, float isize, float tsize, float count, floa
        float text_size;
        string label;
 
-       rgb = MapVote_RGB(id, count);
+       rgb = MapVote_RGB(id);
 
        pos_y = pos_y + hud_fontsize_y;
 
@@ -135,10 +243,10 @@ vector MapVote_GridVec(vector gridspec, float i, float m)
 
 float MapVote_Selection(vector topleft, vector cellsize, float rows, float columns)
 {
-       float cell;
+
        float c, r;
 
-       cell = -1;
+       mv_mouse_selection = -1;
 
        for (r = 0; r < rows; ++r)
                for (c = 0; c < columns; ++c)
@@ -148,18 +256,21 @@ float MapVote_Selection(vector topleft, vector cellsize, float rows, float colum
                                mv_mousepos_y >= topleft_y + cellsize_y *  r &&
                                mv_mousepos_y <= topleft_y + cellsize_y * (r + 1))
                        {
-                               cell = r * columns + c;
+                               mv_mouse_selection = r * columns + c;
                                break;
                        }
                }
 
-       if (cell >= mv_num_maps)
-               cell = -1;
+       if (mv_mouse_selection >= mv_num_maps)
+               mv_mouse_selection = -1;
 
-       if (mv_abstain && cell < 0)
-               return mv_num_maps;
+       if (mv_abstain && mv_mouse_selection < 0)
+               mv_mouse_selection = mv_num_maps;
 
-       return cell;
+       if ( mv_selection_keyboard )
+               return mv_selection;
+       
+       return mv_mouse_selection;
 }
 
 void MapVote_Draw()
@@ -169,7 +280,7 @@ void MapVote_Draw()
        vector pos;
        float isize;
        float center;
-       float columns, rows;
+       float rows;
        float tsize;
        vector dist = '0 0 0';
 
@@ -178,10 +289,14 @@ void MapVote_Draw()
 
        if (!autocvar_hud_cursormode)
        {
-               mv_mousepos = mv_mousepos + getmousepos();
+               vector mpos = mv_mousepos + getmousepos();
+               mpos_x = bound(0, mpos_x, vid_conwidth);
+               mpos_y = bound(0, mpos_y, vid_conheight);
+               
+               if ( mpos_x != mv_mousepos_x || mpos_y != mv_mousepos_y )
+                       mv_selection_keyboard = 0;
+               mv_mousepos = mpos;
 
-               mv_mousepos_x = bound(0, mv_mousepos_x, vid_conwidth);
-               mv_mousepos_y = bound(0, mv_mousepos_y, vid_conheight);
        }
 
        center = (vid_conwidth - 1)/2;
@@ -200,11 +315,18 @@ void MapVote_Draw()
        pos_z = 0;
 
        draw_beginBoldFont();
-       map = _("Vote for a map");
+       map = ((gametypevote) ? _("Decide the gametype") : _("Vote for a map"));
        pos_x = center - stringwidth(map, false, '12 0 0');
        drawstring(pos, map, '24 24 0', '1 1 1', 1, DRAWFLAG_NORMAL);
        pos_y += 26;
 
+       if( mapvote_chosenmap != "" )
+       {
+               pos_x = center - stringwidth(mapvote_chosenmap, false, hud_fontsize*1.5/2);
+               drawstring(pos, mapvote_chosenmap, hud_fontsize*1.5, '1 1 1', 1, DRAWFLAG_NORMAL);
+               pos_y += hud_fontsize_y*2;
+       }
+
        i = ceil(max(0, mv_timeout - time));
        map = sprintf(_("%d seconds left"), i);
        pos_x = center - stringwidth(map, false, '8 0 0');
@@ -218,36 +340,56 @@ void MapVote_Draw()
        if(mv_abstain)
                mv_num_maps -= 1;
 
-       if(mv_num_maps > 3)
-       {
-               columns = 3;
-       } else {
-               columns = mv_num_maps;
-       }
-       rows = ceil(mv_num_maps / columns);
+       rows = ceil(mv_num_maps / mv_columns);
 
-       dist_x = (xmax - xmin) / columns;
+       dist_x = (xmax - xmin) / mv_columns;
        dist_y = (ymax - pos_y) / rows;
-       tsize = dist_x - 10;
-       isize = min(dist_y - 10, 0.75 * tsize);
 
-       mv_selection = MapVote_Selection(pos, dist, rows, columns);
+       if ( gametypevote )
+       {
+               tsize = dist_x - hud_fontsize_y;
+               isize = dist_y;
+               float maxheight = (ymax - pos_y) / 3;
+               if ( isize > maxheight )
+               {
+                       pos_x += (isize - maxheight)/2;
+                       isize = maxheight;
+               }
+               else
+                       dist_y += hud_fontsize_y;
+               pos_x = ( vid_conwidth - dist_x * mv_columns ) / 2;
+       }
+       else
+       {
+               tsize = dist_x - 10;
+               isize = min(dist_y - 10, 0.75 * tsize);
+       }
+
+       mv_selection = MapVote_Selection(pos, dist, rows, mv_columns);
 
-       pos_x += (xmax - xmin) / (2 * columns);
+       if ( !gametypevote )
+               pos_x += dist_x / 2;
        pos_y += (dist_y - isize) / 2;
        ymax -= isize;
 
        if (mv_top2_time)
                mv_top2_alpha = max(0.2, 1 - (time - mv_top2_time)*(time - mv_top2_time));
 
+       void (vector, float, float, string, string, float, float) DrawItem;
+
+       if(gametypevote)
+               DrawItem = GameTypeVote_DrawGameTypeItem;
+       else
+               DrawItem = MapVote_DrawMapItem;
+
        for(i = 0; i < mv_num_maps; ++i)
        {
                tmp = mv_votes[i]; // FTEQCC bug: too many array accesses in the function call screw it up
                map = mv_maps[i];
                if(mv_preview[i])
-                       MapVote_DrawMapItem(pos + MapVote_GridVec(dist, i, columns), isize, tsize, map, mv_pics[i], tmp, i);
+                       DrawItem(pos + MapVote_GridVec(dist, i, mv_columns), isize, tsize, map, mv_pics[i], tmp, i);
                else
-                       MapVote_DrawMapItem(pos + MapVote_GridVec(dist, i, columns), isize, tsize, map, "", tmp, i);
+                       DrawItem(pos + MapVote_GridVec(dist, i, mv_columns), isize, tsize, map, "", tmp, i);
        }
 
        if(mv_abstain)
@@ -329,12 +471,35 @@ void MapVote_CheckPic(string pic, string pk3, float id)
        MapVote_CheckPK3(pic, pk3, id);
 }
 
+void MapVote_ReadMask()
+{
+       float i;
+       if ( mv_num_maps < 24 )
+       {
+               float mask, power;
+               if(mv_num_maps < 8)
+                       mask = ReadByte();
+               else if(mv_num_maps < 16)
+                       mask = ReadShort();
+               else
+                       mask = ReadLong();
+               
+               for(i = 0, power = 1; i < mv_num_maps; ++i, power *= 2)
+                       mv_avail[i] = (mask & power) ? GTV_AVAILABLE : GTV_FORBIDDEN;
+       }
+       else
+       {
+               for(i = 0; i < mv_num_maps; ++i )
+                       mv_avail[i] = ReadByte();
+       }
+}
+
 #define NUM_SSDIRS 4
 string ssdirs[NUM_SSDIRS];
 float n_ssdirs;
 void MapVote_Init()
 {
-       float i, j, power;
+       float i, j;
        string map, pk3, s;
 
        precache_sound ("misc/invshot.wav");
@@ -343,6 +508,7 @@ void MapVote_Init()
        if(autocvar_hud_cursormode) { setcursormode(1); }
        else { mv_mousepos = '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight; }
        mv_selection = -1;
+       mv_selection_keyboard = 0;
 
        for(n_ssdirs = 0; ; ++n_ssdirs)
        {
@@ -363,39 +529,72 @@ void MapVote_Init()
        mv_ownvote = -1;
        mv_timeout = ReadCoord();
 
-       if(mv_num_maps <= 8)
-               mv_maps_mask = ReadByte();
-       else
-               mv_maps_mask = ReadShort();
+       gametypevote = ReadByte();
+       
+       float mv_real_num_maps = mv_num_maps - mv_abstain;
+
+       if(gametypevote)
+       {
+               mapvote_chosenmap = strzone(ReadString());
+               if ( gametypevote == 2 )
+                       gametypevote = 0;
+
+               gtv_text_size = hud_fontsize*1.4;
+               gtv_text_size_small = hud_fontsize*1.1;
+               
+               if (mv_real_num_maps > 8 )
+                       mv_columns = 3;
+               else
+                       mv_columns = min(2, mv_real_num_maps);
+    }
+    else
+       {
+               if (mv_real_num_maps > 16)
+                       mv_columns = 5;
+               else if (mv_real_num_maps > 9)
+                       mv_columns = 4;
+               else if(mv_real_num_maps > 3)
+                       mv_columns = 3;
+               else
+                       mv_columns = mv_real_num_maps;
+       }
+
+       MapVote_ReadMask();
+       for(i = 0; i < mv_num_maps; ++i )
+               mv_avail_start[i] = mv_avail[i];
 
        // Assume mv_pk3list is world, there should only be 1 mapvote per round
        mv_pk3list = world; // I'm still paranoid!
 
-       for(i = 0, power = 1; i < mv_num_maps; ++i, power *= 2)
+       for(i = 0; i < mv_num_maps; ++i)
        {
                mv_votes[i] = 0;
 
-               if(mv_maps_mask & power)
-               {
-                       map = strzone(ReadString());
-                       pk3 = strzone(ReadString());
-                       j = bound(0, ReadByte(), n_ssdirs - 1);
-
-                       mv_maps[i] = map;
-                       mv_pk3[i] = pk3;
-                       map = strzone(strcat(ssdirs[j], "/", map));
-                       mv_pics[i] = map;
+               map = strzone(ReadString());
+               pk3 = strzone(ReadString());
+               j = bound(0, ReadByte(), n_ssdirs - 1);
 
-                       mv_preview[i] = false;
+               mv_maps[i] = map;
+               mv_pk3[i] = pk3;
+               mv_avail[i] = ReadByte();
 
-                       MapVote_CheckPic(map, pk3, i);
+               if(gametypevote)
+               {
+                       //map = strzone(strcat("gfx/menu/default/gametype_", map));
+                       //map = strzone(sprintf("gfx/menu/%s/gametype_%s", autocvar_menu_skin, map));
+                       string mv_picpath = sprintf("gfx/menu/%s/gametype_%s", autocvar_menu_skin, map);
+                       if(precache_pic(mv_picpath) == "")
+                               mv_picpath = strcat("gfx/menu/default/gametype_", map);
+                       map = strzone(mv_picpath);
+                       mv_pics[i] = map;
+                       mv_preview[i] = PreviewExists(map);
                }
                else
                {
-                       mv_maps[i] = strzone("if-you-see-this-the-code-is-broken");
-                       mv_pk3[i] = strzone("if-you-see-this-the-code-is-broken");
-                       mv_pics[i] = strzone("if-you-see-this-the-code-is-broken");
+                       map = strzone(strcat(ssdirs[j], "/", map));
+                       mv_pics[i] = map;
                        mv_preview[i] = false;
+                       MapVote_CheckPic(map, pk3, i);
                }
        }
 
@@ -404,6 +603,68 @@ void MapVote_Init()
        n_ssdirs = 0;
 }
 
+void MapVote_SendChoice(float index)
+{
+       localcmd(strcat("\nimpulse ", ftos(index+1), "\n"));
+}
+
+float MapVote_MoveLeft(float pos)
+{
+       float imp;
+       if ( pos < 0 ) 
+               imp = mv_num_maps - 1;
+       else
+               imp = pos < 1 ? mv_num_maps - 1 : pos - 1;
+       if ( mv_avail[imp] != GTV_AVAILABLE && imp != mv_ownvote )
+               imp = MapVote_MoveLeft(imp);
+       return imp;
+}
+float MapVote_MoveRight(float pos)
+{
+       float imp;
+       if ( pos < 0 ) 
+               imp = 0;
+       else
+               imp = pos >= mv_num_maps - 1 ? 0 : pos + 1;
+       if ( mv_avail[imp] != GTV_AVAILABLE && imp != mv_ownvote )
+               imp = MapVote_MoveRight(imp);
+       return imp;
+}
+float MapVote_MoveUp(float pos)
+{
+       float imp;
+       if ( pos < 0 ) 
+               imp = mv_num_maps - 1;
+       else
+       {
+               imp = pos - mv_columns;
+               if ( imp < 0 )
+               {
+                       imp = floor(mv_num_maps/mv_columns)*mv_columns + pos % mv_columns;
+                       if ( imp >= mv_num_maps )
+                               imp -= mv_columns;
+               }
+       }
+       if ( mv_avail[imp] != GTV_AVAILABLE && imp != mv_ownvote )
+               imp = MapVote_MoveUp(imp);
+       return imp;
+}
+float MapVote_MoveDown(float pos)
+{
+       float imp;
+       if ( pos < 0 ) 
+               imp = 0;
+       else
+       {
+               imp = pos + mv_columns;
+               if ( imp >= mv_num_maps )
+                       imp = imp % mv_columns;
+       }
+       if ( mv_avail[imp] != GTV_AVAILABLE && imp != mv_ownvote )
+               imp = MapVote_MoveDown(imp);
+       return imp;
+}
+
 float MapVote_InputEvent(float bInputType, float nPrimary, float nSecondary)
 {
        float imp;
@@ -415,6 +676,7 @@ float MapVote_InputEvent(float bInputType, float nPrimary, float nSecondary)
        {
                mv_mousepos_x = nPrimary;
                mv_mousepos_y = nSecondary;
+               mv_selection_keyboard = 0;
                return true;
        }
 
@@ -440,48 +702,58 @@ float MapVote_InputEvent(float bInputType, float nPrimary, float nSecondary)
                case K_KP_8: localcmd("\nimpulse 8\n"); return true;
                case K_KP_9: localcmd("\nimpulse 9\n"); return true;
                case K_KP_0: localcmd("\nimpulse 10\n"); return true;
+
+               case K_RIGHTARROW:
+                       mv_selection_keyboard = 1;
+                       mv_selection = MapVote_MoveRight(mv_selection);
+                       return true;
+               case K_LEFTARROW:
+                       mv_selection_keyboard = 1;
+                       mv_selection = MapVote_MoveLeft(mv_selection);
+                       return true;
+               case K_DOWNARROW:
+                       mv_selection_keyboard = 1;
+                       mv_selection = MapVote_MoveDown(mv_selection);
+                       return true;
+               case K_UPARROW:
+                       mv_selection_keyboard = 1;
+                       mv_selection = MapVote_MoveUp(mv_selection);
+                       return true;
+               case K_KP_ENTER:
+               case K_ENTER:
+               case K_SPACE:
+                       if ( mv_selection_keyboard )
+                               MapVote_SendChoice(mv_selection);
+                       return true;
        }
 
        if (nPrimary == K_MOUSE1)
+       {
+               mv_selection_keyboard = 0;
+               mv_selection = mv_mouse_selection;
                if (mv_selection >= 0)
                {
                        imp = min(mv_selection + 1, mv_num_maps);
                        localcmd(strcat("\nimpulse ", ftos(imp), "\n"));
                        return true;
                }
+       }
 
        return false;
 }
 
 void MapVote_UpdateMask()
 {
-       float i, power;
-       float oldmask;
-
-       oldmask = mv_maps_mask;
-       if(mv_num_maps <= 8)
-               mv_maps_mask = ReadByte();
-       else
-               mv_maps_mask = ReadShort();
-
-       if((oldmask & mv_maps_mask) != oldmask)
-               if((oldmask & mv_maps_mask) == mv_maps_mask)
-                        sound(world, CH_INFO, "misc_invshot.wav", VOL_BASE, ATTEN_NONE);
-
-       // remove votes that no longer apply
-       for(i = 0, power = 1; i < mv_num_maps; ++i, power *= 2)
-               if (!(mv_maps_mask & power))
-                       mv_votes[i] = -1;
-
+       MapVote_ReadMask();
        mv_top2_time = time;
 }
 
 void MapVote_UpdateVotes()
 {
-       float i, power;
-       for(i = 0, power = 1; i < mv_num_maps; ++i, power *= 2)
+       float i;
+       for(i = 0; i < mv_num_maps; ++i)
        {
-               if(mv_maps_mask & power)
+               if(mv_avail[i] == GTV_AVAILABLE)
                {
                        if(mv_detail)
                                mv_votes[i] = ReadByte();
index a4122d261f175961a4b52ae55a1c21418b3141f6..b536797ce4b1d05f5159b64eb4e2f623aa0f03cb 100644 (file)
@@ -1,4 +1,3 @@
-const float STAT_MOVEFLAGS = 225;
 const float MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE = 4;
 #define GRAVITY_UNAFFECTED_BY_TICRATE (getstati(STAT_MOVEFLAGS) & MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE)
 
index 1a518c5a697f9079977a896ae21cf841cdedeb17..b7281c562124b2c8dc0c20be64254d23095fa8da 100644 (file)
@@ -8,6 +8,7 @@ sys-post.qh
 Defs.qc
 ../dpdefs/keycodes.qc
 ../common/constants.qh
+../common/stats.qh
 
 ../warpzonelib/anglestransform.qh
 ../warpzonelib/mathlib.qh
@@ -16,6 +17,7 @@ Defs.qc
 
 ../common/teams.qh
 ../common/util.qh
+../common/buffs.qh
 ../common/test.qh
 ../common/counting.qh
 ../common/items.qh
@@ -117,6 +119,8 @@ command/cl_cmd.qc
 
 ../common/monsters/monsters.qc
 
+../common/buffs.qc
+
 ../warpzonelib/anglestransform.qc
 ../warpzonelib/mathlib.qc
 ../warpzonelib/common.qc
index 1367501a8a3743ab17b2c2fa5dbb02c9a7870911..38d1579282eed5650d36d556b21a4d4041cb6206 100644 (file)
@@ -241,6 +241,7 @@ vector spritelookupcolor(string s, vector def)
 }
 string spritelookuptext(string s)
 {
+       if(substring(s, 0, 5) == "buff-") { return Buff_PrettyName(Buff_Type_FromSprite(s)); }
        switch(s)
        {
                case "as-push": return _("Push");
diff --git a/qcsrc/common/.DS_Store b/qcsrc/common/.DS_Store
new file mode 100644 (file)
index 0000000..ddd2a7a
Binary files /dev/null and b/qcsrc/common/.DS_Store differ
diff --git a/qcsrc/common/buffs.qc b/qcsrc/common/buffs.qc
new file mode 100644 (file)
index 0000000..2f8e0fc
--- /dev/null
@@ -0,0 +1,63 @@
+vector Buff_Color(float buff_id)
+{
+       entity e;
+       for(e = Buff_Type_first; e; e = e.enemy)
+               if(buff_id == e.items)
+                       return e.colormod;
+       return '1 1 1';
+}
+
+string Buff_PrettyName(float buff_id)
+{
+       entity e;
+       for(e = Buff_Type_first; e; e = e.enemy)
+               if(buff_id == e.items)
+                       return e.message;
+       return "";
+}
+
+string Buff_Name(float buff_id)
+{
+       entity e;
+       for(e = Buff_Type_first; e; e = e.enemy)
+               if(buff_id == e.items)
+                       return e.netname;
+       return "";
+}
+
+float Buff_Type_FromName(string buff_name)
+{
+       entity e;
+       for(e = Buff_Type_first; e; e = e.enemy)
+               if(buff_name == e.netname)
+                       return e.items;
+       return 0;
+}
+
+float Buff_Type_FromSprite(string buff_sprite)
+{
+       entity e;
+       for(e = Buff_Type_first; e; e = e.enemy)
+               if(buff_sprite == e.model2)
+                       return e.items;
+       return 0;
+}
+
+
+float Buff_Skin(float buff_id)
+{
+       entity e;
+       for(e = Buff_Type_first; e; e = e.enemy)
+               if(buff_id == e.items)
+                       return e.skin;
+       return 0;
+}
+
+string Buff_Sprite(float buff_id)
+{
+       entity e;
+       for(e = Buff_Type_first; e; e = e.enemy)
+               if(buff_id == e.items)
+                       return e.model2;
+       return "";
+}
diff --git a/qcsrc/common/buffs.qh b/qcsrc/common/buffs.qh
new file mode 100644 (file)
index 0000000..c29dad6
--- /dev/null
@@ -0,0 +1,93 @@
+entity Buff_Type_first;
+entity Buff_Type_last;
+.entity enemy; // internal next pointer
+
+var float BUFF_LAST = 1;
+
+.float items; // buff ID
+.string netname; // buff name
+.string message; // human readable name
+.vector colormod; // buff color
+.string model2; // buff sprite
+.float skin; // buff skin
+
+#define REGISTER_BUFF(hname,sname,NAME,bskin,bcolor) \
+       var float BUFF_##NAME; \
+       var entity Buff_Type##sname; \
+       void RegisterBuffs_##sname() \
+       { \
+               BUFF_##NAME = BUFF_LAST * 2; \
+               BUFF_LAST = BUFF_##NAME; \
+               Buff_Type##sname = spawn(); \
+               Buff_Type##sname.items = BUFF_##NAME; \
+               Buff_Type##sname.netname = #sname; \
+               Buff_Type##sname.message = hname; \
+               Buff_Type##sname.skin = bskin; \
+               Buff_Type##sname.colormod = bcolor; \
+               Buff_Type##sname.model2 = strzone(strcat("buff-", #sname)); \
+               if(!Buff_Type_first) \
+                       Buff_Type_first = Buff_Type##sname; \
+               if(Buff_Type_last) \
+                       Buff_Type_last.enemy = Buff_Type##sname; \
+               Buff_Type_last = Buff_Type##sname; \
+       } \
+       ACCUMULATE_FUNCTION(RegisterBuffs, RegisterBuffs_##sname)
+
+REGISTER_BUFF(_("Ammo"),ammo,AMMO,3,'0.2 1 0.2');
+REGISTER_BUFF(_("Resistance"),resistance,RESISTANCE,0,'0.3 0.2 1');
+REGISTER_BUFF(_("Speed"),speed,SPEED,9,'1 1 0.2');
+REGISTER_BUFF(_("Medic"),medic,MEDIC,1,'1 0.3 1');
+REGISTER_BUFF(_("Bash"),bash,BASH,5,'1 0.4 0');
+REGISTER_BUFF(_("Vampire"),vampire,VAMPIRE,2,'1 0.15 0');
+REGISTER_BUFF(_("Disability"),disability,DISABILITY,7,'0.66 0.66 0.73');
+REGISTER_BUFF(_("Vengeance"),vengeance,VENGEANCE,15,'0.55 0.5 1');
+REGISTER_BUFF(_("Jump"),jump,JUMP,10,'0.7 0.2 1');
+REGISTER_BUFF(_("Flight"),flight,FLIGHT,11,'1 0.2 0.5');
+REGISTER_BUFF(_("Invisible"),invisible,INVISIBLE,12,'0.9 0.9 0.9');
+#undef REGISTER_BUFF
+
+#ifdef SVQC
+.float buffs;
+void buff_Init(entity ent);
+void buff_Init_Compat(entity ent, float replacement);
+
+#define BUFF_SPAWNFUNC(e,b,t) void spawnfunc_item_buff_##e() { self.buffs = b; self.team = t; buff_Init(self); }
+#define BUFF_SPAWNFUNC_Q3TA_COMPAT(o,r) void spawnfunc_item_##o() { buff_Init_Compat(self,r); }
+#define BUFF_SPAWNFUNCS(e,b)                         \
+        BUFF_SPAWNFUNC(e,           b,  0)           \
+        BUFF_SPAWNFUNC(e##_team1,   b,  NUM_TEAM_1) \
+        BUFF_SPAWNFUNC(e##_team2,   b,  NUM_TEAM_2) \
+        BUFF_SPAWNFUNC(e##_team3,   b,  NUM_TEAM_3) \
+        BUFF_SPAWNFUNC(e##_team4,   b,  NUM_TEAM_4) 
+
+BUFF_SPAWNFUNCS(resistance,            BUFF_RESISTANCE)
+BUFF_SPAWNFUNCS(ammo,                  BUFF_AMMO)
+BUFF_SPAWNFUNCS(speed,                 BUFF_SPEED)
+BUFF_SPAWNFUNCS(medic,                 BUFF_MEDIC)
+BUFF_SPAWNFUNCS(bash,                  BUFF_BASH)
+BUFF_SPAWNFUNCS(vampire,               BUFF_VAMPIRE)
+BUFF_SPAWNFUNCS(disability,            BUFF_DISABILITY)
+BUFF_SPAWNFUNCS(vengeance,             BUFF_VENGEANCE)
+BUFF_SPAWNFUNCS(jump,                  BUFF_JUMP)
+BUFF_SPAWNFUNCS(flight,                        BUFF_FLIGHT)
+BUFF_SPAWNFUNCS(invisible,             BUFF_INVISIBLE)
+BUFF_SPAWNFUNCS(random,                        0)
+
+BUFF_SPAWNFUNC_Q3TA_COMPAT(doubler,    BUFF_MEDIC)
+BUFF_SPAWNFUNC_Q3TA_COMPAT(resistance, BUFF_RESISTANCE)
+BUFF_SPAWNFUNC_Q3TA_COMPAT(scout,      BUFF_SPEED)
+BUFF_SPAWNFUNC_Q3TA_COMPAT(ammoregen,  BUFF_AMMO)
+
+// actually Q3
+BUFF_SPAWNFUNC_Q3TA_COMPAT(haste,      BUFF_SPEED)
+BUFF_SPAWNFUNC_Q3TA_COMPAT(invis,      BUFF_INVISIBLE)
+BUFF_SPAWNFUNC_Q3TA_COMPAT(medic,      BUFF_MEDIC)
+#endif
+
+vector Buff_Color(float buff_id);
+string Buff_PrettyName(float buff_id);
+string Buff_Name(float buff_id);
+float Buff_Type_FromName(string buff_name);
+float Buff_Type_FromSprite(string buff_sprite);
+float Buff_Skin(float buff_id);
+string Buff_Sprite(float buff_id);
index bf312e3ad6401a1dfda0e0487c044229e313838a..7c8e9b35d32ec342abc38aa5860edac3edef9433 100644 (file)
@@ -139,78 +139,6 @@ const float CVAR_READONLY = 4;
 ///////////////////////////
 // csqc communication stuff
 
-const float STAT_KH_KEYS = 32;
-const float STAT_CTF_STATE = 33;
-const float STAT_WEAPONS = 35;
-const float STAT_SWITCHWEAPON = 36;
-const float STAT_GAMESTARTTIME = 37;
-const float STAT_STRENGTH_FINISHED = 38;
-const float STAT_INVINCIBLE_FINISHED = 39;
-const float STAT_PRESSED_KEYS = 42;
-const float STAT_ALLOW_OLDNEXBEAM = 43; // this stat could later contain some other bits of info, like, more server-side particle config
-const float STAT_FUEL = 44;
-const float STAT_NB_METERSTART = 45;
-const float STAT_SHOTORG = 46; // compressShotOrigin
-const float STAT_LEADLIMIT = 47;
-const float STAT_WEAPON_CLIPLOAD = 48;
-const float STAT_WEAPON_CLIPSIZE = 49;
-const float STAT_NEX_CHARGE = 50;
-const float STAT_LAST_PICKUP = 51;
-const float STAT_HUD = 52;
-const float STAT_NEX_CHARGEPOOL = 53;
-const float STAT_HIT_TIME = 54;
-const float STAT_TYPEHIT_TIME = 55;
-const float STAT_LAYED_MINES = 56;
-const float STAT_HAGAR_LOAD = 57;
-const float STAT_SWITCHINGWEAPON = 58;
-const float STAT_SUPERWEAPONS_FINISHED = 59;
-
-const float STAT_VEHICLESTAT_HEALTH = 60;
-const float STAT_VEHICLESTAT_SHIELD = 61;
-const float STAT_VEHICLESTAT_ENERGY = 62;
-const float STAT_VEHICLESTAT_AMMO1 = 63;
-const float STAT_VEHICLESTAT_RELOAD1 = 64;
-const float STAT_VEHICLESTAT_AMMO2 = 65;
-const float STAT_VEHICLESTAT_RELOAD2 = 66;
-
-const float STAT_SECRETS_TOTAL = 70;
-const float STAT_SECRETS_FOUND = 71;
-
-const float STAT_RESPAWN_TIME = 72;
-const float STAT_ROUNDSTARTTIME = 73;
-
-const float STAT_WEAPONS2 = 74;
-const float STAT_WEAPONS3 = 75;
-
-const float STAT_MONSTERS_TOTAL = 76;
-const float STAT_MONSTERS_KILLED = 77;
-
-// mod stats (1xx)
-const float STAT_REDALIVE = 100;
-const float STAT_BLUEALIVE = 101;
-const float STAT_YELLOWALIVE = 102;
-const float STAT_PINKALIVE = 103;
-
-// freeze tag
-const float STAT_FROZEN = 104;
-const float STAT_REVIVE_PROGRESS = 105;
-
-// domination
-const float STAT_DOM_TOTAL_PPS = 100;
-const float STAT_DOM_PPS_RED = 101;
-const float STAT_DOM_PPS_BLUE = 102;
-const float STAT_DOM_PPS_PINK = 103;
-const float STAT_DOM_PPS_YELLOW = 104;
-
-//const float STAT_SPIDERBOT_AIM 53 // compressShotOrigin
-//const float STAT_SPIDERBOT_TARGET 54 // compressShotOrigin
-
-// see DP source, quakedef.h
-const float STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW = 222;
-const float STAT_MOVEVARS_AIRSTRAFEACCEL_QW = 223;
-const float STAT_MOVEVARS_MAXSPEED = 244;
-const float STAT_MOVEVARS_AIRACCEL_QW = 254;
-
 const float CTF_STATE_ATTACK = 1;
 const float CTF_STATE_DEFEND = 2;
 const float CTF_STATE_COMMANDER = 3;
@@ -230,7 +158,7 @@ const vector eZ = '0 0 1';
 
 // moved that here so the client knows the max.
 // # of maps, I'll use arrays for them :P
-#define MAPVOTE_COUNT 10
+#define MAPVOTE_COUNT 30
 
 /**
  * Lower scores are better (e.g. suicides)
@@ -438,3 +366,8 @@ noref var vector autocvar_sv_player_headsize = '24 24 12';
 #define URI_GET_UPDATENOTIFICATION 33
 #define URI_GET_URLLIB 128
 #define URI_GET_URLLIB_END 191
+
+// gametype votes
+#define GTV_AVAILABLE 0
+// for later use in per-map gametype filtering
+#define GTV_FORBIDDEN 2
index 1265e7eea05736d3ac2270e1e0b3a33af5e141c5..595cad7eea387401d507ee998b6031b663f91e1d 100644 (file)
@@ -4,6 +4,7 @@
 
 #define DEATHTYPES \
        DEATHTYPE(DEATH_AUTOTEAMCHANGE,         DEATH_SELF_AUTOTEAMCHANGE,          NO_MSG,                        DEATH_SPECIAL_START) \
+       DEATHTYPE(DEATH_BUFF_VENGEANCE,         NO_MSG,                             DEATH_MURDER_VENGEANCE,        NORMAL_POS) \
        DEATHTYPE(DEATH_CAMP,                   DEATH_SELF_CAMP,                    NO_MSG,                        NORMAL_POS) \
        DEATHTYPE(DEATH_CHEAT,                  DEATH_SELF_CHEAT,                   DEATH_MURDER_CHEAT,            NORMAL_POS) \
        DEATHTYPE(DEATH_CUSTOM,                 DEATH_SELF_CUSTOM,                  NO_MSG,                        NORMAL_POS) \
index b8fe58b34481a868bb7883e773d887183af876d7..b060b375f08df8f4aeaa7bd1349ab17b4d3ea434 100644 (file)
@@ -696,6 +696,15 @@ float MapInfo_Type_FromString(string t)
        return 0;
 }
 
+string MapInfo_Type_Description(float t)
+{
+       entity e;
+       for(e = MapInfo_Type_first; e; e = e.enemy)
+               if(t == e.items)
+                       return e.gametype_description;
+       return "";
+}
+
 string MapInfo_Type_ToString(float t)
 {
        entity e;
@@ -1265,14 +1274,14 @@ void MapInfo_LoadMap(string s, float reinit)
                localcmd(strcat("\nchangelevel ", s, "\n"));
 }
 
-string MapInfo_ListAllowedMaps(float pRequiredFlags, float pForbiddenFlags)
+string MapInfo_ListAllowedMaps(float type, float pRequiredFlags, float pForbiddenFlags)
 {
        string out;
        float i;
 
        // to make absolutely sure:
        MapInfo_Enumerate();
-       MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), pRequiredFlags, pForbiddenFlags, 0);
+       MapInfo_FilterGametype(type, MapInfo_CurrentFeatures(), pRequiredFlags, pForbiddenFlags, 0);
 
        out = "";
        for(i = 0; i < MapInfo_count; ++i)
index 3c0afec9827f87010a6c7fe4a7b67117dfb178b5..aa1d8fd5e7fbd3c05756aed46131fb0bcd2cdd61 100644 (file)
@@ -8,8 +8,9 @@ entity MapInfo_Type_last;
 .string mdl; // game type short name
 .string message; // human readable name
 .string model2; // game type defaults
+.string gametype_description; // game type description
 
-#define REGISTER_GAMETYPE(hname,sname,g_name,NAME,defaults) \
+#define REGISTER_GAMETYPE(hname,sname,g_name,NAME,defaults,gdescription) \
        var float MAPINFO_TYPE_##NAME; \
        var entity MapInfo_Type##g_name; \
        void RegisterGametypes_##g_name() \
@@ -22,6 +23,7 @@ entity MapInfo_Type_last;
                MapInfo_Type##g_name.mdl = #sname; \
                MapInfo_Type##g_name.message = hname; \
                MapInfo_Type##g_name.model2 = defaults; \
+               MapInfo_Type##g_name.gametype_description = gdescription; \
                if(!MapInfo_Type_first) \
                        MapInfo_Type_first = MapInfo_Type##g_name; \
                if(MapInfo_Type_last) \
@@ -33,49 +35,49 @@ entity MapInfo_Type_last;
 #define IS_GAMETYPE(NAME) \
        (MapInfo_LoadedGametype == MAPINFO_TYPE_##NAME)
 
-REGISTER_GAMETYPE(_("Deathmatch"),dm,g_dm,DEATHMATCH,"timelimit=20 pointlimit=30 leadlimit=0");
+REGISTER_GAMETYPE(_("Deathmatch"),dm,g_dm,DEATHMATCH,"timelimit=20 pointlimit=30 leadlimit=0",_("Kill all enemies"));
 #define g_dm IS_GAMETYPE(DEATHMATCH)
 
-REGISTER_GAMETYPE(_("Last Man Standing"),lms,g_lms,LMS,"timelimit=20 lives=9 leadlimit=0");
+REGISTER_GAMETYPE(_("Last Man Standing"),lms,g_lms,LMS,"timelimit=20 lives=9 leadlimit=0",_("Survive and kill until the enemies have no lives left"));
 #define g_lms IS_GAMETYPE(LMS)
 
-REGISTER_GAMETYPE(_("Race"),rc,g_race,RACE,"timelimit=20 qualifying_timelimit=5 laplimit=7 teamlaplimit=15 leadlimit=0");
+REGISTER_GAMETYPE(_("Race"),rc,g_race,RACE,"timelimit=20 qualifying_timelimit=5 laplimit=7 teamlaplimit=15 leadlimit=0",_("Race against other players to the finish line"));
 #define g_race IS_GAMETYPE(RACE)
 
-REGISTER_GAMETYPE(_("Race CTS"),cts,g_cts,CTS,"timelimit=20 skill=-1");
+REGISTER_GAMETYPE(_("Race CTS"),cts,g_cts,CTS,"timelimit=20 skill=-1",_("Race for fastest time"));
 #define g_cts IS_GAMETYPE(CTS)
 
-REGISTER_GAMETYPE(_("Team Deathmatch"),tdm,g_tdm,TEAM_DEATHMATCH,"timelimit=20 pointlimit=50 teams=2 leadlimit=0");
+REGISTER_GAMETYPE(_("Team Deathmatch"),tdm,g_tdm,TEAM_DEATHMATCH,"timelimit=20 pointlimit=50 teams=2 leadlimit=0",_("Kill all enemy teammates"));
 #define g_tdm IS_GAMETYPE(TEAM_DEATHMATCH)
 
-REGISTER_GAMETYPE(_("Capture the Flag"),ctf,g_ctf,CTF,"timelimit=20 caplimit=10 leadlimit=0");
+REGISTER_GAMETYPE(_("Capture the Flag"),ctf,g_ctf,CTF,"timelimit=20 caplimit=10 leadlimit=0",_("Find and bring the enemy flag to your base to capture it"));
 #define g_ctf IS_GAMETYPE(CTF)
 
-REGISTER_GAMETYPE(_("Clan Arena"),ca,g_ca,CA,"timelimit=20 pointlimit=10 leadlimit=0");
+REGISTER_GAMETYPE(_("Clan Arena"),ca,g_ca,CA,"timelimit=20 pointlimit=10 leadlimit=0",_("Kill all enemy teammates to win the round"));
 #define g_ca IS_GAMETYPE(CA)
 
-REGISTER_GAMETYPE(_("Domination"),dom,g_domination,DOMINATION,"timelimit=20 pointlimit=200 teams=2 leadlimit=0");
+REGISTER_GAMETYPE(_("Domination"),dom,g_domination,DOMINATION,"timelimit=20 pointlimit=200 teams=2 leadlimit=0",_("Capture all the control points to win"));
 #define g_domination IS_GAMETYPE(DOMINATION)
 
-REGISTER_GAMETYPE(_("Key Hunt"),kh,g_keyhunt,KEYHUNT,"timelimit=20 pointlimit=1000 teams=3 leadlimit=0");
+REGISTER_GAMETYPE(_("Key Hunt"),kh,g_keyhunt,KEYHUNT,"timelimit=20 pointlimit=1000 teams=3 leadlimit=0",_("Gather all the keys to win the round"));
 #define g_keyhunt IS_GAMETYPE(KEYHUNT)
 
-REGISTER_GAMETYPE(_("Assault"),as,g_assault,ASSAULT,"timelimit=20");
+REGISTER_GAMETYPE(_("Assault"),as,g_assault,ASSAULT,"timelimit=20",_("Destroy obstacles to find and destroy the enemy power core before time runs out"));
 #define g_assault IS_GAMETYPE(ASSAULT)
 
-REGISTER_GAMETYPE(_("Onslaught"),ons,g_onslaught,ONSLAUGHT,"timelimit=20");
+REGISTER_GAMETYPE(_("Onslaught"),ons,g_onslaught,ONSLAUGHT,"timelimit=20",_("Capture control points to reach and destroy the enemy generator"));
 #define g_onslaught IS_GAMETYPE(ONSLAUGHT)
 
-REGISTER_GAMETYPE(_("Nexball"),nb,g_nexball,NEXBALL,"timelimit=20 pointlimit=5 leadlimit=0");
+REGISTER_GAMETYPE(_("Nexball"),nb,g_nexball,NEXBALL,"timelimit=20 pointlimit=5 leadlimit=0",_("XonSports"));
 #define g_nexball IS_GAMETYPE(NEXBALL)
 
-REGISTER_GAMETYPE(_("Freeze Tag"),ft,g_freezetag,FREEZETAG,"timelimit=20 pointlimit=10 teams=2 leadlimit=0");
+REGISTER_GAMETYPE(_("Freeze Tag"),ft,g_freezetag,FREEZETAG,"timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill enemies to freeze them, stand next to teammates to revive them"));
 #define g_freezetag IS_GAMETYPE(FREEZETAG)
 
-REGISTER_GAMETYPE(_("Keepaway"),ka,g_keepaway,KEEPAWAY,"timelimit=20 pointlimit=30");
+REGISTER_GAMETYPE(_("Keepaway"),ka,g_keepaway,KEEPAWAY,"timelimit=20 pointlimit=30",_("Hold the ball to get points for kills"));
 #define g_keepaway IS_GAMETYPE(KEEPAWAY)
 
-REGISTER_GAMETYPE(_("Invasion"),inv,g_invasion,INVASION,"pointlimit=50 teams=0");
+REGISTER_GAMETYPE(_("Invasion"),inv,g_invasion,INVASION,"pointlimit=50 teams=0",_("Survive against waves of monsters"));
 #define g_invasion IS_GAMETYPE(INVASION)
 
 const float MAPINFO_FEATURE_WEAPONS       = 1; // not defined for minstagib-only maps
@@ -134,13 +136,14 @@ float MapInfo_CheckMap(string s); // returns 0 if the map can't be played with t
 void MapInfo_LoadMap(string s, float reinit);
 
 // list all maps for the current game type
-string MapInfo_ListAllowedMaps(float pFlagsRequired, float pFlagsForbidden);
+string MapInfo_ListAllowedMaps(float type, float pFlagsRequired, float pFlagsForbidden);
 // list all allowed maps (for any game type)
 string MapInfo_ListAllAllowedMaps(float pFlagsRequired, float pFlagsForbidden);
 
 // gets a gametype from a string
 string _MapInfo_GetDefaultEx(float t);
 float MapInfo_Type_FromString(string t);
+string MapInfo_Type_Description(float t);
 string MapInfo_Type_ToString(float t);
 string MapInfo_Type_ToText(float t);
 void MapInfo_SwitchGameType(float t);
index 117965f7bb016aacb21fbb0ce5733197db86e231..9c55e457b947cd479ba7fc6ac2d98d2f93913b35 100644 (file)
@@ -378,6 +378,7 @@ void Send_Notification_WOCOVA(
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VH_WAKI_DEATH,     3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_death",         _("^BG%s%s^K1 got caught in the blast when ^BG%s^K1's Racer exploded%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VH_WAKI_GUN,       3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_death",         _("^BG%s%s^K1 was bolted down by ^BG%s^K1's Racer%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VH_WAKI_ROCKET,    3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_death",         _("^BG%s%s^K1 couldn't find shelter from ^BG%s^K1's Racer%s%s"), "") \
+       MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VENGEANCE,         3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_death",         _("^BG%s%s^K1 was destroyed by the vengeful ^BG%s^K1%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VOID,              3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",  "notify_void",          _("^BG%s%s^K1 was thrown into a world of hurt by ^BG%s^K1%s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_AUTOTEAMCHANGE,      2, 1, "s1 s2loc death_team", "",         "",                     _("^BG%s^K1 was moved into the %s%s"), "") \
        MSG_INFO_NOTIF(1, INFO_DEATH_SELF_BETRAYAL,            2, 1, "s1 s2loc spree_lost", "s1",       "notify_teamkill_red",  _("^BG%s^K1 became enemies with the Lord of Teamplay%s%s"), "") \
@@ -439,6 +440,10 @@ void Send_Notification_WOCOVA(
        MSG_INFO_NOTIF(1, INFO_ROUND_OVER,                     0, 0, "", "",                            "",                     _("^BGRound over, there's no winner"), "") \
        MSG_INFO_NOTIF(1, INFO_FREEZETAG_SELF,                 1, 0, "s1", "",                          "",                     _("^BG%s^K1 froze themself"), "") \
        MSG_INFO_NOTIF(1, INFO_GODMODE_OFF,                    0, 1, "f1", "",                          "",                     _("^BGGodmode saved you %s units of damage, cheater!"), "") \
+       MSG_INFO_NOTIF(1, INFO_ITEM_BUFF,                      1, 1, "s1 item_buffname", "",            "",                     _("^BG%s^BG got the %s^BG Buff!"), "") \
+       MSG_INFO_NOTIF(1, INFO_ITEM_BUFF_LOST,                 1, 1, "s1 item_buffname", "",            "",                     _("^BG%s^BG lost the %s^BG Buff!"), "") \
+       MSG_INFO_NOTIF(1, INFO_ITEM_BUFF_DROP,                 0, 1, "item_buffname", "",               "",                     _("^BGYou dropped the %s^BG Buff!"), "") \
+       MSG_INFO_NOTIF(1, INFO_ITEM_BUFF_GOT,                  0, 1, "item_buffname", "",               "",                     _("^BGYou got the %s^BG Buff!"), "") \
        MSG_INFO_NOTIF(0, INFO_ITEM_WEAPON_DONTHAVE,           0, 1, "item_wepname", "",                      "",               _("^BGYou do not have the ^F1%s"), "") \
        MSG_INFO_NOTIF(0, INFO_ITEM_WEAPON_DROP,               1, 1, "item_wepname item_wepammo", "",         "",               _("^BGYou dropped the ^F1%s^BG%s"), "") \
        MSG_INFO_NOTIF(0, INFO_ITEM_WEAPON_GOT,                0, 1, "item_wepname", "",                      "",               _("^BGYou got the ^F1%s"), "") \
@@ -627,6 +632,8 @@ void Send_Notification_WOCOVA(
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_SELF,              0, 0, "",             NO_CPID,             "0 0", _("^K1You froze yourself"), "") \
        MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_SPAWN_LATE,        0, 0, "",             NO_CPID,             "0 0", _("^K1Round already started, you spawn as frozen"), "") \
        MSG_CENTER_NOTIF(1, CENTER_INVASION_SUPERMONSTER,       1, 0, "s1",           NO_CPID,             "0 0", _("^K1A %s has arrived!"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_ITEM_BUFF_DROP,              0, 1, "item_buffname",                     CPID_ITEM, "item_centime 0", _("^BGYou dropped the %s^BG Buff!"), "") \
+       MSG_CENTER_NOTIF(1, CENTER_ITEM_BUFF_GOT,               0, 1, "item_buffname",                     CPID_ITEM, "item_centime 0", _("^BGYou got the %s^BG Buff!"), "") \
        MSG_CENTER_NOTIF(1, CENTER_ITEM_WEAPON_DONTHAVE,        0, 1, "item_wepname",                      CPID_ITEM, "item_centime 0", _("^BGYou do not have the ^F1%s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_ITEM_WEAPON_DROP,            1, 1, "item_wepname item_wepammo",         CPID_ITEM, "item_centime 0", _("^BGYou dropped the ^F1%s^BG%s"), "") \
        MSG_CENTER_NOTIF(1, CENTER_ITEM_WEAPON_GOT,             0, 1, "item_wepname",                      CPID_ITEM, "item_centime 0", _("^BGYou got the ^F1%s"), "") \
@@ -717,6 +724,7 @@ void Send_Notification_WOCOVA(
        MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_WAKI_DEATH,           NO_MSG,        INFO_DEATH_MURDER_VH_WAKI_DEATH,           NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_WAKI_GUN,             NO_MSG,        INFO_DEATH_MURDER_VH_WAKI_GUN,             NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_WAKI_ROCKET,          NO_MSG,        INFO_DEATH_MURDER_VH_WAKI_ROCKET,          NO_MSG) \
+       MSG_MULTI_NOTIF(1, DEATH_MURDER_VENGEANCE,               NO_MSG,        INFO_DEATH_MURDER_VENGEANCE,               NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_MURDER_VOID,                    NO_MSG,        INFO_DEATH_MURDER_VOID,                    NO_MSG) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_AUTOTEAMCHANGE,            NO_MSG,        INFO_DEATH_SELF_AUTOTEAMCHANGE,            CENTER_DEATH_SELF_AUTOTEAMCHANGE) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_BETRAYAL,                  NO_MSG,        INFO_DEATH_SELF_BETRAYAL,                  CENTER_DEATH_SELF_BETRAYAL) \
@@ -767,6 +775,8 @@ void Send_Notification_WOCOVA(
        MSG_MULTI_NOTIF(1, DEATH_SELF_VH_WAKI_DEATH,             NO_MSG,        INFO_DEATH_SELF_VH_WAKI_DEATH,             CENTER_DEATH_SELF_VH_WAKI_DEATH) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_VH_WAKI_ROCKET,            NO_MSG,        INFO_DEATH_SELF_VH_WAKI_ROCKET,            CENTER_DEATH_SELF_VH_WAKI_ROCKET) \
        MSG_MULTI_NOTIF(1, DEATH_SELF_VOID,                      NO_MSG,        INFO_DEATH_SELF_VOID,                      CENTER_DEATH_SELF_VOID) \
+       MSG_MULTI_NOTIF(1, ITEM_BUFF_DROP,                       NO_MSG,        INFO_ITEM_BUFF_DROP,                       CENTER_ITEM_BUFF_DROP) \
+       MSG_MULTI_NOTIF(1, ITEM_BUFF_GOT,                        NO_MSG,        INFO_ITEM_BUFF_GOT,                        CENTER_ITEM_BUFF_GOT) \
        MSG_MULTI_NOTIF(1, ITEM_WEAPON_DONTHAVE,                 NO_MSG,        INFO_ITEM_WEAPON_DONTHAVE,                 CENTER_ITEM_WEAPON_DONTHAVE) \
        MSG_MULTI_NOTIF(1, ITEM_WEAPON_DROP,                     NO_MSG,        INFO_ITEM_WEAPON_DROP,                     CENTER_ITEM_WEAPON_DROP) \
        MSG_MULTI_NOTIF(1, ITEM_WEAPON_GOT,                      NO_MSG,        INFO_ITEM_WEAPON_GOT,                      CENTER_ITEM_WEAPON_GOT) \
@@ -934,6 +944,7 @@ var float autocvar_notification_show_sprees_center_specialonly = TRUE;
     item_wepname: return full name of a weapon from weaponid
     item_wepammo: ammo display for weapon from string
     item_centime: amount of time to display weapon message in centerprint
+    item_buffname: return full name of a buff from buffid
     death_team: show the full name of the team a player is switching from
 */
 
@@ -986,6 +997,7 @@ string arg_slot[NOTIF_MAX_ARGS];
     ARG_CASE(ARG_CS_SV,     "spree_end",     (autocvar_notification_show_sprees ? notif_arg_spree_inf(-1, "", "", f1) : "")) \
     ARG_CASE(ARG_CS_SV,     "spree_lost",    (autocvar_notification_show_sprees ? notif_arg_spree_inf(-2, "", "", f1) : "")) \
     ARG_CASE(ARG_CS_SV,     "item_wepname",  W_Name(f1)) \
+    ARG_CASE(ARG_CS_SV,     "item_buffname", sprintf("%s%s", rgb_to_hexcolor(Buff_Color(f1)), Buff_PrettyName(f1))) \
     ARG_CASE(ARG_CS_SV,     "item_wepammo",  (s1 != "" ? sprintf(_(" with %s"), s1) : "")) \
     ARG_CASE(ARG_DC,        "item_centime",  ftos(autocvar_notification_item_centerprinttime)) \
     ARG_CASE(ARG_SV,        "death_team",    Team_ColoredFullName(f1)) \
diff --git a/qcsrc/common/stats.qh b/qcsrc/common/stats.qh
new file mode 100644 (file)
index 0000000..7965597
--- /dev/null
@@ -0,0 +1,285 @@
+// Full list of all stat constants, icnluded in a single location for easy reference
+// 255 is the current limit (MAX_CL_STATS - 1), engine will need to be modified if you wish to add more stats
+
+const float MAX_CL_STATS                = 256;
+const float STAT_HEALTH                 = 0;
+// 1 empty?
+const float STAT_WEAPON                 = 2;
+const float STAT_AMMO                   = 3;
+const float STAT_ARMOR                  = 4;
+const float STAT_WEAPONFRAME            = 5;
+const float STAT_SHELLS                 = 6;
+const float STAT_NAILS                  = 7;
+const float STAT_ROCKETS                = 8;
+const float STAT_CELLS                  = 9;
+const float STAT_ACTIVEWEAPON           = 10;
+const float STAT_TOTALSECRETS           = 11;
+const float STAT_TOTALMONSTERS          = 12;
+const float STAT_SECRETS                = 13;
+const float STAT_MONSTERS               = 14;
+const float STAT_ITEMS                  = 15;
+const float STAT_VIEWHEIGHT             = 16;
+// 17 empty?
+// 18 empty?
+// 19 empty?
+// 20 empty?
+const float STAT_VIEWZOOM               = 21;
+// 22 empty?
+// 23 empty?
+// 24 empty?
+// 25 empty?
+// 26 empty?
+// 27 empty?
+// 28 empty?
+// 29 empty?
+// 30 empty?
+// 31 empty?
+const float STAT_KH_KEYS                = 32;
+const float STAT_CTF_STATE              = 33;
+// 34 empty?
+const float STAT_WEAPONS                = 35;
+const float STAT_SWITCHWEAPON           = 36;
+const float STAT_GAMESTARTTIME          = 37;
+const float STAT_STRENGTH_FINISHED      = 38;
+const float STAT_INVINCIBLE_FINISHED    = 39;
+// 40 empty?
+// 41 empty?
+const float STAT_PRESSED_KEYS           = 42;
+const float STAT_ALLOW_OLDNEXBEAM       = 43; // this stat could later contain some other bits of info, like, more server-side particle config
+const float STAT_FUEL                   = 44;
+const float STAT_NB_METERSTART          = 45;
+const float STAT_SHOTORG                = 46; // compressShotOrigin
+const float STAT_LEADLIMIT              = 47;
+const float STAT_WEAPON_CLIPLOAD        = 48;
+const float STAT_WEAPON_CLIPSIZE        = 49;
+const float STAT_NEX_CHARGE             = 50;
+const float STAT_LAST_PICKUP            = 51;
+const float STAT_HUD                    = 52;
+const float STAT_NEX_CHARGEPOOL         = 53;
+const float STAT_HIT_TIME               = 54;
+const float STAT_TYPEHIT_TIME           = 55;
+const float STAT_LAYED_MINES            = 56;
+const float STAT_HAGAR_LOAD             = 57;
+const float STAT_SWITCHINGWEAPON        = 58;
+const float STAT_SUPERWEAPONS_FINISHED  = 59;
+const float STAT_VEHICLESTAT_HEALTH     = 60;
+const float STAT_VEHICLESTAT_SHIELD     = 61;
+const float STAT_VEHICLESTAT_ENERGY     = 62;
+const float STAT_VEHICLESTAT_AMMO1      = 63;
+const float STAT_VEHICLESTAT_RELOAD1    = 64;
+const float STAT_VEHICLESTAT_AMMO2      = 65;
+const float STAT_VEHICLESTAT_RELOAD2    = 66;
+const float STAT_VEHICLESTAT_W2MODE     = 67;
+// 68 empty?
+// 69 empty?
+const float STAT_SECRETS_TOTAL          = 70;
+const float STAT_SECRETS_FOUND          = 71;
+const float STAT_RESPAWN_TIME           = 72;
+const float STAT_ROUNDSTARTTIME         = 73;
+const float STAT_WEAPONS2               = 74;
+const float STAT_WEAPONS3               = 75;
+const float STAT_MONSTERS_TOTAL         = 76;
+const float STAT_MONSTERS_KILLED        = 77;
+const float STAT_BUFFS                  = 78;
+// 79 empty?
+// 80 empty?
+// 86 empty?
+// 87 empty?
+// 88 empty?
+// 89 empty?
+// 90 empty?
+// 91 empty?
+// 92 empty?
+// 93 empty?
+// 94 empty?
+// 95 empty?
+// 96 empty?
+// 97 empty?
+// 98 empty?
+// 99 empty?
+
+
+/* The following stats change depending on the gamemode, so can share the same ID */
+// IDs 100 to 104 reserved for gamemodes
+
+// freeze tag, clan arena, jailbreak
+const float STAT_REDALIVE               = 100;
+const float STAT_BLUEALIVE              = 101;
+const float STAT_YELLOWALIVE            = 102;
+const float STAT_PINKALIVE              = 103;
+
+// domination
+const float STAT_DOM_TOTAL_PPS          = 100;
+const float STAT_DOM_PPS_RED            = 101;
+const float STAT_DOM_PPS_BLUE           = 102;
+const float STAT_DOM_PPS_YELLOW         = 103;
+const float STAT_DOM_PPS_PINK           = 104;
+
+// vip
+const float STAT_VIP                    = 100;
+const float STAT_VIP_RED                = 101;
+const float STAT_VIP_BLUE               = 102;
+const float STAT_VIP_YELLOW             = 103;
+const float STAT_VIP_PINK               = 104;
+
+// key hunt
+const float STAT_KH_REDKEY_TEAM         = 100;
+const float STAT_KH_BLUEKEY_TEAM        = 101;
+const float STAT_KH_YELLOWKEY_TEAM      = 102;
+const float STAT_KH_PINKKEY_TEAM        = 103;
+
+/* Gamemode-specific stats end here */
+
+
+const float STAT_FROZEN                 = 105;
+const float STAT_REVIVE_PROGRESS        = 106;
+// 107 empty?
+// 108 empty?
+// 109 empty?
+// 110 empty?
+// 111 empty?
+// 112 empty?
+// 113 empty?
+// 114 empty?
+// 115 empty?
+// 116 empty?
+// 117 empty?
+// 118 empty?
+// 119 empty?
+// 120 empty?
+// 121 empty?
+// 122 empty?
+// 123 empty?
+// 124 empty?
+// 125 empty?
+// 126 empty?
+// 127 empty?
+// 128 empty?
+// 129 empty?
+// 130 empty?
+// 131 empty?
+// 132 empty?
+// 133 empty?
+// 134 empty?
+// 135 empty?
+// 136 empty?
+// 137 empty?
+// 138 empty?
+// 139 empty?
+// 140 empty?
+// 141 empty?
+// 142 empty?
+// 143 empty?
+// 144 empty?
+// 145 empty?
+// 146 empty?
+// 147 empty?
+// 148 empty?
+// 149 empty?
+// 150 empty?
+// 151 empty?
+// 152 empty?
+// 153 empty?
+// 154 empty?
+// 155 empty?
+// 156 empty?
+// 157 empty?
+// 158 empty?
+// 159 empty?
+// 160 empty?
+// 161 empty?
+// 162 empty?
+// 162 empty?
+// 163 empty?
+// 164 empty?
+// 165 empty?
+// 166 empty?
+// 167 empty?
+// 168 empty?
+// 169 empty?
+// 170 empty?
+// 171 empty?
+// 172 empty?
+// 173 empty?
+// 174 empty?
+// 175 empty?
+// 176 empty?
+// 177 empty?
+// 178 empty?
+// 179 empty?
+// 180 empty?
+// 181 empty?
+// 182 empty?
+// 183 empty?
+// 184 empty?
+// 185 empty?
+// 186 empty?
+// 187 empty?
+// 188 empty?
+// 189 empty?
+// 190 empty?
+// 191 empty?
+// 192 empty?
+// 193 empty?
+// 194 empty?
+// 195 empty?
+// 196 empty?
+// 197 empty?
+// 198 empty?
+// 199 empty?
+// 200 empty?
+// 201 empty?
+// 202 empty?
+// 203 empty?
+// 204 empty?
+// 205 empty?
+// 206 empty?
+// 207 empty?
+// 208 empty?
+// 209 empty?
+// 210 empty?
+// 211 empty?
+// 212 empty?
+// 213 empty?
+// 214 empty?
+// 215 empty?
+// 216 empty?
+// 217 empty?
+// 218 empty?
+// 219 empty?
+const float STAT_MOVEVARS_AIRACCEL_QW_STRETCHFACTOR     = 220;
+const float STAT_MOVEVARS_AIRCONTROL_PENALTY            = 221;
+const float STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW           = 222;
+const float STAT_MOVEVARS_AIRSTRAFEACCEL_QW             = 223;
+const float STAT_MOVEVARS_AIRCONTROL_POWER              = 224;
+const float STAT_MOVEFLAGS                              = 225;
+const float STAT_MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL   = 226;
+const float STAT_MOVEVARS_WARSOWBUNNY_ACCEL             = 227;
+const float STAT_MOVEVARS_WARSOWBUNNY_TOPSPEED          = 228;
+const float STAT_MOVEVARS_WARSOWBUNNY_TURNACCEL         = 229;
+const float STAT_MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO   = 230;
+const float STAT_MOVEVARS_AIRSTOPACCELERATE             = 231;
+const float STAT_MOVEVARS_AIRSTRAFEACCELERATE           = 232;
+const float STAT_MOVEVARS_MAXAIRSTRAFESPEED             = 233;
+const float STAT_MOVEVARS_AIRCONTROL                    = 234;
+const float STAT_FRAGLIMIT                              = 235;
+const float STAT_TIMELIMIT                              = 236;
+const float STAT_MOVEVARS_WALLFRICTION                  = 237;
+const float STAT_MOVEVARS_FRICTION                      = 238;
+const float STAT_MOVEVARS_WATERFRICTION                 = 239;
+const float STAT_MOVEVARS_TICRATE                       = 240;
+const float STAT_MOVEVARS_TIMESCALE                     = 241;
+const float STAT_MOVEVARS_GRAVITY                       = 242;
+const float STAT_MOVEVARS_STOPSPEED                     = 243;
+const float STAT_MOVEVARS_MAXSPEED                      = 244;
+const float STAT_MOVEVARS_SPECTATORMAXSPEED             = 245;
+const float STAT_MOVEVARS_ACCELERATE                    = 246;
+const float STAT_MOVEVARS_AIRACCELERATE                 = 247;
+const float STAT_MOVEVARS_WATERACCELERATE               = 248;
+const float STAT_MOVEVARS_ENTGRAVITY                    = 249;
+const float STAT_MOVEVARS_JUMPVELOCITY                  = 250;
+const float STAT_MOVEVARS_EDGEFRICTION                  = 251;
+const float STAT_MOVEVARS_MAXAIRSPEED                   = 252;
+const float STAT_MOVEVARS_STEPHEIGHT                    = 253;
+const float STAT_MOVEVARS_AIRACCEL_QW                   = 254;
+const float STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION    = 255;
diff --git a/qcsrc/common/weapons/.DS_Store b/qcsrc/common/weapons/.DS_Store
new file mode 100644 (file)
index 0000000..31f6276
Binary files /dev/null and b/qcsrc/common/weapons/.DS_Store differ
diff --git a/qcsrc/menu/.DS_Store b/qcsrc/menu/.DS_Store
new file mode 100644 (file)
index 0000000..9813011
Binary files /dev/null and b/qcsrc/menu/.DS_Store differ
index ff1210c74780fe2d6a512854a75723748c60ba13..ee1ce5d31e025d709b9b3f983b3c420f7a0cc429 100644 (file)
 #include "xonotic/dialog_hudpanel_weapons.c"
 #include "xonotic/dialog_hudpanel_physics.c"
 #include "xonotic/dialog_hudpanel_centerprint.c"
+#include "xonotic/dialog_hudpanel_buffs.c"
 #include "xonotic/slider_picmip.c"
diff --git a/qcsrc/menu/xonotic/.DS_Store b/qcsrc/menu/xonotic/.DS_Store
new file mode 100644 (file)
index 0000000..e3da66e
Binary files /dev/null and b/qcsrc/menu/xonotic/.DS_Store differ
diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_buffs.c b/qcsrc/menu/xonotic/dialog_hudpanel_buffs.c
new file mode 100644 (file)
index 0000000..ac1033c
--- /dev/null
@@ -0,0 +1,22 @@
+#ifdef INTERFACE
+CLASS(XonoticHUDBuffsDialog) EXTENDS(XonoticRootDialog)
+       METHOD(XonoticHUDBuffsDialog, fill, void(entity))
+       ATTRIB(XonoticHUDBuffsDialog, title, string, _("Buffs Panel"))
+       ATTRIB(XonoticHUDBuffsDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT)
+       ATTRIB(XonoticHUDBuffsDialog, intendedWidth, float, 0.4)
+       ATTRIB(XonoticHUDBuffsDialog, rows, float, 15)
+       ATTRIB(XonoticHUDBuffsDialog, columns, float, 4)
+       ATTRIB(XonoticHUDBuffsDialog, name, string, "HUDbuffs")
+       ATTRIB(XonoticHUDBuffsDialog, requiresConnection, float, TRUE)
+ENDCLASS(XonoticHUDBuffsDialog)
+#endif
+
+#ifdef IMPLEMENTATION
+void XonoticHUDBuffsDialog_fill(entity me)
+{
+       entity e;
+       string panelname = "buffs";
+
+       DIALOG_HUDPANEL_COMMON();
+}
+#endif
index debe6bd975c0f5dcfe3fde58b185c595363358fe..5c58025fca1f5e3ac87501e585ffee2828e7aed3 100644 (file)
@@ -126,6 +126,10 @@ void MainWindow_configureMainWindow(entity me)
        i.configureDialog(i);
        me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
 
+       i = spawnXonoticHUDBuffsDialog();
+       i.configureDialog(i);
+       me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
+
 
        // dialogs used by settings
        me.userbindEditDialog = i = spawnXonoticUserbindEditDialog();
index e4195617f9d8542f966e7a196b84a57ba3f6b208..a1592de1754234887d02f4ad47e10732162d3934 100644 (file)
@@ -5,6 +5,7 @@ CLASS(XonoticPlayerModelSelector) EXTENDS(XonoticImage)
        METHOD(XonoticPlayerModelSelector, saveCvars, void(entity))
        METHOD(XonoticPlayerModelSelector, draw, void(entity))
        METHOD(XonoticPlayerModelSelector, resizeNotify, void(entity, vector, vector, vector, vector))
+       METHOD(XonoticPlayerModelSelector, showNotify, void(entity))
        ATTRIB(XonoticPlayerModelSelector, currentModel, string, string_null)
        ATTRIB(XonoticPlayerModelSelector, currentSkin, float, 0)
        ATTRIB(XonoticPlayerModelSelector, currentModelImage, string, string_null)
@@ -201,4 +202,9 @@ void XonoticPlayerModelSelector_resizeNotify(entity me, vector relOrigin, vector
        me.realFontSize_y = me.fontSize / absSize_y;
        me.realFontSize_x = me.fontSize / absSize_x;
 }
+
+void XonoticPlayerModelSelector_showNotify(entity me)
+{
+       me.configureXonoticPlayerModelSelector(me);
+}
 #endif
index 3e094e7d7e7ce91d28c380b8fe94ada5874b56f9..0387303ea3d2a041f7c91cf09ecc1c6c811b8d66 100644 (file)
@@ -162,8 +162,7 @@ void XonoticSkinList_drawListBoxItem(entity me, float i, vector absSize, float i
        s = me.skinParameter(me, i, SKINPARM_PREVIEW);
        draw_Picture(me.columnPreviewOrigin * eX, s, me.columnPreviewSize * eX + eY, '1 1 1', 1);
 
-       s = me.skinParameter(me, i, SKINPARM_NAME);
-       s = sprintf(_("%s: %s"), s, me.skinParameter(me, i, SKINPARM_TITLE));
+       s = me.skinParameter(me, i, SKINPARM_TITLE);
        s = draw_TextShortenToWidth(s, me.columnNameSize, 0, me.realFontSize);
        draw_Text(me.realUpperMargin1 * eY + (me.columnNameOrigin + 0.00 * (me.columnNameSize - draw_TextWidth(s, 0, me.realFontSize))) * eX, s, me.realFontSize, SKINCOLOR_SKINLIST_TITLE, SKINALPHA_TEXT, 0);
 
diff --git a/qcsrc/server/.DS_Store b/qcsrc/server/.DS_Store
new file mode 100644 (file)
index 0000000..fe45060
Binary files /dev/null and b/qcsrc/server/.DS_Store differ
index 080d701d866c083a81858fa90e22ae048e8f5111..3df7c17630851ab470d8ca1350b434cf34237c02 100644 (file)
@@ -1156,6 +1156,11 @@ float autocvar_sv_timeout_resumetime;
 float autocvar_sv_vote_call;
 float autocvar_sv_vote_change;
 string autocvar_sv_vote_commands;
+float autocvar_sv_vote_gametype;
+float autocvar_sv_vote_gametype_timeout;
+string autocvar_sv_vote_gametype_options;
+float autocvar_sv_vote_gametype_keeptwotime;
+float autocvar_sv_vote_gametype_default_current;
 float autocvar_sv_vote_limit;
 float autocvar_sv_vote_majority_factor;
 float autocvar_sv_vote_majority_factor_of_voted;
@@ -1283,3 +1288,33 @@ float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay;
 float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death;
 float autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health;
 float autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath;
+float autocvar_g_buffs_waypoint_distance;
+float autocvar_g_buffs_randomize;
+float autocvar_g_buffs_random_lifetime;
+float autocvar_g_buffs_random_location;
+float autocvar_g_buffs_random_location_attempts;
+float autocvar_g_buffs_spawn_count;
+float autocvar_g_buffs_replace_powerups;
+float autocvar_g_buffs_cooldown_activate;
+float autocvar_g_buffs_cooldown_respawn;
+float autocvar_g_buffs_resistance_blockpercent;
+float autocvar_g_buffs_medic_survive_chance;
+float autocvar_g_buffs_medic_survive_health;
+float autocvar_g_buffs_medic_rot;
+float autocvar_g_buffs_medic_max;
+float autocvar_g_buffs_medic_regen;
+float autocvar_g_buffs_vengeance_damage_multiplier;
+float autocvar_g_buffs_bash_force;
+float autocvar_g_buffs_bash_force_self;
+float autocvar_g_buffs_disability_time;
+float autocvar_g_buffs_disability_speed;
+float autocvar_g_buffs_disability_rate;
+float autocvar_g_buffs_speed_speed;
+float autocvar_g_buffs_speed_rate;
+float autocvar_g_buffs_speed_damage_take;
+float autocvar_g_buffs_speed_regen;
+float autocvar_g_buffs_vampire_damage_steal;
+float autocvar_g_buffs_invisible_alpha;
+float autocvar_g_buffs_flight_gravity;
+float autocvar_g_buffs_jump_height;
+
index 498e4645354ff92adc6e7a1c32f58cb656c69b68..bb24fd6774725cbb22af6ff56321c991cb9e84be 100644 (file)
@@ -1588,17 +1588,26 @@ float CalcRotRegen(float current, float regenstable, float regenfactor, float re
 
 void player_regen (void)
 {
+       float max_mod, regen_mod, rot_mod, limit_mod;
+       max_mod = regen_mod = rot_mod = limit_mod = 1;
+       regen_mod_max = max_mod;
+       regen_mod_regen = regen_mod;
+       regen_mod_rot = rot_mod;
+       regen_mod_limit = limit_mod;
        if(!MUTATOR_CALLHOOK(PlayerRegen))
        {
-               float minh, mina, maxh, maxa, limith, limita, max_mod, regen_mod, rot_mod, limit_mod;
+               float minh, mina, maxh, maxa, limith, limita;
                maxh = autocvar_g_balance_health_rotstable;
                maxa = autocvar_g_balance_armor_rotstable;
                minh = autocvar_g_balance_health_regenstable;
                mina = autocvar_g_balance_armor_regenstable;
                limith = autocvar_g_balance_health_limit;
                limita = autocvar_g_balance_armor_limit;
-
-               max_mod = regen_mod = rot_mod = limit_mod = 1;
+               
+               max_mod = regen_mod_max;
+               regen_mod = regen_mod_regen;
+               rot_mod = regen_mod_rot;
+               limit_mod = regen_mod_limit;
 
                maxh = maxh * max_mod;
                minh = minh * max_mod;
index 43c7be517958e718b864f25bbf46e699b741c90d..7563cf5202214e39a6a272d47311397343a74108 100644 (file)
@@ -23,14 +23,15 @@ void PlayerJump (void)
                return; // no jumping while blocked
 
        float doublejump = FALSE;
+       float mjumpheight = autocvar_sv_jumpvelocity;
 
        player_multijump = doublejump;
+       player_jumpheight = mjumpheight;
        if(MUTATOR_CALLHOOK(PlayerJump))
                return;
 
        doublejump = player_multijump;
-
-       float mjumpheight;
+       mjumpheight = player_jumpheight;
 
        if (autocvar_sv_doublejump)
        {
@@ -47,7 +48,6 @@ void PlayerJump (void)
                }
        }
 
-       mjumpheight = autocvar_sv_jumpvelocity;
        if (self.waterlevel >= WATERLEVEL_SWIMMING)
        {
                self.velocity_z = self.stat_sv_maxspeed * 0.7;
index c36033a2818886ff62ba943a24ca1aa49874f625..dac383633c105f98e3cb9d74ae843c51e072f309 100644 (file)
@@ -14,6 +14,10 @@ float W_WeaponRateFactor()
        float t;
        t = 1.0 / g_weaponratefactor;
 
+       weapon_rate = t;
+       MUTATOR_CALLHOOK(WeaponRateFactor);
+       t = weapon_rate;
+
        return t;
 }
 
index f635bff2a2f6399e209588a9487536201c340646..b049972b564ac18c89d7458e46ea10cbe234ff22 100644 (file)
@@ -55,7 +55,6 @@ const float SPAWNFLAG_NO_WAYPOINTS_FOR_ITEMS = 1;
 string redirection_target;
 float world_initialized;
 
-string GetMapname();
 string GetGametype();
 void GotoNextMap(float reinit);
 void ShuffleMaplist();
@@ -546,6 +545,7 @@ void spawnfunc___init_dedicated_server(void)
        CALL_ACCUMULATED_FUNCTION(RegisterGametypes);
        CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
        CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes);
+       CALL_ACCUMULATED_FUNCTION(RegisterBuffs);
 
        MapInfo_Enumerate();
        MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
@@ -595,6 +595,7 @@ void spawnfunc_worldspawn (void)
        CALL_ACCUMULATED_FUNCTION(RegisterGametypes);
        CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
        CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes);
+       CALL_ACCUMULATED_FUNCTION(RegisterBuffs);
 
        ServerProgsDB = db_load(strcat("server.db", autocvar_sessionid));
 
@@ -896,7 +897,6 @@ string GetGametype()
        return MapInfo_Type_ToString(MapInfo_LoadedGametype);
 }
 
-string getmapname_stored;
 string GetMapname()
 {
        return mapname;
@@ -1221,13 +1221,27 @@ float DoNextMapOverride(float reinit)
                return TRUE;
        }
        if(autocvar_nextmap != "")
-               if(MapInfo_CheckMap(autocvar_nextmap))
+       {
+               string m;
+               m = GameTypeVote_MapInfo_FixName(autocvar_nextmap);
+               cvar_set("nextmap",m);
+       
+               if(!m || gametypevote)
+                       return FALSE;
+               if(autocvar_sv_vote_gametype)
                {
-                       Map_Goto_SetStr(autocvar_nextmap);
+                       Map_Goto_SetStr(m);
+                       return FALSE;
+               }
+               
+               if(MapInfo_CheckMap(m))
+               {
+                       Map_Goto_SetStr(m);
                        Map_Goto(reinit);
                        alreadychangedlevel = TRUE;
                        return TRUE;
                }
+       }
        if(!reinit && autocvar_lastlevel)
        {
                cvar_settemp_restore();
@@ -1264,9 +1278,6 @@ When the player presses attack or jump, change to the next level
 ============
 */
 .float autoscreenshot;
-void() MapVote_Start;
-void() MapVote_Think;
-float mapvote_initialized;
 void IntermissionThink()
 {
        FixIntermissionClient(self);
@@ -2031,17 +2042,6 @@ void CheckRules_World()
 
        SetDefaultAlpha();
 
-       /*
-       MapVote_Think should now do that part
-       if (intermission_running)
-               if (time >= intermission_exittime + 60)
-               {
-                       if(!DoNextMapOverride())
-                               GotoNextMap();
-                       return;
-               }
-       */
-
        if (gameover)   // someone else quit the game already
        {
                if(player_count == 0) // Nobody there? Then let's go to the next map
@@ -2203,517 +2203,14 @@ void CheckRules_World()
        }
 }
 
-float mapvote_nextthink;
-float mapvote_initialized;
-float mapvote_keeptwotime;
-float mapvote_timeout;
-string mapvote_message;
-#define MAPVOTE_SCREENSHOT_DIRS_COUNT 4
-string mapvote_screenshot_dirs[MAPVOTE_SCREENSHOT_DIRS_COUNT];
-float mapvote_screenshot_dirs_count;
-
-float mapvote_count;
-float mapvote_count_real;
-string mapvote_maps[MAPVOTE_COUNT];
-float mapvote_maps_screenshot_dir[MAPVOTE_COUNT];
-string mapvote_maps_pakfile[MAPVOTE_COUNT];
-float mapvote_maps_suggested[MAPVOTE_COUNT];
-string mapvote_suggestions[MAPVOTE_COUNT];
-float mapvote_suggestion_ptr;
-float mapvote_voters;
-float mapvote_selections[MAPVOTE_COUNT];
-float mapvote_run;
-float mapvote_detail;
-float mapvote_abstain;
-.float mapvote;
-
-void MapVote_ClearAllVotes()
-{
-       FOR_EACH_CLIENT(other)
-               other.mapvote = 0;
-}
-
-string MapVote_Suggest(string m)
+string GotoMap(string m)
 {
-       float i;
-       if(m == "")
-               return "That's not how to use this command.";
-       if(!autocvar_g_maplist_votable_suggestions)
-               return "Suggestions are not accepted on this server.";
-       if(mapvote_initialized)
-               return "Can't suggest - voting is already in progress!";
-       m = MapInfo_FixName(m);
+       m = GameTypeVote_MapInfo_FixName(m);
        if (!m)
                return "The map you suggested is not available on this server.";
-       if(!autocvar_g_maplist_votable_suggestions_override_mostrecent)
-               if(Map_IsRecent(m))
-                       return "This server does not allow for recent maps to be played again. Please be patient for some rounds.";
-
+       if (!autocvar_sv_vote_gametype)
        if(!MapInfo_CheckMap(m))
                return "The map you suggested does not support the current game mode.";
-       for(i = 0; i < mapvote_suggestion_ptr; ++i)
-               if(mapvote_suggestions[i] == m)
-                       return "This map was already suggested.";
-       if(mapvote_suggestion_ptr >= MAPVOTE_COUNT)
-       {
-               i = floor(random() * mapvote_suggestion_ptr);
-       }
-       else
-       {
-               i = mapvote_suggestion_ptr;
-               mapvote_suggestion_ptr += 1;
-       }
-       if(mapvote_suggestions[i] != "")
-               strunzone(mapvote_suggestions[i]);
-       mapvote_suggestions[i] = strzone(m);
-       if(autocvar_sv_eventlog)
-               GameLogEcho(strcat(":vote:suggested:", m, ":", ftos(self.playerid)));
-       return strcat("Suggestion of ", m, " accepted.");
-}
-
-void MapVote_AddVotable(string nextMap, float isSuggestion)
-{
-       float j, i, o;
-       string pakfile, mapfile;
-
-       if(nextMap == "")
-               return;
-       for(j = 0; j < mapvote_count; ++j)
-               if(mapvote_maps[j] == nextMap)
-                       return;
-       // suggestions might be no longer valid/allowed after gametype switch!
-       if(isSuggestion)
-               if(!MapInfo_CheckMap(nextMap))
-                       return;
-       mapvote_maps[mapvote_count] = strzone(nextMap);
-       mapvote_maps_suggested[mapvote_count] = isSuggestion;
-
-       pakfile = string_null;
-       for(i = 0; i < mapvote_screenshot_dirs_count; ++i)
-       {
-               mapfile = strcat(mapvote_screenshot_dirs[i], "/", mapvote_maps[i]);
-               pakfile = whichpack(strcat(mapfile, ".tga"));
-               if(pakfile == "")
-                       pakfile = whichpack(strcat(mapfile, ".jpg"));
-               if(pakfile == "")
-                       pakfile = whichpack(strcat(mapfile, ".png"));
-               if(pakfile != "")
-                       break;
-       }
-       if(i >= mapvote_screenshot_dirs_count)
-               i = 0; // FIXME maybe network this error case, as that means there is no mapshot on the server?
-       for(o = strstr(pakfile, "/", 0)+1; o > 0; o = strstr(pakfile, "/", 0)+1)
-               pakfile = substring(pakfile, o, -1);
-
-       mapvote_maps_screenshot_dir[mapvote_count] = i;
-       mapvote_maps_pakfile[mapvote_count] = strzone(pakfile);
-
-       mapvote_count += 1;
-}
-
-void MapVote_Spawn();
-void MapVote_Init()
-{
-       float i;
-       float nmax, smax;
-
-       MapVote_ClearAllVotes();
-
-       mapvote_count = 0;
-       mapvote_detail = !autocvar_g_maplist_votable_nodetail;
-       mapvote_abstain = autocvar_g_maplist_votable_abstain;
-
-       if(mapvote_abstain)
-               nmax = min(MAPVOTE_COUNT - 1, autocvar_g_maplist_votable);
-       else
-               nmax = min(MAPVOTE_COUNT, autocvar_g_maplist_votable);
-       smax = min3(nmax, autocvar_g_maplist_votable_suggestions, mapvote_suggestion_ptr);
-
-       // we need this for AddVotable, as that cycles through the screenshot dirs
-       mapvote_screenshot_dirs_count = tokenize_console(autocvar_g_maplist_votable_screenshot_dir);
-       if(mapvote_screenshot_dirs_count == 0)
-               mapvote_screenshot_dirs_count = tokenize_console("maps levelshots");
-       mapvote_screenshot_dirs_count = min(mapvote_screenshot_dirs_count, MAPVOTE_SCREENSHOT_DIRS_COUNT);
-       for(i = 0; i < mapvote_screenshot_dirs_count; ++i)
-               mapvote_screenshot_dirs[i] = strzone(argv(i));
-
-       if(mapvote_suggestion_ptr)
-               for(i = 0; i < 100 && mapvote_count < smax; ++i)
-                       MapVote_AddVotable(mapvote_suggestions[floor(random() * mapvote_suggestion_ptr)], TRUE);
-
-       for(i = 0; i < 100 && mapvote_count < nmax; ++i)
-               MapVote_AddVotable(GetNextMap(), FALSE);
-
-       if(mapvote_count == 0)
-       {
-               bprint( "Maplist contains no single playable map!  Resetting it to default map list.\n" );
-               cvar_set("g_maplist", MapInfo_ListAllAllowedMaps(MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags()));
-               if(autocvar_g_maplist_shuffle)
-                       ShuffleMaplist();
-               localcmd("\nmenu_cmd sync\n");
-               for(i = 0; i < 100 && mapvote_count < nmax; ++i)
-                       MapVote_AddVotable(GetNextMap(), FALSE);
-       }
-
-       mapvote_count_real = mapvote_count;
-       if(mapvote_abstain)
-               MapVote_AddVotable("don't care", 0);
-
-       //dprint("mapvote count is ", ftos(mapvote_count), "\n");
-
-       mapvote_keeptwotime = time + autocvar_g_maplist_votable_keeptwotime;
-       mapvote_timeout = time + autocvar_g_maplist_votable_timeout;
-       if(mapvote_count_real < 3 || mapvote_keeptwotime <= time)
-               mapvote_keeptwotime = 0;
-       mapvote_message = "Choose a map and press its key!";
-
-       MapVote_Spawn();
-}
-
-void MapVote_SendPicture(float id)
-{
-       msg_entity = self;
-       WriteByte(MSG_ONE, SVC_TEMPENTITY);
-       WriteByte(MSG_ONE, TE_CSQC_PICTURE);
-       WriteByte(MSG_ONE, id);
-       WritePicture(MSG_ONE, strcat(mapvote_screenshot_dirs[mapvote_maps_screenshot_dir[id]], "/", mapvote_maps[id]), 3072);
-}
-
-float MapVote_GetMapMask()
-{
-       float mask, i, power;
-       mask = 0;
-       for(i = 0, power = 1; i < mapvote_count; ++i, power *= 2)
-               if(mapvote_maps[i] != "")
-                       mask |= power;
-       return mask;
-}
-
-entity mapvote_ent;
-float MapVote_SendEntity(entity to, float sf)
-{
-       float i;
-
-       if(sf & 1)
-               sf &= ~2; // if we send 1, we don't need to also send 2
-
-       WriteByte(MSG_ENTITY, ENT_CLIENT_MAPVOTE);
-       WriteByte(MSG_ENTITY, sf);
-
-       if(sf & 1)
-       {
-               // flag 1 == initialization
-               for(i = 0; i < mapvote_screenshot_dirs_count; ++i)
-                       WriteString(MSG_ENTITY, mapvote_screenshot_dirs[i]);
-               WriteString(MSG_ENTITY, "");
-               WriteByte(MSG_ENTITY, mapvote_count);
-               WriteByte(MSG_ENTITY, mapvote_abstain);
-               WriteByte(MSG_ENTITY, mapvote_detail);
-               WriteCoord(MSG_ENTITY, mapvote_timeout);
-               if(mapvote_count <= 8)
-                       WriteByte(MSG_ENTITY, MapVote_GetMapMask());
-               else
-                       WriteShort(MSG_ENTITY, MapVote_GetMapMask());
-               for(i = 0; i < mapvote_count; ++i)
-                       if(mapvote_maps[i] != "")
-                       {
-                               if(mapvote_abstain && i == mapvote_count - 1)
-                               {
-                                       WriteString(MSG_ENTITY, ""); // abstain needs no text
-                                       WriteString(MSG_ENTITY, ""); // abstain needs no pack
-                                       WriteByte(MSG_ENTITY, 0); // abstain needs no screenshot dir
-                               }
-                               else
-                               {
-                                       WriteString(MSG_ENTITY, mapvote_maps[i]);
-                                       WriteString(MSG_ENTITY, mapvote_maps_pakfile[i]);
-                                       WriteByte(MSG_ENTITY, mapvote_maps_screenshot_dir[i]);
-                               }
-                       }
-       }
-
-       if(sf & 2)
-       {
-               // flag 2 == update of mask
-               if(mapvote_count <= 8)
-                       WriteByte(MSG_ENTITY, MapVote_GetMapMask());
-               else
-                       WriteShort(MSG_ENTITY, MapVote_GetMapMask());
-       }
-
-       if(sf & 4)
-       {
-               if(mapvote_detail)
-                       for(i = 0; i < mapvote_count; ++i)
-                               if(mapvote_maps[i] != "")
-                                       WriteByte(MSG_ENTITY, mapvote_selections[i]);
-
-               WriteByte(MSG_ENTITY, to.mapvote);
-       }
-
-       return TRUE;
-}
-
-void MapVote_Spawn()
-{
-       Net_LinkEntity(mapvote_ent = spawn(), FALSE, 0, MapVote_SendEntity);
-}
-
-void MapVote_TouchMask()
-{
-       mapvote_ent.SendFlags |= 2;
-}
-
-void MapVote_TouchVotes(entity voter)
-{
-       mapvote_ent.SendFlags |= 4;
-}
-
-float MapVote_Finished(float mappos)
-{
-       string result;
-       float i;
-       float didntvote;
-
-       if(autocvar_sv_eventlog)
-       {
-               result = strcat(":vote:finished:", mapvote_maps[mappos]);
-               result = strcat(result, ":", ftos(mapvote_selections[mappos]), "::");
-               didntvote = mapvote_voters;
-               for(i = 0; i < mapvote_count; ++i)
-                       if(mapvote_maps[i] != "")
-                       {
-                               didntvote -= mapvote_selections[i];
-                               if(i != mappos)
-                               {
-                                       result = strcat(result, ":", mapvote_maps[i]);
-                                       result = strcat(result, ":", ftos(mapvote_selections[i]));
-                               }
-                       }
-               result = strcat(result, ":didn't vote:", ftos(didntvote));
-
-               GameLogEcho(result);
-               if(mapvote_maps_suggested[mappos])
-                       GameLogEcho(strcat(":vote:suggestion_accepted:", mapvote_maps[mappos]));
-       }
-
-       FOR_EACH_REALCLIENT(other)
-               FixClientCvars(other);
-
-       Map_Goto_SetStr(mapvote_maps[mappos]);
-       Map_Goto(0);
-       alreadychangedlevel = TRUE;
-       return TRUE;
-}
-void MapVote_CheckRules_1()
-{
-       float i;
-
-       for(i = 0; i < mapvote_count; ++i) if(mapvote_maps[i] != "")
-       {
-               //dprint("Map ", ftos(i), ": "); dprint(mapvote_maps[i], "\n");
-               mapvote_selections[i] = 0;
-       }
-
-       mapvote_voters = 0;
-       FOR_EACH_REALCLIENT(other)
-       {
-               ++mapvote_voters;
-               if(other.mapvote)
-               {
-                       i = other.mapvote - 1;
-                       //dprint("Player ", other.netname, " vote = ", ftos(other.mapvote - 1), "\n");
-                       mapvote_selections[i] = mapvote_selections[i] + 1;
-               }
-       }
-}
-
-float MapVote_CheckRules_2()
-{
-       float i;
-       float firstPlace, secondPlace;
-       float firstPlaceVotes, secondPlaceVotes;
-       float mapvote_voters_real;
-       string result;
-
-       if(mapvote_count_real == 1)
-               return MapVote_Finished(0);
-
-       mapvote_voters_real = mapvote_voters;
-       if(mapvote_abstain)
-               mapvote_voters_real -= mapvote_selections[mapvote_count - 1];
-
-       RandomSelection_Init();
-       for(i = 0; i < mapvote_count_real; ++i) if(mapvote_maps[i] != "")
-               RandomSelection_Add(world, i, string_null, 1, mapvote_selections[i]);
-       firstPlace = RandomSelection_chosen_float;
-       firstPlaceVotes = RandomSelection_best_priority;
-       //dprint("First place: ", ftos(firstPlace), "\n");
-       //dprint("First place votes: ", ftos(firstPlaceVotes), "\n");
-
-       RandomSelection_Init();
-       for(i = 0; i < mapvote_count_real; ++i) if(mapvote_maps[i] != "")
-               if(i != firstPlace)
-                       RandomSelection_Add(world, i, string_null, 1, mapvote_selections[i]);
-       secondPlace = RandomSelection_chosen_float;
-       secondPlaceVotes = RandomSelection_best_priority;
-       //dprint("Second place: ", ftos(secondPlace), "\n");
-       //dprint("Second place votes: ", ftos(secondPlaceVotes), "\n");
-
-       if(firstPlace == -1)
-               error("No first place in map vote... WTF?");
-
-       if(secondPlace == -1 || time > mapvote_timeout || (mapvote_voters_real - firstPlaceVotes) < firstPlaceVotes)
-               return MapVote_Finished(firstPlace);
-
-       if(mapvote_keeptwotime)
-               if(time > mapvote_keeptwotime || (mapvote_voters_real - firstPlaceVotes - secondPlaceVotes) < secondPlaceVotes)
-               {
-                       float didntvote;
-                       MapVote_TouchMask();
-                       mapvote_message = "Now decide between the TOP TWO!";
-                       mapvote_keeptwotime = 0;
-                       result = strcat(":vote:keeptwo:", mapvote_maps[firstPlace]);
-                       result = strcat(result, ":", ftos(firstPlaceVotes));
-                       result = strcat(result, ":", mapvote_maps[secondPlace]);
-                       result = strcat(result, ":", ftos(secondPlaceVotes), "::");
-                       didntvote = mapvote_voters;
-                       for(i = 0; i < mapvote_count; ++i)
-                               if(mapvote_maps[i] != "")
-                               {
-                                       didntvote -= mapvote_selections[i];
-                                       if(i != firstPlace)
-                                               if(i != secondPlace)
-                                               {
-                                                       result = strcat(result, ":", mapvote_maps[i]);
-                                                       result = strcat(result, ":", ftos(mapvote_selections[i]));
-                                                       if(i < mapvote_count_real)
-                                                       {
-                                                               strunzone(mapvote_maps[i]);
-                                                               mapvote_maps[i] = "";
-                                                               strunzone(mapvote_maps_pakfile[i]);
-                                                               mapvote_maps_pakfile[i] = "";
-                                                       }
-                                               }
-                               }
-                       result = strcat(result, ":didn't vote:", ftos(didntvote));
-                       if(autocvar_sv_eventlog)
-                               GameLogEcho(result);
-               }
-
-       return FALSE;
-}
-void MapVote_Tick()
-{
-       float keeptwo;
-       float totalvotes;
-
-       keeptwo = mapvote_keeptwotime;
-       MapVote_CheckRules_1(); // count
-       if(MapVote_CheckRules_2()) // decide
-               return;
-
-       totalvotes = 0;
-       FOR_EACH_REALCLIENT(other)
-       {
-               // hide scoreboard again
-               if(other.health != 2342)
-               {
-                       other.health = 2342;
-                       other.impulse = 0;
-                       if(IS_REAL_CLIENT(other))
-                       {
-                               msg_entity = other;
-                               WriteByte(MSG_ONE, SVC_FINALE);
-                               WriteString(MSG_ONE, "");
-                       }
-               }
-
-               // clear possibly invalid votes
-               if(mapvote_maps[other.mapvote - 1] == "")
-                       other.mapvote = 0;
-               // use impulses as new vote
-               if(other.impulse >= 1 && other.impulse <= mapvote_count)
-                       if(mapvote_maps[other.impulse - 1] != "")
-                       {
-                               other.mapvote = other.impulse;
-                               MapVote_TouchVotes(other);
-                       }
-               other.impulse = 0;
-
-               if(other.mapvote)
-                       ++totalvotes;
-       }
-
-       MapVote_CheckRules_1(); // just count
-}
-void MapVote_Start()
-{
-       if(mapvote_run)
-               return;
-
-       // wait for stats to be sent first
-       if(!playerstats_waitforme)
-               return;
-
-       MapInfo_Enumerate();
-       if(MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 1))
-               mapvote_run = TRUE;
-}
-void MapVote_Think()
-{
-       if(!mapvote_run)
-               return;
-
-       if(alreadychangedlevel)
-               return;
-
-       if(time < mapvote_nextthink)
-               return;
-       //dprint("tick\n");
-
-       mapvote_nextthink = time + 0.5;
-
-       if(!mapvote_initialized)
-       {
-               if(autocvar_rescan_pending == 1)
-               {
-                       cvar_set("rescan_pending", "2");
-                       localcmd("fs_rescan\nrescan_pending 3\n");
-                       return;
-               }
-               else if(autocvar_rescan_pending == 2)
-               {
-                       return;
-               }
-               else if(autocvar_rescan_pending == 3)
-               {
-                       // now build missing mapinfo files
-                       if(!MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 1))
-                               return;
-
-                       // we're done, start the timer
-                       cvar_set("rescan_pending", "0");
-               }
-
-               mapvote_initialized = TRUE;
-               if(DoNextMapOverride(0))
-                       return;
-               if(!autocvar_g_maplist_votable || player_count <= 0)
-               {
-                       GotoNextMap(0);
-                       return;
-               }
-               MapVote_Init();
-       }
-
-       MapVote_Tick();
-}
-
-string GotoMap(string m)
-{
-       if(!MapInfo_CheckMap(m))
-               return "The map you chose is not available on this server.";
        cvar_set("nextmap", m);
        cvar_set("timelimit", "-1");
        if(mapvote_initialized || alreadychangedlevel)
diff --git a/qcsrc/server/mapvoting.qc b/qcsrc/server/mapvoting.qc
new file mode 100644 (file)
index 0000000..3e1a866
--- /dev/null
@@ -0,0 +1,742 @@
+float GameTypeVote_AvailabilityStatus(string gtname) 
+{ 
+       float type = MapInfo_Type_FromString(gtname);
+       if( type == 0 )
+               return GTV_FORBIDDEN;
+       
+       if ( autocvar_nextmap != "" )
+       {
+               if ( !MapInfo_Get_ByName(autocvar_nextmap, FALSE, 0) )
+                       return GTV_FORBIDDEN;
+               if (!(MapInfo_Map_supportedGametypes & type))
+                       return GTV_FORBIDDEN;
+       }
+       
+       return GTV_AVAILABLE;
+}
+
+float GameTypeVote_GetMask()
+{
+       float n, j, gametype_mask;
+       n = tokenizebyseparator(autocvar_sv_vote_gametype_options, " ");
+       n = min(MAPVOTE_COUNT, n);
+       gametype_mask = 0;
+       for(j = 0; j < n; ++j)
+               gametype_mask |= MapInfo_Type_FromString(argv(j));
+       return gametype_mask;
+}
+
+string GameTypeVote_MapInfo_FixName(string m)
+{
+       if ( autocvar_sv_vote_gametype )
+       {
+               MapInfo_Enumerate();
+               MapInfo_FilterGametype(GameTypeVote_GetMask(), 0, MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
+       }
+       return MapInfo_FixName(m);
+}
+
+void MapVote_ClearAllVotes()
+{
+       FOR_EACH_CLIENT(other)
+               other.mapvote = 0;
+}
+
+void MapVote_UnzoneStrings()
+{
+       float j;
+       for(j = 0; j < mapvote_count; ++j)
+       {
+               if ( mapvote_maps[j] )
+               {
+                       strunzone(mapvote_maps[j]);
+                       mapvote_maps[j] = string_null;
+               }
+               if ( mapvote_maps_pakfile[j] )
+               {
+                       strunzone(mapvote_maps_pakfile[j]);
+                       mapvote_maps_pakfile[j] = string_null;
+               }
+       }
+}
+
+string MapVote_Suggest(string m)
+{
+       float i;
+       if(m == "")
+               return "That's not how to use this command.";
+       if(!autocvar_g_maplist_votable_suggestions)
+               return "Suggestions are not accepted on this server.";
+       if(mapvote_initialized)
+       if(!gametypevote)
+               return "Can't suggest - voting is already in progress!";
+       m = GameTypeVote_MapInfo_FixName(m);
+       if (!m)
+               return "The map you suggested is not available on this server.";
+       if(!autocvar_g_maplist_votable_suggestions_override_mostrecent)
+               if(Map_IsRecent(m))
+                       return "This server does not allow for recent maps to be played again. Please be patient for some rounds.";
+
+       if (!autocvar_sv_vote_gametype)
+       if(!MapInfo_CheckMap(m))
+               return "The map you suggested does not support the current game mode.";
+       for(i = 0; i < mapvote_suggestion_ptr; ++i)
+               if(mapvote_suggestions[i] == m)
+                       return "This map was already suggested.";
+       if(mapvote_suggestion_ptr >= MAPVOTE_COUNT)
+       {
+               i = floor(random() * mapvote_suggestion_ptr);
+       }
+       else
+       {
+               i = mapvote_suggestion_ptr;
+               mapvote_suggestion_ptr += 1;
+       }
+       if(mapvote_suggestions[i] != "")
+               strunzone(mapvote_suggestions[i]);
+       mapvote_suggestions[i] = strzone(m);
+       if(autocvar_sv_eventlog)
+               GameLogEcho(strcat(":vote:suggested:", m, ":", ftos(self.playerid)));
+       return strcat("Suggestion of ", m, " accepted.");
+}
+
+void MapVote_AddVotable(string nextMap, float isSuggestion)
+{
+       float j, i, o;
+       string pakfile, mapfile;
+
+       if(nextMap == "")
+               return;
+       for(j = 0; j < mapvote_count; ++j)
+               if(mapvote_maps[j] == nextMap)
+                       return;
+       // suggestions might be no longer valid/allowed after gametype switch!
+       if(isSuggestion)
+               if(!MapInfo_CheckMap(nextMap))
+                       return;
+       mapvote_maps[mapvote_count] = strzone(nextMap);
+       mapvote_maps_suggested[mapvote_count] = isSuggestion;
+
+       pakfile = string_null;
+       for(i = 0; i < mapvote_screenshot_dirs_count; ++i)
+       {
+               mapfile = strcat(mapvote_screenshot_dirs[i], "/", mapvote_maps[i]);
+               pakfile = whichpack(strcat(mapfile, ".tga"));
+               if(pakfile == "")
+                       pakfile = whichpack(strcat(mapfile, ".jpg"));
+               if(pakfile == "")
+                       pakfile = whichpack(strcat(mapfile, ".png"));
+               if(pakfile != "")
+                       break;
+       }
+       if(i >= mapvote_screenshot_dirs_count)
+               i = 0; // FIXME maybe network this error case, as that means there is no mapshot on the server?
+       for(o = strstr(pakfile, "/", 0)+1; o > 0; o = strstr(pakfile, "/", 0)+1)
+               pakfile = substring(pakfile, o, -1);
+
+       mapvote_maps_screenshot_dir[mapvote_count] = i;
+       mapvote_maps_pakfile[mapvote_count] = strzone(pakfile);
+       mapvote_maps_availability[mapvote_count] = GTV_AVAILABLE;
+
+       mapvote_count += 1;
+}
+
+void MapVote_Init()
+{
+       float i;
+       float nmax, smax;
+
+       MapVote_ClearAllVotes();
+       MapVote_UnzoneStrings();
+
+       mapvote_count = 0;
+       mapvote_detail = !autocvar_g_maplist_votable_nodetail;
+       mapvote_abstain = autocvar_g_maplist_votable_abstain;
+
+       if(mapvote_abstain)
+               nmax = min(MAPVOTE_COUNT - 1, autocvar_g_maplist_votable);
+       else
+               nmax = min(MAPVOTE_COUNT, autocvar_g_maplist_votable);
+       smax = min3(nmax, autocvar_g_maplist_votable_suggestions, mapvote_suggestion_ptr);
+
+       // we need this for AddVotable, as that cycles through the screenshot dirs
+       mapvote_screenshot_dirs_count = tokenize_console(autocvar_g_maplist_votable_screenshot_dir);
+       if(mapvote_screenshot_dirs_count == 0)
+               mapvote_screenshot_dirs_count = tokenize_console("maps levelshots");
+       mapvote_screenshot_dirs_count = min(mapvote_screenshot_dirs_count, MAPVOTE_SCREENSHOT_DIRS_COUNT);
+       for(i = 0; i < mapvote_screenshot_dirs_count; ++i)
+               mapvote_screenshot_dirs[i] = strzone(argv(i));
+
+       if(mapvote_suggestion_ptr)
+               for(i = 0; i < 100 && mapvote_count < smax; ++i)
+                       MapVote_AddVotable(mapvote_suggestions[floor(random() * mapvote_suggestion_ptr)], TRUE);
+
+       for(i = 0; i < 100 && mapvote_count < nmax; ++i)
+               MapVote_AddVotable(GetNextMap(), FALSE);
+
+       if(mapvote_count == 0)
+       {
+               bprint( "Maplist contains no single playable map!  Resetting it to default map list.\n" );
+               cvar_set("g_maplist", MapInfo_ListAllowedMaps(MapInfo_CurrentGametype(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags()));
+               if(autocvar_g_maplist_shuffle)
+                       ShuffleMaplist();
+               localcmd("\nmenu_cmd sync\n");
+               for(i = 0; i < 100 && mapvote_count < nmax; ++i)
+                       MapVote_AddVotable(GetNextMap(), FALSE);
+       }
+
+       mapvote_count_real = mapvote_count;
+       if(mapvote_abstain)
+               MapVote_AddVotable("don't care", 0);
+
+       //dprint("mapvote count is ", ftos(mapvote_count), "\n");
+
+       mapvote_keeptwotime = time + autocvar_g_maplist_votable_keeptwotime;
+       mapvote_timeout = time + autocvar_g_maplist_votable_timeout;
+       if(mapvote_count_real < 3 || mapvote_keeptwotime <= time)
+               mapvote_keeptwotime = 0;
+       mapvote_message = "Choose a map and press its key!";
+
+       MapVote_Spawn();
+}
+
+void MapVote_SendPicture(float id)
+{
+       msg_entity = self;
+       WriteByte(MSG_ONE, SVC_TEMPENTITY);
+       WriteByte(MSG_ONE, TE_CSQC_PICTURE);
+       WriteByte(MSG_ONE, id);
+       WritePicture(MSG_ONE, strcat(mapvote_screenshot_dirs[mapvote_maps_screenshot_dir[id]], "/", mapvote_maps[id]), 3072);
+}
+
+
+void MapVote_WriteMask()
+{
+       float i;
+       if ( mapvote_count < 24 )
+       {
+               float mask,power;
+               mask = 0;
+               for(i = 0, power = 1; i < mapvote_count; ++i, power *= 2)
+                       if(mapvote_maps_availability[i] == GTV_AVAILABLE )
+                               mask |= power;
+                       
+               if(mapvote_count < 8)
+                       WriteByte(MSG_ENTITY, mask);
+               else if (mapvote_count < 16)
+                       WriteShort(MSG_ENTITY,mask);
+               else
+                       WriteLong(MSG_ENTITY, mask);
+       }
+       else
+       {
+               for ( i = 0; i < mapvote_count; ++i )
+                       WriteByte(MSG_ENTITY, mapvote_maps_availability[i]);
+       }
+}
+
+float MapVote_SendEntity(entity to, float sf)
+{
+       float i;
+
+       if(sf & 1)
+               sf &= ~2; // if we send 1, we don't need to also send 2
+
+       WriteByte(MSG_ENTITY, ENT_CLIENT_MAPVOTE);
+       WriteByte(MSG_ENTITY, sf);
+
+       if(sf & 1)
+       {
+               // flag 1 == initialization
+               for(i = 0; i < mapvote_screenshot_dirs_count; ++i)
+                       WriteString(MSG_ENTITY, mapvote_screenshot_dirs[i]);
+               WriteString(MSG_ENTITY, "");
+               WriteByte(MSG_ENTITY, mapvote_count);
+               WriteByte(MSG_ENTITY, mapvote_abstain);
+               WriteByte(MSG_ENTITY, mapvote_detail);
+               WriteCoord(MSG_ENTITY, mapvote_timeout);
+               
+               if ( gametypevote )
+               {
+                       // gametype vote
+                       WriteByte(MSG_ENTITY, 1);
+                       WriteString(MSG_ENTITY, autocvar_nextmap);
+               }
+               else if ( autocvar_sv_vote_gametype )
+               {
+                        // map vote but gametype has been chosen via voting screen
+                       WriteByte(MSG_ENTITY, 2);
+                       WriteString(MSG_ENTITY, MapInfo_Type_ToText(MapInfo_CurrentGametype()));
+               }
+               else
+                       WriteByte(MSG_ENTITY, 0); // map vote
+
+               MapVote_WriteMask();
+
+               for(i = 0; i < mapvote_count; ++i)
+               {
+                       if(mapvote_abstain && i == mapvote_count - 1)
+                       {
+                               WriteString(MSG_ENTITY, ""); // abstain needs no text
+                               WriteString(MSG_ENTITY, ""); // abstain needs no pack
+                               WriteByte(MSG_ENTITY, 0); // abstain needs no screenshot dir
+                               WriteByte(MSG_ENTITY, GTV_AVAILABLE);
+                       }
+                       else
+                       {
+                               WriteString(MSG_ENTITY, mapvote_maps[i]);
+                               WriteString(MSG_ENTITY, mapvote_maps_pakfile[i]);
+                               WriteByte(MSG_ENTITY, mapvote_maps_screenshot_dir[i]);
+                               WriteByte(MSG_ENTITY, mapvote_maps_availability[i]);
+                       }
+               }
+       }
+
+       if(sf & 2)
+       {
+               // flag 2 == update of mask
+               MapVote_WriteMask();
+       }
+
+       if(sf & 4)
+       {
+               if(mapvote_detail)
+                       for(i = 0; i < mapvote_count; ++i)
+                               if ( mapvote_maps_availability[i] == GTV_AVAILABLE )
+                                       WriteByte(MSG_ENTITY, mapvote_selections[i]);
+
+               WriteByte(MSG_ENTITY, to.mapvote);
+       }
+
+       return TRUE;
+}
+
+void MapVote_Spawn()
+{
+       Net_LinkEntity(mapvote_ent = spawn(), FALSE, 0, MapVote_SendEntity);
+}
+
+void MapVote_TouchMask()
+{
+       mapvote_ent.SendFlags |= 2;
+}
+
+void MapVote_TouchVotes(entity voter)
+{
+       mapvote_ent.SendFlags |= 4;
+}
+
+float MapVote_Finished(float mappos)
+{
+       if(alreadychangedlevel)
+               return FALSE;
+
+       string result;
+       float i;
+       float didntvote;
+
+       if(autocvar_sv_eventlog)
+       {
+               result = strcat(":vote:finished:", mapvote_maps[mappos]);
+               result = strcat(result, ":", ftos(mapvote_selections[mappos]), "::");
+               didntvote = mapvote_voters;
+               for(i = 0; i < mapvote_count; ++i)
+                       if(mapvote_maps_availability[i] == GTV_AVAILABLE )
+                       {
+                               didntvote -= mapvote_selections[i];
+                               if(i != mappos)
+                               {
+                                       result = strcat(result, ":", mapvote_maps[i]);
+                                       result = strcat(result, ":", ftos(mapvote_selections[i]));
+                               }
+                       }
+               result = strcat(result, ":didn't vote:", ftos(didntvote));
+
+               GameLogEcho(result);
+               if(mapvote_maps_suggested[mappos])
+                       GameLogEcho(strcat(":vote:suggestion_accepted:", mapvote_maps[mappos]));
+       }
+
+       FOR_EACH_REALCLIENT(other)
+               FixClientCvars(other);
+
+       if(gametypevote)
+       {
+               if ( GameTypeVote_Finished(mappos) )
+               {
+                       gametypevote = FALSE;
+                       if(autocvar_nextmap != "")
+                       {
+                               Map_Goto_SetStr(autocvar_nextmap);
+                               Map_Goto(0);
+                               alreadychangedlevel = TRUE;
+                               return TRUE;
+                       }
+                       else
+                               MapVote_Init();
+               }
+               return FALSE;
+       }
+       
+       Map_Goto_SetStr(mapvote_maps[mappos]);
+       Map_Goto(0);
+       alreadychangedlevel = TRUE;
+       
+       return TRUE;
+}
+
+void MapVote_CheckRules_1()
+{
+       float i;
+
+       for(i = 0; i < mapvote_count; ++i) 
+               if( mapvote_maps_availability[i] == GTV_AVAILABLE )
+               {
+                       //dprint("Map ", ftos(i), ": "); dprint(mapvote_maps[i], "\n");
+                       mapvote_selections[i] = 0;
+               }
+
+       mapvote_voters = 0;
+       FOR_EACH_REALCLIENT(other)
+       {
+               ++mapvote_voters;
+               if(other.mapvote)
+               {
+                       i = other.mapvote - 1;
+                       //dprint("Player ", other.netname, " vote = ", ftos(other.mapvote - 1), "\n");
+                       mapvote_selections[i] = mapvote_selections[i] + 1;
+               }
+       }
+}
+
+float MapVote_CheckRules_2()
+{
+       float i;
+       float firstPlace, secondPlace, currentPlace;
+       float firstPlaceVotes, secondPlaceVotes, currentVotes;
+       float mapvote_voters_real;
+       string result;
+
+       if(mapvote_count_real == 1)
+               return MapVote_Finished(0);
+
+       mapvote_voters_real = mapvote_voters;
+       if(mapvote_abstain)
+               mapvote_voters_real -= mapvote_selections[mapvote_count - 1];
+
+       RandomSelection_Init();
+       currentPlace = 0;
+       currentVotes = -1;
+       for(i = 0; i < mapvote_count_real; ++i) 
+               if ( mapvote_maps_availability[i] == GTV_AVAILABLE )
+               {
+                       RandomSelection_Add(world, i, string_null, 1, mapvote_selections[i]);
+                       if ( gametypevote &&  mapvote_maps[i] == MapInfo_Type_ToString(MapInfo_CurrentGametype()) )
+                       {
+                               currentVotes = mapvote_selections[i];
+                               currentPlace = i;
+                       }
+               }
+       firstPlaceVotes = RandomSelection_best_priority;
+       if ( autocvar_sv_vote_gametype_default_current && currentVotes == firstPlaceVotes )
+               firstPlace = currentPlace;
+       else
+               firstPlace = RandomSelection_chosen_float;
+       
+       //dprint("First place: ", ftos(firstPlace), "\n");
+       //dprint("First place votes: ", ftos(firstPlaceVotes), "\n");
+
+       RandomSelection_Init();
+       for(i = 0; i < mapvote_count_real; ++i)
+               if(i != firstPlace)
+               if ( mapvote_maps_availability[i] == GTV_AVAILABLE )
+                       RandomSelection_Add(world, i, string_null, 1, mapvote_selections[i]);
+       secondPlace = RandomSelection_chosen_float;
+       secondPlaceVotes = RandomSelection_best_priority;
+       //dprint("Second place: ", ftos(secondPlace), "\n");
+       //dprint("Second place votes: ", ftos(secondPlaceVotes), "\n");
+
+       if(firstPlace == -1)
+               error("No first place in map vote... WTF?");
+
+       if(secondPlace == -1 || time > mapvote_timeout || (mapvote_voters_real - firstPlaceVotes) < firstPlaceVotes)
+               return MapVote_Finished(firstPlace);
+
+       if(mapvote_keeptwotime)
+               if(time > mapvote_keeptwotime || (mapvote_voters_real - firstPlaceVotes - secondPlaceVotes) < secondPlaceVotes)
+               {
+                       float didntvote;
+                       MapVote_TouchMask();
+                       mapvote_message = "Now decide between the TOP TWO!";
+                       mapvote_keeptwotime = 0;
+                       result = strcat(":vote:keeptwo:", mapvote_maps[firstPlace]);
+                       result = strcat(result, ":", ftos(firstPlaceVotes));
+                       result = strcat(result, ":", mapvote_maps[secondPlace]);
+                       result = strcat(result, ":", ftos(secondPlaceVotes), "::");
+                       didntvote = mapvote_voters;
+                       for(i = 0; i < mapvote_count; ++i)
+                       {
+                               didntvote -= mapvote_selections[i];
+                               if(i != firstPlace)
+                                       if(i != secondPlace)
+                                       {
+                                               result = strcat(result, ":", mapvote_maps[i]);
+                                               result = strcat(result, ":", ftos(mapvote_selections[i]));
+                                               if(i < mapvote_count_real)
+                                               {
+                                                       mapvote_maps_availability[i] = GTV_FORBIDDEN;
+                                               }
+                                       }
+                       }
+                       result = strcat(result, ":didn't vote:", ftos(didntvote));
+                       if(autocvar_sv_eventlog)
+                               GameLogEcho(result);
+               }
+
+       return FALSE;
+}
+
+void MapVote_Tick()
+{
+       float keeptwo;
+       float totalvotes;
+
+       keeptwo = mapvote_keeptwotime;
+       MapVote_CheckRules_1(); // count
+       if(MapVote_CheckRules_2()) // decide
+               return;
+
+       totalvotes = 0;
+       FOR_EACH_REALCLIENT(other)
+       {
+               // hide scoreboard again
+               if(other.health != 2342)
+               {
+                       other.health = 2342;
+                       other.impulse = 0;
+                       if(IS_REAL_CLIENT(other))
+                       {
+                               msg_entity = other;
+                               WriteByte(MSG_ONE, SVC_FINALE);
+                               WriteString(MSG_ONE, "");
+                       }
+               }
+
+               // clear possibly invalid votes
+               if ( mapvote_maps_availability[other.mapvote-1] != GTV_AVAILABLE )
+                       other.mapvote = 0;
+               // use impulses as new vote
+               if(other.impulse >= 1 && other.impulse <= mapvote_count)
+                       if( mapvote_maps_availability[other.impulse - 1] == GTV_AVAILABLE )
+                       {
+                               other.mapvote = other.impulse;
+                               MapVote_TouchVotes(other);
+                       }
+               other.impulse = 0;
+
+               if(other.mapvote)
+                       ++totalvotes;
+       }
+
+       MapVote_CheckRules_1(); // just count
+}
+
+void MapVote_Start()
+{
+       if(mapvote_run)
+               return;
+
+       // wait for stats to be sent first
+       if(!playerstats_waitforme)
+               return;
+
+       MapInfo_Enumerate();
+       if(MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 1))
+               mapvote_run = TRUE;
+}
+
+void MapVote_Think()
+{
+       if(!mapvote_run)
+               return;
+
+       if(alreadychangedlevel)
+               return;
+
+       if(time < mapvote_nextthink)
+               return;
+       //dprint("tick\n");
+
+       mapvote_nextthink = time + 0.5;
+
+       if(!mapvote_initialized)
+       {
+               if(autocvar_rescan_pending == 1)
+               {
+                       cvar_set("rescan_pending", "2");
+                       localcmd("fs_rescan\nrescan_pending 3\n");
+                       return;
+               }
+               else if(autocvar_rescan_pending == 2)
+               {
+                       return;
+               }
+               else if(autocvar_rescan_pending == 3)
+               {
+                       // now build missing mapinfo files
+                       if(!MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 1))
+                               return;
+
+                       // we're done, start the timer
+                       cvar_set("rescan_pending", "0");
+               }
+
+               mapvote_initialized = TRUE;
+               if(DoNextMapOverride(0))
+                       return;
+               if(!autocvar_g_maplist_votable || player_count <= 0)
+               {
+                       GotoNextMap(0);
+                       return;
+               }
+               
+               if(autocvar_sv_vote_gametype) { GameTypeVote_Start(); }
+               else if(autocvar_nextmap == "") { MapVote_Init(); }
+       }
+
+       MapVote_Tick();
+}
+
+float GameTypeVote_SetGametype(float type)
+{
+       if (MapInfo_CurrentGametype() == type)
+               return TRUE;
+               
+       float tsave = MapInfo_CurrentGametype();
+
+       MapInfo_SwitchGameType(type);
+
+       MapInfo_Enumerate();
+       MapInfo_FilterGametype(type, MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
+       if(MapInfo_count > 0)
+       {
+               // update lsmaps in case the gametype changed, this way people can easily list maps for it
+               if(lsmaps_reply != "") { strunzone(lsmaps_reply); }
+               lsmaps_reply = strzone(getlsmaps());
+               bprint("Game type successfully switched to ", MapInfo_Type_ToString(type), "\n");
+       }
+       else
+       {
+               bprint("Cannot use this game type: no map for it found\n");
+               MapInfo_SwitchGameType(tsave);
+               MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
+               return FALSE;
+       }
+
+       //localcmd("gametype ", MapInfo_Type_ToString(type), "\n");
+
+       cvar_set("g_maplist", MapInfo_ListAllowedMaps(type, MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags()) );
+       if(autocvar_g_maplist_shuffle)
+               ShuffleMaplist();
+
+       return TRUE;
+}
+
+float gametypevote_finished;
+float GameTypeVote_Finished(float pos)
+{
+       if(!gametypevote || gametypevote_finished)
+               return FALSE;
+       
+       if ( !GameTypeVote_SetGametype(MapInfo_Type_FromString(mapvote_maps[pos])) )
+       {
+               dprint("Selected gametype is not supported by any map");
+       }
+       
+       localcmd("sv_vote_gametype_hook_all\n");
+       localcmd("sv_vote_gametype_hook_", mapvote_maps[pos], "\n");
+       
+       gametypevote_finished = TRUE;
+       
+       return TRUE;
+}
+
+float GameTypeVote_AddVotable(string nextMode)
+{
+       float j;
+       if ( nextMode == "" || MapInfo_Type_FromString(nextMode) == 0 )
+               return FALSE;
+       for(j = 0; j < mapvote_count; ++j)
+               if(mapvote_maps[j] == nextMode)
+                       return FALSE;
+       
+       mapvote_maps[mapvote_count] = strzone(nextMode);
+       mapvote_maps_suggested[mapvote_count] = FALSE;
+
+       mapvote_maps_screenshot_dir[mapvote_count] = 0;
+       mapvote_maps_pakfile[mapvote_count] = strzone("");
+       mapvote_maps_availability[mapvote_count] = GameTypeVote_AvailabilityStatus(nextMode);
+
+       mapvote_count += 1;
+       
+       return TRUE;
+       
+}
+
+float GameTypeVote_Start()
+{
+       float j;
+       MapVote_ClearAllVotes();
+       MapVote_UnzoneStrings();
+       
+       mapvote_count = 0;
+       mapvote_timeout = time + autocvar_sv_vote_gametype_timeout;
+       mapvote_abstain = 0;
+       mapvote_detail = !autocvar_g_maplist_votable_nodetail;
+       
+       float n = tokenizebyseparator(autocvar_sv_vote_gametype_options, " ");
+       n = min(MAPVOTE_COUNT, n);
+       
+       float really_available, which_available;
+       really_available = 0;
+       which_available = -1;
+       for(j = 0; j < n; ++j)
+       {
+               if ( GameTypeVote_AddVotable(argv(j)) )
+               if ( mapvote_maps_availability[j] == GTV_AVAILABLE )
+               {
+                       really_available++;
+                       which_available = j;
+               }
+       }
+
+       mapvote_count_real = mapvote_count;
+       
+       gametypevote = 1;
+       
+       if ( really_available == 0 )
+       {
+               if ( mapvote_count > 0 )
+                       strunzone(mapvote_maps[0]);
+               mapvote_maps[0] = strzone(MapInfo_Type_ToString(MapInfo_CurrentGametype()));
+               //GameTypeVote_Finished(0);
+               MapVote_Finished(0);
+               return FALSE;
+       }
+       if ( really_available == 1 )
+       {
+               //GameTypeVote_Finished(which_available);
+               MapVote_Finished(which_available);
+               return FALSE;
+       }
+       
+       mapvote_count_real = mapvote_count;
+
+       mapvote_keeptwotime = time + autocvar_sv_vote_gametype_keeptwotime;
+       if(mapvote_count_real < 3 || mapvote_keeptwotime <= time)
+               mapvote_keeptwotime = 0;
+       
+       MapVote_Spawn();
+       
+       return TRUE;
+}
diff --git a/qcsrc/server/mapvoting.qh b/qcsrc/server/mapvoting.qh
new file mode 100644 (file)
index 0000000..6875958
--- /dev/null
@@ -0,0 +1,39 @@
+// definitions for functions used outside mapvoting.qc
+void MapVote_Start();
+void MapVote_Spawn();
+void MapVote_Think();
+float GameTypeVote_Start();
+float GameTypeVote_Finished(float pos);
+string GameTypeVote_MapInfo_FixName(string m);
+
+// definitions
+float gametypevote;
+string getmapname_stored;
+float mapvote_initialized;
+
+float mapvote_nextthink;
+float mapvote_initialized;
+float mapvote_keeptwotime;
+float mapvote_timeout;
+string mapvote_message;
+#define MAPVOTE_SCREENSHOT_DIRS_COUNT 4
+string mapvote_screenshot_dirs[MAPVOTE_SCREENSHOT_DIRS_COUNT];
+float mapvote_screenshot_dirs_count;
+
+float mapvote_count;
+float mapvote_count_real;
+string mapvote_maps[MAPVOTE_COUNT];
+float mapvote_maps_screenshot_dir[MAPVOTE_COUNT];
+string mapvote_maps_pakfile[MAPVOTE_COUNT];
+float mapvote_maps_suggested[MAPVOTE_COUNT];
+string mapvote_suggestions[MAPVOTE_COUNT];
+float mapvote_suggestion_ptr;
+float mapvote_voters;
+float mapvote_selections[MAPVOTE_COUNT];
+float mapvote_maps_availability[MAPVOTE_COUNT];
+float mapvote_run;
+float mapvote_detail;
+float mapvote_abstain;
+.float mapvote;
+
+entity mapvote_ent;
index 3cb28bc8469a7f82dda9ef2fe89e155a649922c8..5defa9b1e73bea7503cbed2351f2059e0ad242d4 100644 (file)
@@ -930,6 +930,7 @@ void readlevelcvars(void)
        CHECK_MUTATOR_ADD("g_nades", mutator_nades, 1);
        CHECK_MUTATOR_ADD("g_sandbox", sandbox, 1);
        CHECK_MUTATOR_ADD("g_campcheck", mutator_campcheck, 1);
+       CHECK_MUTATOR_ADD("g_buffs", mutator_buffs, 1);
 
        #undef CHECK_MUTATOR_ADD
 
index 78a08c551e540dc77a3dd5c2e8b4d56bebdb3ddd..f4021eb4c0a45b33a2f2741363ea50d36cc68848 100644 (file)
@@ -77,6 +77,7 @@ MUTATOR_HOOKABLE(PlayerJump);
        // called when a player presses the jump key
        // INPUT, OUTPUT:
                float player_multijump;
+               float player_jumpheight;
 
 MUTATOR_HOOKABLE(GiveFragsForKill);
        // called when someone was fragged by "self", and is expected to change frag_score to adjust scoring for the kill
@@ -102,6 +103,11 @@ MUTATOR_HOOKABLE(SpectateCopy);
 MUTATOR_HOOKABLE(ForbidThrowCurrentWeapon);
        // returns 1 if throwing the current weapon shall not be allowed
 
+MUTATOR_HOOKABLE(WeaponRateFactor);
+       // allows changing attack rate
+       // INPUT, OUTPUT:
+               float weapon_rate;
+
 MUTATOR_HOOKABLE(SetStartItems);
        // adjusts {warmup_}start_{items,weapons,ammo_{cells,rockets,nails,shells,fuel}}
 
@@ -222,6 +228,11 @@ MUTATOR_HOOKABLE(PlayerPowerups);
 MUTATOR_HOOKABLE(PlayerRegen);
        // called every player think frame
        // return 1 to disable regen
+       // INPUT, OUTPUT:
+               float regen_mod_max;
+               float regen_mod_regen;
+               float regen_mod_rot;
+               float regen_mod_limit;
 
 MUTATOR_HOOKABLE(PlayerUseKey);
        // called when the use key is pressed
diff --git a/qcsrc/server/mutators/mutator_buffs.qc b/qcsrc/server/mutators/mutator_buffs.qc
new file mode 100644 (file)
index 0000000..c71fe60
--- /dev/null
@@ -0,0 +1,787 @@
+float buffs_BuffModel_Customize()
+{
+       entity player, myowner;
+       float same_team;
+
+       player = WaypointSprite_getviewentity(other);
+       myowner = self.owner;
+       same_team = (SAME_TEAM(player, myowner) || SAME_TEAM(player, myowner));
+
+       if(myowner.alpha <= 0.5 && !same_team && myowner.alpha != 0)
+               return FALSE;
+
+       if(player == myowner || (IS_SPEC(other) && other.enemy == myowner))
+       {
+               // somewhat hide the model, but keep the glow
+               self.effects = 0;
+               self.alpha = -1;
+       }
+       else
+       {
+               self.effects = EF_FULLBRIGHT | EF_LOWPRECISION;
+               self.alpha = 1;
+       }
+       return TRUE;
+}
+
+// buff item
+float buff_Waypoint_visible_for_player(entity plr)
+{
+    if(!self.owner.buff_active && !self.owner.buff_activetime)
+        return FALSE;
+
+       if(plr.buffs)
+       {
+               if(plr.cvar_cl_buffs_autoreplace)
+               {
+                       if(plr.buffs == self.owner.buffs)
+                               return FALSE;
+               }
+               else
+                       return FALSE;
+       }
+
+    return WaypointSprite_visible_for_player(plr);
+}
+
+void buff_Waypoint_Spawn(entity e)
+{
+    WaypointSprite_Spawn(Buff_Sprite(e.buffs), 0, autocvar_g_buffs_waypoint_distance, e, '0 0 1' * e.maxs_z, world, e.team, e, buff_waypoint, TRUE, RADARICON_POWERUP, e.glowmod);
+    WaypointSprite_UpdateTeamRadar(e.buff_waypoint, RADARICON_POWERUP, e.glowmod);
+    e.buff_waypoint.waypointsprite_visible_for_player = buff_Waypoint_visible_for_player;
+}
+
+void buff_SetCooldown(float cd)
+{
+       cd = max(0, cd);
+
+       if(!self.buff_waypoint)
+               buff_Waypoint_Spawn(self);
+
+       WaypointSprite_UpdateBuildFinished(self.buff_waypoint, time + cd);
+       self.buff_activetime = cd;
+       self.buff_active = !cd;
+}
+
+void buff_Respawn(entity ent)
+{
+       if(gameover) { return; }
+       
+       vector oldbufforigin = ent.origin;
+       
+       if(!MoveToRandomMapLocation(ent, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, ((autocvar_g_buffs_random_location_attempts > 0) ? autocvar_g_buffs_random_location_attempts : 10), 1024, 256))
+       {
+               entity spot = SelectSpawnPoint(TRUE);
+               setorigin(ent, ((spot.origin + '0 0 200') + (randomvec() * 300)));
+               ent.angles = spot.angles;
+       }
+       
+       tracebox(ent.origin, ent.mins * 1.5, self.maxs * 1.5, ent.origin, MOVE_NOMONSTERS, ent);
+       
+       setorigin(ent, trace_endpos); // attempt to unstick
+       
+       ent.movetype = MOVETYPE_TOSS;
+       
+       makevectors(ent.angles);
+       ent.velocity = '0 0 200';
+       ent.angles = '0 0 0';
+       if(autocvar_g_buffs_random_lifetime > 0)
+               ent.lifetime = time + autocvar_g_buffs_random_lifetime;
+
+       pointparticles(particleeffectnum("electro_combo"), oldbufforigin + ((ent.mins + ent.maxs) * 0.5), '0 0 0', 1);
+       pointparticles(particleeffectnum("electro_combo"), CENTER_OR_VIEWOFS(ent), '0 0 0', 1);
+       
+       WaypointSprite_Ping(ent.buff_waypoint);
+       
+       sound(ent, CH_TRIGGER, "keepaway/respawn.wav", VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere)
+}
+
+void buff_Touch()
+{
+       if(gameover) { return; }
+
+       if(ITEM_TOUCH_NEEDKILL())
+       {
+               buff_Respawn(self);
+               return;
+       }
+
+       if((self.team && DIFF_TEAM(other, self))
+       || (other.freezetag_frozen)
+       || (other.vehicle)
+       || (!IS_PLAYER(other))
+       || (!self.buff_active)
+       )
+       {
+               // can't touch this
+               return;
+       }
+
+       if(other.buffs)
+       {
+               if(other.cvar_cl_buffs_autoreplace && other.buffs != self.buffs)
+               {
+                       //Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_BUFF_DROP, other.buffs);
+                       Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ITEM_BUFF_LOST, other.netname, other.buffs);
+
+                       other.buffs = 0;
+                       //sound(other, CH_TRIGGER, "relics/relic_effect.wav", VOL_BASE, ATTN_NORM);
+               }
+               else { return; } // do nothing
+       }
+               
+       self.owner = other;
+       self.buff_active = FALSE;
+       self.lifetime = 0;
+       
+       Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_BUFF_GOT, self.buffs);
+       Send_Notification(NOTIF_ALL_EXCEPT, other, MSG_INFO, INFO_ITEM_BUFF, other.netname, self.buffs);
+
+       pointparticles(particleeffectnum("item_pickup"), CENTER_OR_VIEWOFS(self), '0 0 0', 1);
+       sound(other, CH_TRIGGER, "misc/shield_respawn.wav", VOL_BASE, ATTN_NORM);
+       other.buffs |= (self.buffs);
+}
+
+float buff_Available(float buffid)
+{
+       if(buffid == BUFF_AMMO && ((start_items & IT_UNLIMITED_WEAPON_AMMO) || (start_items & IT_UNLIMITED_AMMO) || (cvar("g_melee_only"))))
+               return FALSE;
+
+       if(buffid == BUFF_VAMPIRE && cvar("g_vampire"))
+               return FALSE;
+
+       if(!cvar(strcat("g_buffs_", Buff_Name(buffid))))
+               return FALSE;
+
+       return TRUE;
+}
+
+void buff_NewType(entity ent, float cb)
+{
+       entity e;
+       RandomSelection_Init();
+       for(e = Buff_Type_first; e; e = e.enemy)
+       if(buff_Available(e.items))
+       {
+               RandomSelection_Add(world, e.items, string_null, 1, 1 / e.count); // if it's already been chosen, give it a lower priority
+               e.count += 1;
+       }
+       ent.buffs = RandomSelection_chosen_float;
+}
+
+void buff_Think()
+{
+       if(self.buffs != self.oldbuffs)
+       {
+               self.color = Buff_Color(self.buffs);
+               self.glowmod = ((self.team) ? Team_ColorRGB(self.team) + '0.1 0.1 0.1' : self.color);
+               self.skin = Buff_Skin(self.buffs);
+               
+               setmodel(self, "models/relics/relic.md3");
+
+               if(self.buff_waypoint)
+               {
+                       //WaypointSprite_Disown(self.buff_waypoint, 1);
+                       WaypointSprite_Kill(self.buff_waypoint);
+                       buff_Waypoint_Spawn(self);
+                       if(self.buff_activetime)
+                               WaypointSprite_UpdateBuildFinished(self.buff_waypoint, time + self.buff_activetime - frametime);
+               }
+
+               self.oldbuffs = self.buffs;
+       }
+
+       if(!gameover)
+       if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime)
+       if(!self.buff_activetime_updated)
+       {
+               buff_SetCooldown(self.buff_activetime);
+               self.buff_activetime_updated = TRUE;
+    }
+
+       if(!self.buff_active && !self.buff_activetime)
+       if(!self.owner || self.owner.freezetag_frozen || self.owner.deadflag != DEAD_NO || !self.owner.iscreature || !(self.owner.buffs & self.buffs))
+       {
+               buff_SetCooldown(autocvar_g_buffs_cooldown_respawn + frametime);
+               self.owner = world;
+               if(autocvar_g_buffs_randomize)
+                       buff_NewType(self, self.buffs);
+                       
+               if(autocvar_g_buffs_random_location || (self.spawnflags & 1))
+                       buff_Respawn(self);
+       }
+       
+       if(self.buff_activetime)
+       if(!gameover)
+       if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime)
+       {
+               self.buff_activetime = max(0, self.buff_activetime - frametime);
+
+               if(!self.buff_activetime)
+               {
+                       self.buff_active = TRUE;
+                       sound(self, CH_TRIGGER, "misc/strength_respawn.wav", VOL_BASE, ATTN_NORM);
+                       pointparticles(particleeffectnum("item_respawn"), CENTER_OR_VIEWOFS(self), '0 0 0', 1);
+               }
+       }
+
+       if(!self.buff_active)
+       {
+               self.alpha = 0.3;
+               self.effects &= ~(EF_FULLBRIGHT);
+               self.pflags = 0;
+       }
+       else
+       {
+               self.alpha = 1;
+               self.effects |= EF_FULLBRIGHT;
+               self.light_lev = 220 + 36 * sin(time);
+               self.pflags = PFLAGS_FULLDYNAMIC;
+
+               if(self.team && !self.buff_waypoint)
+                       buff_Waypoint_Spawn(self);
+                       
+               if(self.lifetime)
+               if(time >= self.lifetime)
+                       buff_Respawn(self);
+       }
+    
+       self.nextthink = time;
+       //self.angles_y = time * 110.1;
+}
+
+void buff_Waypoint_Reset()
+{
+       WaypointSprite_Kill(self.buff_waypoint);
+
+       if(self.buff_activetime) { buff_Waypoint_Spawn(self); }
+}
+
+void buff_Reset()
+{
+       if(autocvar_g_buffs_randomize)
+               buff_NewType(self, self.buffs);
+       self.owner = world;
+       buff_SetCooldown(autocvar_g_buffs_cooldown_activate);
+       buff_Waypoint_Reset();
+       self.buff_activetime_updated = FALSE;
+       
+       if(autocvar_g_buffs_random_location || (self.spawnflags & 1))
+               buff_Respawn(self);
+}
+
+void buff_Init(entity ent)
+{
+       if(!cvar("g_buffs")) { remove(self); return; }
+       
+       if(!teamplay && self.team) { self.team = 0; }
+
+       entity oldself = self;
+       self = ent;
+       if(!self.buffs || buff_Available(self.buffs))
+               buff_NewType(self, 0);
+       
+       self.classname = "item_buff";
+       self.solid = SOLID_TRIGGER;
+       self.flags = FL_ITEM;
+       self.think = buff_Think;
+       self.touch = buff_Touch;
+       self.reset = buff_Reset;
+       self.nextthink = time + 0.1;
+       self.gravity = 1;
+       self.movetype = MOVETYPE_TOSS;
+       self.scale = 1;
+       self.skin = Buff_Skin(self.buffs);
+       self.effects = EF_FULLBRIGHT | EF_STARDUST | EF_NOSHADOW;
+       self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY;
+       //self.gravity = 100;
+       self.color = Buff_Color(self.buffs);
+       self.glowmod = ((self.team) ? Team_ColorRGB(self.team) + '0.1 0.1 0.1' : self.color);
+       buff_SetCooldown(autocvar_g_buffs_cooldown_activate + game_starttime);
+       self.buff_active = !self.buff_activetime;
+       self.pflags = PFLAGS_FULLDYNAMIC;
+       
+       if(self.noalign)
+               self.movetype = MOVETYPE_NONE; // reset by random location
+
+       setmodel(self, "models/relics/relic.md3");
+       setsize(self, BUFF_MIN, BUFF_MAX);
+       
+       if(cvar("g_buffs_random_location") || (self.spawnflags & 1))
+               buff_Respawn(self);
+       
+       self = oldself;
+}
+
+void buff_Init_Compat(entity ent, float replacement)
+{
+       if(ent.spawnflags & 2)
+               ent.team = NUM_TEAM_1;
+       else if(ent.spawnflags & 4)
+               ent.team = NUM_TEAM_2;
+
+       ent.buffs = replacement;
+
+       buff_Init(ent);
+}
+
+void buff_SpawnReplacement(entity ent, entity old)
+{
+       setorigin(ent, old.origin);
+       ent.angles = old.angles;
+       ent.noalign = old.noalign;
+       
+       buff_Init(ent);
+}
+
+// mutator hooks
+MUTATOR_HOOKFUNCTION(buffs_PlayerDamage_SplitHealthArmor)
+{
+       if(frag_deathtype == DEATH_BUFF_VENGEANCE) { return FALSE; } // oh no you don't
+
+       if(frag_target.buffs & BUFF_RESISTANCE)
+       {
+               vector v = healtharmor_applydamage(50, autocvar_g_buffs_resistance_blockpercent, frag_deathtype, frag_damage);
+               damage_take = v_x;
+               damage_save = v_y;
+       }
+
+       return FALSE;
+}
+
+void buff_Vengeance_DelayedDamage()
+{
+       if(self.enemy)
+               Damage(self.enemy, self.owner, self.owner, self.dmg, DEATH_BUFF_VENGEANCE, self.enemy.origin, '0 0 0');
+       
+       remove(self);
+       return;
+}
+
+MUTATOR_HOOKFUNCTION(buffs_PlayerDamage_Calculate)
+{
+       if(frag_deathtype == DEATH_BUFF_VENGEANCE) { return FALSE; } // oh no you don't
+
+       if(frag_target.buffs & BUFF_SPEED)
+       if(frag_target != frag_attacker)
+               frag_damage *= autocvar_g_buffs_speed_damage_take;
+
+       if(frag_target.buffs & BUFF_MEDIC)
+       if((frag_target.health - frag_damage) <= 0)
+       if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype))
+       if(frag_attacker)
+       if(random() <= autocvar_g_buffs_medic_survive_chance)
+       if(frag_target.health - autocvar_g_buffs_medic_survive_health > 0) // not if the final result would be less than 0, medic must get health
+               frag_damage = frag_target.health - autocvar_g_buffs_medic_survive_health;
+               
+       if(frag_target.buffs & BUFF_VENGEANCE)
+       if(frag_attacker)
+       if(frag_attacker != frag_target)
+       if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype))
+       {
+               entity dmgent = spawn();
+
+               dmgent.dmg = frag_damage * autocvar_g_buffs_vengeance_damage_multiplier;
+               dmgent.enemy = frag_attacker;
+               dmgent.owner = frag_target;
+               dmgent.think = buff_Vengeance_DelayedDamage;
+               dmgent.nextthink = time + 0.1;
+       }
+
+       if(frag_target.buffs & BUFF_BASH)
+       if(frag_attacker != frag_target)
+       if(vlen(frag_force))
+               frag_force = '0 0 0';
+       
+       if(frag_attacker.buffs & BUFF_BASH)
+       if(vlen(frag_force))
+       if(frag_attacker == frag_target)
+               frag_force *= autocvar_g_buffs_bash_force_self;
+       else
+               frag_force *= autocvar_g_buffs_bash_force;
+       
+       if(frag_attacker.buffs & BUFF_DISABILITY)
+       if(frag_target != frag_attacker)
+               frag_target.buff_disability_time = time + autocvar_g_buffs_disability_time;
+
+       if(frag_attacker.buffs & BUFF_MEDIC)
+       if(SAME_TEAM(frag_attacker, frag_target))
+       if(frag_attacker != frag_target)
+       {
+               frag_target.health = min(g_pickup_healthmega_max, frag_target.health + frag_damage);
+               frag_damage = 0;
+       }
+
+       // this... is ridiculous (TODO: fix!)
+       if(frag_attacker.buffs & BUFF_VAMPIRE)
+       if(!frag_target.vehicle)
+       if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype))
+       if(frag_target.deadflag == DEAD_NO)
+       if(IS_PLAYER(frag_target) || (frag_target.flags & FL_MONSTER))
+       if(frag_attacker != frag_target)
+       if(!frag_target.freezetag_frozen)
+       if(frag_target.takedamage)
+       if(DIFF_TEAM(frag_attacker, frag_target))
+               frag_attacker.health = bound(0, frag_attacker.health + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.health), g_pickup_healthsmall_max);
+
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(buffs_PlayerSpawn)
+{
+       self.buffs = 0;
+       // reset timers here to prevent them continuing after re-spawn
+       self.buff_disability_time = 0;
+       self.buff_disability_effect_time = 0;
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(buffs_PlayerPhysics)
+{
+       if(self.buffs & BUFF_SPEED)
+       {
+               self.stat_sv_maxspeed *= autocvar_g_buffs_speed_speed;
+               self.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_speed_speed;
+       }
+       
+       if(time < self.buff_disability_time)
+       {
+               self.stat_sv_maxspeed *= autocvar_g_buffs_disability_speed;
+               self.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_disability_speed;
+       }
+
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(buffs_PlayerJump)
+{
+       if(self.buffs & BUFF_JUMP)
+               player_jumpheight = autocvar_g_buffs_jump_height;
+       self.stat_jumpheight = player_jumpheight;
+
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(buffs_MonsterMove)
+{
+       if(time < self.buff_disability_time)
+       {
+               monster_speed_walk *= autocvar_g_buffs_disability_speed;
+               monster_speed_run *= autocvar_g_buffs_disability_speed;
+       }
+
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(buffs_PlayerDies)
+{
+       if(self.buffs)
+       {
+               Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, self.buffs);
+               self.buffs = 0;
+               
+               if(self.buff_model)
+               {
+                       remove(self.buff_model);
+                       self.buff_model = world;
+               }
+       }
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(buffs_PlayerUseKey)
+{
+       if(MUTATOR_RETURNVALUE || gameover) { return FALSE; }
+       if(self.buffs)
+       {
+               Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_BUFF_DROP, self.buffs);
+               Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, self.buffs);
+
+               self.buffs = 0;
+               sound(self, CH_TRIGGER, "relics/relic_effect.wav", VOL_BASE, ATTN_NORM);
+               return TRUE;
+       }
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(buffs_RemovePlayer)
+{
+       if(self.buff_model)
+       {
+               remove(self.buff_model);
+               self.buff_model = world;
+       }
+       
+       // also reset timers here to prevent them continuing after spectating
+       self.buff_disability_time = 0;
+       self.buff_disability_effect_time = 0;
+
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(buffs_CustomizeWaypoint)
+{
+       entity e = WaypointSprite_getviewentity(other);
+
+       // if you have the invisibility powerup, sprites ALWAYS are restricted to your team
+       // but only apply this to real players, not to spectators
+       if((self.owner.flags & FL_CLIENT) && (self.owner.buffs & BUFF_INVISIBLE) && (e == other))
+       if(DIFF_TEAM(self.owner, e))
+               return TRUE;
+
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(buffs_OnEntityPreSpawn)
+{
+       if(autocvar_g_buffs_replace_powerups)
+       switch(self.classname)
+       {
+               case "item_strength":
+               case "item_invincible":
+               {
+                       entity e = spawn();
+                       buff_SpawnReplacement(e, self);
+                       return TRUE;
+               }
+       }
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(buffs_WeaponRate)
+{
+       if(self.buffs & BUFF_SPEED)
+               weapon_rate *= autocvar_g_buffs_speed_rate;
+               
+       if(time < self.buff_disability_time)
+               weapon_rate *= autocvar_g_buffs_disability_rate;
+       
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(buffs_PlayerThink)
+{
+       if(gameover || self.deadflag != DEAD_NO) { return FALSE; }
+       
+       if(time < self.buff_disability_time)
+       if(time >= self.buff_disability_effect_time)
+       {
+               pointparticles(particleeffectnum("smoking"), self.origin + ((self.mins + self.maxs) * 0.5), '0 0 0', 1);
+               self.buff_disability_effect_time = time + 0.5;
+       }
+       
+       if(self.freezetag_frozen)
+       {
+               if(self.buffs)
+               {
+                       Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, self.buffs);
+                       self.buffs = 0;
+               }
+       }
+               
+       if((self.buffs & BUFF_INVISIBLE) && (self.oldbuffs & BUFF_INVISIBLE))
+       if(self.alpha != autocvar_g_buffs_invisible_alpha)
+               self.alpha = autocvar_g_buffs_invisible_alpha;
+
+       if(self.buffs != self.oldbuffs)
+       {
+               if(self.oldbuffs & BUFF_AMMO)
+               {
+                       if(self.buff_ammo_prev_infitems)
+                               self.items |= IT_UNLIMITED_WEAPON_AMMO;
+                       else
+                               self.items &= ~IT_UNLIMITED_WEAPON_AMMO;
+               }
+               else if(self.buffs & BUFF_AMMO)
+               {
+                       self.buff_ammo_prev_infitems = (self.items & IT_UNLIMITED_WEAPON_AMMO);
+                       self.items |= IT_UNLIMITED_WEAPON_AMMO;
+                       if(!self.ammo_shells) { self.ammo_shells = 20; }
+                       if(!self.ammo_cells) { self.ammo_cells = 20; }
+                       if(!self.ammo_rockets) { self.ammo_rockets = 20; }
+                       if(!self.ammo_nails) { self.ammo_nails = 20; }
+                       if(!self.ammo_fuel) { self.ammo_fuel = 20; }
+               }
+               
+               if(self.oldbuffs & BUFF_INVISIBLE)
+               {
+                       if(time < self.strength_finished && g_minstagib)
+                               self.alpha = autocvar_g_minstagib_invis_alpha;
+                       else
+                               self.alpha = self.buff_invisible_prev_alpha;
+               }
+               else if(self.buffs & BUFF_INVISIBLE)
+               {
+                       if(time < self.strength_finished && g_minstagib)
+                               self.buff_invisible_prev_alpha = default_player_alpha;
+                       else
+                               self.buff_invisible_prev_alpha = self.alpha;
+                       self.alpha = autocvar_g_buffs_invisible_alpha;
+               }
+               
+               if(self.oldbuffs & BUFF_FLIGHT)
+                       self.gravity = self.buff_flight_prev_gravity;
+               else if(self.buffs & BUFF_FLIGHT)
+               {
+                       self.buff_flight_prev_gravity = self.gravity;
+                       self.gravity = autocvar_g_buffs_flight_gravity;
+               }
+
+               self.oldbuffs = self.buffs;
+               if(self.buffs)
+               {
+                       if(!self.buff_model)
+                       {
+                               self.buff_model = spawn();
+                               setmodel(self.buff_model, "models/relics/relic.md3");
+                               setsize(self.buff_model, '0 0 -40', '0 0 40');
+                               setattachment(self.buff_model, self, "");
+                               setorigin(self.buff_model, '0 0 1' * (self.buff_model.maxs_z * 1));
+                               self.buff_model.owner = self;
+                               self.buff_model.scale = 0.7;
+                               self.buff_model.pflags = PFLAGS_FULLDYNAMIC;
+                               self.buff_model.light_lev = 200;
+                               self.buff_model.customizeentityforclient = buffs_BuffModel_Customize;
+                       }
+                       self.buff_model.color = Buff_Color(self.buffs);
+                       self.buff_model.glowmod = ((self.buff_model.team) ? Team_ColorRGB(self.buff_model.team) + '0.1 0.1 0.1' : self.buff_model.color);
+                       self.buff_model.skin = Buff_Skin(self.buffs);
+                       
+                       self.effects |= EF_NOSHADOW;
+               }
+               else
+               {
+                       remove(self.buff_model);
+                       self.buff_model = world;
+                       
+                       self.effects &= ~(EF_NOSHADOW);
+               }
+       }
+
+       if(self.buff_model)
+       {
+               self.buff_model.effects = self.effects;
+               self.buff_model.effects |= EF_LOWPRECISION;
+               self.buff_model.effects = self.buff_model.effects & EFMASK_CHEAP; // eat performance
+               
+               self.buff_model.alpha = self.alpha;
+       }
+
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(buffs_SpectateCopy)
+{
+       self.buffs = other.buffs;
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(buffs_VehicleEnter)
+{
+       vh_vehicle.buffs = vh_player.buffs;
+       vh_player.buffs = 0;
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(buffs_VehicleExit)
+{
+       vh_player.buffs = vh_vehicle.buffs;
+       vh_vehicle.buffs = 0;
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(buffs_PlayerRegen)
+{
+       if(self.buffs & BUFF_MEDIC)
+       {
+               regen_mod_rot = autocvar_g_buffs_medic_rot;
+               regen_mod_limit = regen_mod_max = autocvar_g_buffs_medic_max;
+               regen_mod_regen = autocvar_g_buffs_medic_regen;
+       }
+       
+       if(self.buffs & BUFF_SPEED)
+               regen_mod_regen = autocvar_g_buffs_speed_regen;
+
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(buffs_GetCvars)
+{
+       GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_buffs_autoreplace, "cl_buffs_autoreplace");
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(buffs_BuildMutatorsString)
+{
+       ret_string = strcat(ret_string, ":Buffs");
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(buffs_BuildMutatorsPrettyString)
+{
+       ret_string = strcat(ret_string, ", Buffs");
+       return FALSE;
+}
+
+void buffs_DelayedInit()
+{
+       if(autocvar_g_buffs_spawn_count > 0)
+       if(find(world, classname, "item_buff") == world)
+       {
+               float i;
+               for(i = 0; i < autocvar_g_buffs_spawn_count; ++i)
+               {
+                       entity e = spawn();
+                       e.spawnflags |= 1; // always randomize
+                       e.velocity = randomvec() * 250; // this gets reset anyway if random location works
+                       buff_Init(e);
+               }
+       }
+}
+
+void buffs_Initialize()
+{
+       precache_model("models/relics/relic.md3");
+       precache_sound("misc/strength_respawn.wav");
+       precache_sound("misc/shield_respawn.wav");
+       precache_sound("relics/relic_effect.wav");
+       precache_sound("weapons/rocket_impact.wav");
+       precache_sound("keepaway/respawn.wav");
+
+       addstat(STAT_BUFFS, AS_INT, buffs);
+       addstat(STAT_MOVEVARS_JUMPVELOCITY, AS_FLOAT, stat_jumpheight);
+       
+       InitializeEntity(world, buffs_DelayedInit, INITPRIO_FINDTARGET);
+}
+
+MUTATOR_DEFINITION(mutator_buffs)
+{
+       MUTATOR_HOOK(PlayerDamage_SplitHealthArmor, buffs_PlayerDamage_SplitHealthArmor, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerDamage_Calculate, buffs_PlayerDamage_Calculate, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerSpawn, buffs_PlayerSpawn, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerPhysics, buffs_PlayerPhysics, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerJump, buffs_PlayerJump, CBC_ORDER_ANY);
+       MUTATOR_HOOK(MonsterMove, buffs_MonsterMove, CBC_ORDER_ANY);
+       MUTATOR_HOOK(SpectateCopy, buffs_SpectateCopy, CBC_ORDER_ANY);
+       MUTATOR_HOOK(VehicleEnter, buffs_VehicleEnter, CBC_ORDER_ANY);
+       MUTATOR_HOOK(VehicleExit, buffs_VehicleExit, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerRegen, buffs_PlayerRegen, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerDies, buffs_PlayerDies, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerUseKey, buffs_PlayerUseKey, CBC_ORDER_ANY);
+       MUTATOR_HOOK(MakePlayerObserver, buffs_RemovePlayer, CBC_ORDER_ANY);
+       MUTATOR_HOOK(ClientDisconnect, buffs_RemovePlayer, CBC_ORDER_ANY);
+       MUTATOR_HOOK(OnEntityPreSpawn, buffs_OnEntityPreSpawn, CBC_ORDER_ANY);
+       MUTATOR_HOOK(CustomizeWaypoint, buffs_CustomizeWaypoint, CBC_ORDER_ANY);
+       MUTATOR_HOOK(WeaponRateFactor, buffs_WeaponRate, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerPreThink, buffs_PlayerThink, CBC_ORDER_ANY);
+       MUTATOR_HOOK(GetCvars, buffs_GetCvars, CBC_ORDER_ANY);
+       MUTATOR_HOOK(BuildMutatorsString, buffs_BuildMutatorsString, CBC_ORDER_ANY);
+       MUTATOR_HOOK(BuildMutatorsPrettyString, buffs_BuildMutatorsPrettyString, CBC_ORDER_ANY);
+
+       MUTATOR_ONADD
+       {
+               buffs_Initialize();
+       }
+
+       return FALSE;
+}
diff --git a/qcsrc/server/mutators/mutator_buffs.qh b/qcsrc/server/mutators/mutator_buffs.qh
new file mode 100644 (file)
index 0000000..66540b8
--- /dev/null
@@ -0,0 +1,28 @@
+// buff specific variables \\
+//
+// ammo
+.float buff_ammo_prev_infitems;
+// invisible
+.float buff_invisible_prev_alpha;
+// flight
+.float buff_flight_prev_gravity;
+// jump
+.float stat_jumpheight;
+const float STAT_MOVEVARS_JUMPVELOCITY = 250; // engine hack
+// disability
+.float buff_disability_time;
+.float buff_disability_effect_time;
+
+// buff definitions
+.float buff_active;
+.float buff_activetime;
+.float buff_activetime_updated;
+.entity buff_waypoint;
+.float oldbuffs; // for updating effects
+.entity buff_model; // controls effects (TODO: make csqc)
+
+#define BUFF_MIN ('-16 -16 -20')
+#define BUFF_MAX ('16 16 20')
+
+// client side options
+.float cvar_cl_buffs_autoreplace;
index ecab77c88025dff20bf63c4778f811dbd5261da4..ab519e98f9d8d711fe798624d57f8393997bc8b2 100644 (file)
@@ -29,5 +29,6 @@ MUTATOR_DECLARATION(mutator_multijump);
 MUTATOR_DECLARATION(mutator_melee_only);
 MUTATOR_DECLARATION(mutator_nades);
 MUTATOR_DECLARATION(mutator_campcheck);
+MUTATOR_DECLARATION(mutator_buffs);
 
 MUTATOR_DECLARATION(sandbox);
index 1ae22e2029f7ff518aff3b21533a54a07163673d..812a1d390a000439b14d08f16032bb2d7a303b9d 100644 (file)
@@ -13,8 +13,10 @@ sys-post.qh
 ../warpzonelib/server.qh
 
 ../common/constants.qh
+../common/stats.qh
 ../common/teams.qh
 ../common/util.qh
+../common/buffs.qh
 ../common/test.qh
 ../common/counting.qh
 ../common/items.qh
@@ -50,6 +52,7 @@ mutators/gamemode_lms.qh
 mutators/gamemode_invasion.qh
 mutators/mutator_dodging.qh
 mutators/mutator_nades.qh
+mutators/mutator_buffs.qh
 
 //// tZork Turrets ////
 tturrets/include/turrets_early.qh
@@ -88,6 +91,8 @@ scores.qh
 
 spawnpoints.qh
 
+mapvoting.qh
+
 ipban.qh
 
 race.qh
@@ -131,6 +136,8 @@ pathlib/pathlib.qh
 g_world.qc
 g_casings.qc
 
+mapvoting.qc
+
 t_jumppads.qc
 t_teleporters.qc
 
@@ -214,6 +221,8 @@ target_music.qc
 
 ../common/items.qc
 
+../common/buffs.qc
+
 
 accuracy.qc
 ../csqcmodellib/sv_model.qc
@@ -266,6 +275,7 @@ mutators/mutator_multijump.qc
 mutators/mutator_melee_only.qc
 mutators/mutator_nades.qc
 mutators/mutator_campcheck.qc
+mutators/mutator_buffs.qc
 
 ../warpzonelib/anglestransform.qc
 ../warpzonelib/mathlib.qc
index bb1128bd60e8c532c217de9d8f9d99f9c0404d27..86a87c043d1fd5b781c13201bdb6d242435286b0 100644 (file)
@@ -114,18 +114,24 @@ void spawnfunc_target_give()
        InitializeEntity(self, target_give_init, INITPRIO_FINDTARGET);
 }
 
-//void spawnfunc_item_flight()       /* not supported */
-//void spawnfunc_item_haste()        /* not supported */
+//void spawnfunc_item_flight()       /* handled by buffs mutator or jetpack */
+//void spawnfunc_item_haste()        /* handled by buffs mutator */
 //void spawnfunc_item_health()       /* handled in t_quake.qc */
 //void spawnfunc_item_health_large() /* handled in t_items.qc */
 //void spawnfunc_item_health_small() /* handled in t_items.qc */
 //void spawnfunc_item_health_mega()  /* handled in t_items.qc */
-//void spawnfunc_item_invis()        /* not supported */
-//void spawnfunc_item_regen()        /* not supported */
+//void spawnfunc_item_invis()        /* handled by buffs mutator */
+//void spawnfunc_item_regen()        /* handled by buffs mutator */
 
 // CTF spawnfuncs handled in mutators/gamemode_ctf.qc now
 
-void spawnfunc_item_flight()         { spawnfunc_item_jetpack();       }
+void spawnfunc_item_flight()
+{
+       if(!cvar("g_buffs") || !cvar("g_buffs_flight"))
+               spawnfunc_item_jetpack();
+       else
+               buff_Init_Compat(self, BUFF_FLIGHT);
+}
 
 .float notteam;
 .float notsingle;
diff --git a/scripts/.DS_Store b/scripts/.DS_Store
new file mode 100644 (file)
index 0000000..bcac026
Binary files /dev/null and b/scripts/.DS_Store differ
diff --git a/textures/.DS_Store b/textures/.DS_Store
new file mode 100644 (file)
index 0000000..a499c09
Binary files /dev/null and b/textures/.DS_Store differ
index 9fe937716a1d5d78f25ea2509a52fba7d0dd508f..2fcdf98ce1059a4166623531862fccd1de31b431 100644 (file)
@@ -113,6 +113,7 @@ Oleh "BlaXpirit" Prypin
 Przemysław "atheros" Grzywacz
 Robert "ai" Kuroto
 The player with the unnecessarily long name
+Mattia "Melanosuchus" Basaglia
 
 
 **Translators