From 0e73fb13fe56e1c6856bacd89591f2c5d5321994 Mon Sep 17 00:00:00 2001 From: Mario Date: Sat, 31 May 2014 00:55:58 +1000 Subject: [PATCH] Buffs mutator, loosely based on the old relics mutator --- _hud_descriptions.cfg | 10 + hud_luminos.cfg | 10 + hud_luminos_minimal.cfg | 10 + hud_luminos_minimal_xhair.cfg | 10 + hud_luminos_old.cfg | 10 + hud_nexuiz.cfg | 10 + mutators.cfg | 49 ++ qcsrc/client/Main.qc | 1 + qcsrc/client/autocvars.qh | 2 + qcsrc/client/hud.qc | 60 ++ qcsrc/client/hud.qh | 3 +- qcsrc/client/progs.src | 3 + qcsrc/common/buffs.qc | 44 ++ qcsrc/common/buffs.qh | 83 +++ qcsrc/common/constants.qh | 2 + qcsrc/common/deathtypes.qh | 3 + qcsrc/common/notifications.qh | 14 + qcsrc/menu/classes.c | 1 + qcsrc/menu/xonotic/dialog_hudpanel_buffs.c | 22 + qcsrc/menu/xonotic/mainwindow.c | 4 + qcsrc/server/autocvars.qh | 30 + qcsrc/server/cl_client.qc | 15 +- qcsrc/server/cl_physics.qc | 6 +- qcsrc/server/cl_weaponsystem.qc | 4 + qcsrc/server/g_world.qc | 2 + qcsrc/server/miscfunctions.qc | 1 + qcsrc/server/mutators/base.qh | 11 + qcsrc/server/mutators/mutator_buffs.qc | 744 +++++++++++++++++++++ qcsrc/server/mutators/mutator_buffs.qh | 28 + qcsrc/server/mutators/mutators.qh | 1 + qcsrc/server/progs.src | 5 + 31 files changed, 1191 insertions(+), 7 deletions(-) create mode 100644 qcsrc/common/buffs.qc create mode 100644 qcsrc/common/buffs.qh create mode 100644 qcsrc/menu/xonotic/dialog_hudpanel_buffs.c create mode 100644 qcsrc/server/mutators/mutator_buffs.qc create mode 100644 qcsrc/server/mutators/mutator_buffs.qh diff --git a/_hud_descriptions.cfg b/_hud_descriptions.cfg index 5f21867d3..5818c461c 100644 --- a/_hud_descriptions.cfg +++ b/_hud_descriptions.cfg @@ -296,3 +296,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" diff --git a/hud_luminos.cfg b/hud_luminos.cfg index f2b4fa4bd..86c72ea2a 100644 --- a/hud_luminos.cfg +++ b/hud_luminos.cfg @@ -295,4 +295,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/hud_luminos_minimal.cfg b/hud_luminos_minimal.cfg index c401132ac..a992b3acf 100644 --- a/hud_luminos_minimal.cfg +++ b/hud_luminos_minimal.cfg @@ -295,4 +295,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/hud_luminos_minimal_xhair.cfg b/hud_luminos_minimal_xhair.cfg index 518492ed4..254141559 100644 --- a/hud_luminos_minimal_xhair.cfg +++ b/hud_luminos_minimal_xhair.cfg @@ -295,4 +295,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/hud_luminos_old.cfg b/hud_luminos_old.cfg index 0a5f05955..965737501 100644 --- a/hud_luminos_old.cfg +++ b/hud_luminos_old.cfg @@ -295,4 +295,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/hud_nexuiz.cfg b/hud_nexuiz.cfg index acf3ad921..9fa75602c 100644 --- a/hud_nexuiz.cfg +++ b/hud_nexuiz.cfg @@ -295,4 +295,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/mutators.cfg b/mutators.cfg index c762ef9de..55fabef22 100644 --- a/mutators.cfg +++ b/mutators.cfg @@ -162,3 +162,52 @@ 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 "enable ammo buff" +set g_buffs_resistance 1 "enable resistance buff" +set g_buffs_resistance_blockpercent 0.7 "damage reduction multiplier, higher values mean less damage" +set g_buffs_medic 1 "enable medic buff" +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 "enable vengeance buff" +set g_buffs_vengeance_damage 0.9 "damage multiplier while player is under the influence of vengeance" +set g_buffs_vengeance_damage_take 1.1 "damage multiplier player takes while under the influence of vengeance" +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 "enable bash buff" +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 "enable disability buff" +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 "enable speed buff" +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 "enable vampire buff" +set g_buffs_vampire_damage_steal 0.6 "damage stolen multiplier while holding vampire buff" +set g_buffs_jump 1 "enable jump buff" +set g_buffs_jump_height 600 "jump height while holding jump buff" +set g_buffs_flight 1 "enable flight buff" +set g_buffs_flight_gravity 0.3 "player gravity multiplier while holding flight buff" +set g_buffs_invisible 1 "enable invisibility buff" +set g_buffs_invisible_alpha 0.1 "player invisibility multiplier while holding invisible buff" + diff --git a/qcsrc/client/Main.qc b/qcsrc/client/Main.qc index 4ea750ce5..c948c9a70 100644 --- a/qcsrc/client/Main.qc +++ b/qcsrc/client/Main.qc @@ -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(); diff --git a/qcsrc/client/autocvars.qh b/qcsrc/client/autocvars.qh index 67a032593..5449b6737 100644 --- a/qcsrc/client/autocvars.qh +++ b/qcsrc/client/autocvars.qh @@ -290,6 +290,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; diff --git a/qcsrc/client/hud.qc b/qcsrc/client/hud.qc index bb5eb4a68..9bb7b5331 100644 --- a/qcsrc/client/hud.qc +++ b/qcsrc/client/hud.qc @@ -4366,6 +4366,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 diff --git a/qcsrc/client/hud.qh b/qcsrc/client/hud.qh index 5c062c5fa..908b6c1d5 100644 --- a/qcsrc/client/hud.qh +++ b/qcsrc/client/hud.qh @@ -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; \ diff --git a/qcsrc/client/progs.src b/qcsrc/client/progs.src index 1a518c5a6..19edfbb5b 100644 --- a/qcsrc/client/progs.src +++ b/qcsrc/client/progs.src @@ -16,6 +16,7 @@ Defs.qc ../common/teams.qh ../common/util.qh +../common/buffs.qh ../common/test.qh ../common/counting.qh ../common/items.qh @@ -117,6 +118,8 @@ command/cl_cmd.qc ../common/monsters/monsters.qc +../common/buffs.qc + ../warpzonelib/anglestransform.qc ../warpzonelib/mathlib.qc ../warpzonelib/common.qc diff --git a/qcsrc/common/buffs.qc b/qcsrc/common/buffs.qc new file mode 100644 index 000000000..f9075d05a --- /dev/null +++ b/qcsrc/common/buffs.qc @@ -0,0 +1,44 @@ +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_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; +} diff --git a/qcsrc/common/buffs.qh b/qcsrc/common/buffs.qh new file mode 100644 index 000000000..b61d5307d --- /dev/null +++ b/qcsrc/common/buffs.qh @@ -0,0 +1,83 @@ +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 +.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; \ + 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'); + +#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_##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(item_doubler, BUFF_MEDIC) +BUFF_SPAWNFUNC_Q3TA_COMPAT(item_resistance, BUFF_RESISTANCE) +BUFF_SPAWNFUNC_Q3TA_COMPAT(item_scout, BUFF_SPEED) +BUFF_SPAWNFUNC_Q3TA_COMPAT(item_ammoregen, BUFF_AMMO) +#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_Skin(float buff_id); diff --git a/qcsrc/common/constants.qh b/qcsrc/common/constants.qh index e02fad45f..552fd74c4 100644 --- a/qcsrc/common/constants.qh +++ b/qcsrc/common/constants.qh @@ -185,6 +185,8 @@ const float STAT_WEAPONS3 = 75; const float STAT_MONSTERS_TOTAL = 76; const float STAT_MONSTERS_KILLED = 77; +const float STAT_BUFFS = 78; + // mod stats (1xx) const float STAT_REDALIVE = 100; const float STAT_BLUEALIVE = 101; diff --git a/qcsrc/common/deathtypes.qh b/qcsrc/common/deathtypes.qh index 1265e7eea..e61568217 100644 --- a/qcsrc/common/deathtypes.qh +++ b/qcsrc/common/deathtypes.qh @@ -4,6 +4,8 @@ #define DEATHTYPES \ DEATHTYPE(DEATH_AUTOTEAMCHANGE, DEATH_SELF_AUTOTEAMCHANGE, NO_MSG, DEATH_SPECIAL_START) \ + DEATHTYPE(DEATH_BUFF_VAMPIRE, NO_MSG, DEATH_MURDER_VAMPIRE, DEATH_BUFF_FIRST) \ + DEATHTYPE(DEATH_BUFF_VENGEANCE, NO_MSG, DEATH_MURDER_VENGEANCE, DEATH_BUFF_LAST) \ 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) \ @@ -95,6 +97,7 @@ DEATHTYPES #define DEATH_ISVEHICLE(t) ((t) >= DEATH_VHFIRST && (t) <= DEATH_VHLAST) #define DEATH_ISTURRET(t) ((t) >= DEATH_TURRET_FIRST && (t) <= DEATH_TURRET_LAST) #define DEATH_ISMONSTER(t) ((t) >= DEATH_MONSTER_FIRST && (t) <= DEATH_MONSTER_LAST) +#define DEATH_ISBUFF(t) ((t) >= DEATH_BUFF_FIRST && (t) <= DEATH_BUFF_LAST) #define DEATH_WEAPONOFWEAPONDEATH(t) ((t) & DEATH_WEAPONMASK) #define DEATH_ISWEAPON(t,w) (!DEATH_ISSPECIAL(t) && DEATH_WEAPONOFWEAPONDEATH(t) == (w)) #define DEATH_WEAPONOF(t) (DEATH_ISSPECIAL(t) ? 0 : DEATH_WEAPONOFWEAPONDEATH(t)) diff --git a/qcsrc/common/notifications.qh b/qcsrc/common/notifications.qh index 003c0fcc1..92d2830ff 100644 --- a/qcsrc/common/notifications.qh +++ b/qcsrc/common/notifications.qh @@ -378,6 +378,8 @@ 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_VAMPIRE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_death", _("^BG%s%s^K1's blood was sucked by ^BG%s^K1%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 +441,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 +633,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"), "") \ @@ -716,6 +724,8 @@ 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_VAMPIRE, NO_MSG, INFO_DEATH_MURDER_VAMPIRE, 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) \ @@ -766,6 +776,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) \ @@ -933,6 +945,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 */ @@ -985,6 +998,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/menu/classes.c b/qcsrc/menu/classes.c index 1de86b647..8752fa6fe 100644 --- a/qcsrc/menu/classes.c +++ b/qcsrc/menu/classes.c @@ -110,4 +110,5 @@ #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/dialog_hudpanel_buffs.c b/qcsrc/menu/xonotic/dialog_hudpanel_buffs.c new file mode 100644 index 000000000..ac1033cd5 --- /dev/null +++ b/qcsrc/menu/xonotic/dialog_hudpanel_buffs.c @@ -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 diff --git a/qcsrc/menu/xonotic/mainwindow.c b/qcsrc/menu/xonotic/mainwindow.c index 72cecea19..834242b1c 100644 --- a/qcsrc/menu/xonotic/mainwindow.c +++ b/qcsrc/menu/xonotic/mainwindow.c @@ -125,6 +125,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(); diff --git a/qcsrc/server/autocvars.qh b/qcsrc/server/autocvars.qh index 674c95b14..ce6da5c52 100644 --- a/qcsrc/server/autocvars.qh +++ b/qcsrc/server/autocvars.qh @@ -1278,3 +1278,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; + diff --git a/qcsrc/server/cl_client.qc b/qcsrc/server/cl_client.qc index d06bc96be..fb12db4ef 100644 --- a/qcsrc/server/cl_client.qc +++ b/qcsrc/server/cl_client.qc @@ -1585,17 +1585,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; diff --git a/qcsrc/server/cl_physics.qc b/qcsrc/server/cl_physics.qc index 0d0dd3138..4bfc28870 100644 --- a/qcsrc/server/cl_physics.qc +++ b/qcsrc/server/cl_physics.qc @@ -20,14 +20,15 @@ When you press the jump key void PlayerJump (void) { 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) { @@ -44,7 +45,6 @@ void PlayerJump (void) } } - mjumpheight = autocvar_sv_jumpvelocity; if (self.waterlevel >= WATERLEVEL_SWIMMING) { self.velocity_z = self.stat_sv_maxspeed * 0.7; diff --git a/qcsrc/server/cl_weaponsystem.qc b/qcsrc/server/cl_weaponsystem.qc index f3d675f35..67a1ddb1c 100644 --- a/qcsrc/server/cl_weaponsystem.qc +++ b/qcsrc/server/cl_weaponsystem.qc @@ -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; } diff --git a/qcsrc/server/g_world.qc b/qcsrc/server/g_world.qc index c779f9d4b..80176c25e 100644 --- a/qcsrc/server/g_world.qc +++ b/qcsrc/server/g_world.qc @@ -546,6 +546,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 +596,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)); diff --git a/qcsrc/server/miscfunctions.qc b/qcsrc/server/miscfunctions.qc index 5dc6f2e39..7efaf33c5 100644 --- a/qcsrc/server/miscfunctions.qc +++ b/qcsrc/server/miscfunctions.qc @@ -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 diff --git a/qcsrc/server/mutators/base.qh b/qcsrc/server/mutators/base.qh index 0d2d7c96a..198b7d991 100644 --- a/qcsrc/server/mutators/base.qh +++ b/qcsrc/server/mutators/base.qh @@ -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 index 000000000..70b5246fb --- /dev/null +++ b/qcsrc/server/mutators/mutator_buffs.qc @@ -0,0 +1,744 @@ +float buffs_BuffModel_Customize() +{ + float same_team = (SAME_TEAM(other, self.owner) || (IS_SPEC(other) && SAME_TEAM(other.enemy, self.owner))); + if(self.owner.alpha <= 0.5 && !same_team && self.owner.alpha != 0) + return FALSE; + + if(other == self.owner || (IS_SPEC(other) && other.enemy == self.owner)) + { + // 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_PrettyName(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(self, (spot.origin + '0 0 200') + (randomvec() * 300)); + self.angles = spot.angles; + } + + 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); +} + +void buff_NewType(entity ent, float cb) +{ + entity e; + RandomSelection_Init(); + for(e = Buff_Type_first; e; e = e.enemy) + { + if(e.items == BUFF_AMMO && ((start_items & IT_UNLIMITED_WEAPON_AMMO) || (start_items & IT_UNLIMITED_AMMO))) + continue; + if(e.items == BUFF_VAMPIRE && cvar("g_vampire")) + continue; + if(!cvar(strcat("g_buffs_", e.netname))) + continue; + 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 || !cvar(strcat("g_buffs_", Buff_Name(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; + + 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(DEATH_ISBUFF(frag_deathtype)) { 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; +} + +MUTATOR_HOOKFUNCTION(buffs_PlayerDamage_Calculate) +{ + if(DEATH_ISBUFF(frag_deathtype)) { 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) + 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)) + { + vector v = healtharmor_applydamage(frag_attacker.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_BUFF_VENGEANCE, frag_damage * autocvar_g_buffs_vengeance_damage_multiplier); + float take = v_x; + Damage(frag_attacker, frag_target, frag_target, take, DEATH_BUFF_VENGEANCE, frag_attacker.origin, '0 0 0'); + } + + 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; + + 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_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.oldbuffs & BUFF_JUMP) && !(self.buffs & BUFF_JUMP)) + self.stat_jumpheight = autocvar_sv_jumpvelocity; // reset so we don't break anything + else if((self.buffs & BUFF_JUMP) && self.stat_jumpheight != autocvar_g_buffs_jump_height) + self.stat_jumpheight = autocvar_g_buffs_jump_height; + + 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_JUMP) + self.stat_jumpheight = autocvar_sv_jumpvelocity; + else if(self.buffs & BUFF_JUMP) + self.stat_jumpheight = autocvar_g_buffs_jump_height; + + 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_LAST); + 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(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 index 000000000..66540b876 --- /dev/null +++ b/qcsrc/server/mutators/mutator_buffs.qh @@ -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; diff --git a/qcsrc/server/mutators/mutators.qh b/qcsrc/server/mutators/mutators.qh index ecab77c88..ab519e98f 100644 --- a/qcsrc/server/mutators/mutators.qh +++ b/qcsrc/server/mutators/mutators.qh @@ -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); diff --git a/qcsrc/server/progs.src b/qcsrc/server/progs.src index 1ae22e202..abb047989 100644 --- a/qcsrc/server/progs.src +++ b/qcsrc/server/progs.src @@ -15,6 +15,7 @@ sys-post.qh ../common/constants.qh ../common/teams.qh ../common/util.qh +../common/buffs.qh ../common/test.qh ../common/counting.qh ../common/items.qh @@ -50,6 +51,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 @@ -214,6 +216,8 @@ target_music.qc ../common/items.qc +../common/buffs.qc + accuracy.qc ../csqcmodellib/sv_model.qc @@ -266,6 +270,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 -- 2.39.2