From: Mario Date: Mon, 22 Aug 2016 04:13:45 +0000 (+1000) Subject: Merge branch 'master' into terencehill/spectatee_status_update X-Git-Tag: xonotic-v0.8.2~682^2 X-Git-Url: https://de.git.xonotic.org/?p=xonotic%2Fxonotic-data.pk3dir.git;a=commitdiff_plain;h=e424ba544c41fc40b241b17bd7c1d9c2fc930705;hp=45b05dec0ca9d6ad1530659240b1cef34840eceb Merge branch 'master' into terencehill/spectatee_status_update --- diff --git a/CMakeLists.txt b/CMakeLists.txt index fdbce7d5ac..2f38e43ba2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,11 +26,11 @@ set_source_files_properties( ) add_executable(csprogs qcsrc/client/progs.inc) -target_compile_definitions(csprogs PRIVATE -DCSQC) +target_compile_definitions(csprogs PRIVATE -DGAMEQC -DCSQC) add_dependencies(csprogs gmqcc) add_executable(progs qcsrc/server/progs.inc) -target_compile_definitions(progs PRIVATE -DSVQC) +target_compile_definitions(progs PRIVATE -DGAMEQC -DSVQC) add_dependencies(progs gmqcc) add_executable(menu qcsrc/menu/progs.inc) diff --git a/bal-wep-mario.cfg b/bal-wep-mario.cfg index 5979b78b84..f0fbb6291c 100644 --- a/bal-wep-mario.cfg +++ b/bal-wep-mario.cfg @@ -61,7 +61,7 @@ set g_balance_shotgun_secondary_alt_refire 1.2 set g_balance_shotgun_switchdelay_drop 0.2 set g_balance_shotgun_switchdelay_raise 0.2 set g_balance_shotgun_weaponreplace "" -set g_balance_shotgun_weaponstart 0 +set g_balance_shotgun_weaponstart 1 set g_balance_shotgun_weaponstartoverride -1 set g_balance_shotgun_weaponthrowable 1 // }}} @@ -343,7 +343,7 @@ set g_balance_hagar_primary_lifetime 5 set g_balance_hagar_primary_radius 65 set g_balance_hagar_primary_refire 0.16667 set g_balance_hagar_primary_speed 2200 -set g_balance_hagar_primary_spread 0.03 +set g_balance_hagar_primary_spread 0 set g_balance_hagar_reload_ammo 0 set g_balance_hagar_reload_time 2 set g_balance_hagar_secondary 1 @@ -721,7 +721,7 @@ set g_balance_shockwave_melee_traces 10 set g_balance_shockwave_switchdelay_drop 0.2 set g_balance_shockwave_switchdelay_raise 0.2 set g_balance_shockwave_weaponreplace "" -set g_balance_shockwave_weaponstart 1 +set g_balance_shockwave_weaponstart 0 set g_balance_shockwave_weaponstartoverride -1 set g_balance_shockwave_weaponthrowable 0 // }}} diff --git a/qcsrc/Doxyfile b/qcsrc/Doxyfile index 09d0128578..06ee7468dd 100644 --- a/qcsrc/Doxyfile +++ b/qcsrc/Doxyfile @@ -1997,6 +1997,7 @@ INCLUDE_FILE_PATTERNS = # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. PREDEFINED = \ + "XONOTIC" \ "USING(name, T)=using name = T" \ "CLASS(name, base)=class name : public base { public:" \ "INIT(class)=class::class()" \ diff --git a/qcsrc/Makefile b/qcsrc/Makefile index 0ed67281b4..903d8537e6 100644 --- a/qcsrc/Makefile +++ b/qcsrc/Makefile @@ -8,7 +8,7 @@ QCCFLAGS_WATERMARK ?= $(shell git describe --tags --dirty='~') VER = $(subst *,\*,$(QCCFLAGS_WATERMARK)) NDEBUG ?= 1 XONOTIC ?= 1 -BUILD_MOD ?= 0 +BUILD_MOD ?= ifndef ZIP ifneq ($(shell which zip),) @@ -34,7 +34,7 @@ QCCDEFS ?= \ -DXONOTIC=$(XONOTIC) \ -DWATERMARK="$(QCCFLAGS_WATERMARK)" \ -DNDEBUG=$(NDEBUG) \ - -DBUILD_MOD=$(BUILD_MOD) \ + $(if $(BUILD_MOD), -DBUILD_MOD="$(BUILD_MOD)" -I$(BUILD_MOD), ) \ $(QCCDEFS_EXTRA) # -Ooverlap-locals is required diff --git a/qcsrc/client/_all.inc b/qcsrc/client/_all.inc new file mode 100644 index 0000000000..f592f8adb1 --- /dev/null +++ b/qcsrc/client/_all.inc @@ -0,0 +1,20 @@ +#include +#include "_mod.inc" + +#include "commands/_mod.inc" +#include "hud/_mod.inc" +#include "mutators/_mod.inc" +#include "weapons/_mod.inc" + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include diff --git a/qcsrc/client/_all.qh b/qcsrc/client/_all.qh index 746734dee2..077b5f450a 100644 --- a/qcsrc/client/_all.qh +++ b/qcsrc/client/_all.qh @@ -1,4 +1,5 @@ #pragma once +//#include "_mod.qh" #include @@ -10,3 +11,5 @@ #include "defs.qh" #include "main.qh" #include "miscfunctions.qh" + +#include diff --git a/qcsrc/client/autocvars.qh b/qcsrc/client/autocvars.qh index 0ac857754b..65a2197207 100644 --- a/qcsrc/client/autocvars.qh +++ b/qcsrc/client/autocvars.qh @@ -396,7 +396,6 @@ float autocvar_hud_shownames_offset; string autocvar_hud_skin; float autocvar_menu_mouse_speed; string autocvar_menu_skin; -float autocvar_r_drawviewmodel; int autocvar_r_fakelight; int autocvar_r_fullbright; float autocvar_r_letterbox; @@ -456,4 +455,5 @@ vector autocvar_crosshair_rpc_color = '0.2 1.0 0.2'; float autocvar_crosshair_rpc_alpha = 1; float autocvar_crosshair_rpc_size = 1; int autocvar_cl_nade_timer; +bool autocvar_r_drawviewmodel; bool autocvar_cl_items_nofade; diff --git a/qcsrc/client/commands/_mod.inc b/qcsrc/client/commands/_mod.inc index 235f1297fd..dcfc6ecea5 100644 --- a/qcsrc/client/commands/_mod.inc +++ b/qcsrc/client/commands/_mod.inc @@ -1,3 +1,4 @@ // generated file; do not modify -#include -#include +#ifdef CSQC + #include +#endif diff --git a/qcsrc/client/commands/_mod.qh b/qcsrc/client/commands/_mod.qh index 03df56398c..7829965a23 100644 --- a/qcsrc/client/commands/_mod.qh +++ b/qcsrc/client/commands/_mod.qh @@ -1,3 +1,4 @@ // generated file; do not modify -#include -#include +#ifdef CSQC + #include +#endif diff --git a/qcsrc/client/commands/all.qc b/qcsrc/client/commands/all.qc deleted file mode 100644 index bc15eeb689..0000000000 --- a/qcsrc/client/commands/all.qc +++ /dev/null @@ -1,2 +0,0 @@ -#include "all.qh" -#include diff --git a/qcsrc/client/commands/all.qh b/qcsrc/client/commands/all.qh deleted file mode 100644 index 2df61f004b..0000000000 --- a/qcsrc/client/commands/all.qh +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include - -#include "cl_cmd.qh" diff --git a/qcsrc/client/commands/cl_cmd.qc b/qcsrc/client/commands/cl_cmd.qc index 9b8f58f944..631090cc1f 100644 --- a/qcsrc/client/commands/cl_cmd.qc +++ b/qcsrc/client/commands/cl_cmd.qc @@ -4,12 +4,12 @@ // Last updated: December 28th, 2011 // ============================================== -#include +#include #include "cl_cmd.qh" #include "../autocvars.qh" #include "../defs.qh" -#include +#include #include "../main.qh" #include "../mapvoting.qh" #include "../miscfunctions.qh" @@ -18,8 +18,6 @@ #include -#include - void DrawDebugModel(entity this) { if (time - floor(time) > 0.5) diff --git a/qcsrc/client/csqcmodel_hooks.qc b/qcsrc/client/csqcmodel_hooks.qc index d6ee5f8f79..0643bb09d5 100644 --- a/qcsrc/client/csqcmodel_hooks.qc +++ b/qcsrc/client/csqcmodel_hooks.qc @@ -3,6 +3,7 @@ #include "player_skeleton.qh" #include "weapons/projectile.qh" #include +#include #include #include #include @@ -192,7 +193,7 @@ void CSQCPlayer_ModelAppearance_Apply(entity this, bool islocalplayer) bool isfriend; int cm; cm = this.forceplayermodels_savecolormap; - cm = (cm >= 1024) ? cm : (stof(getplayerkeyvalue(cm - 1, "colors")) + 1024); + cm = (cm >= 1024) ? cm : (entcs_GetClientColors(cm - 1) + 1024); if(teamplay) isfriend = (cm == 1024 + 17 * myteam); @@ -279,7 +280,7 @@ void CSQCPlayer_ModelAppearance_Apply(entity this, bool islocalplayer) // GLOWMOD AND DEATH FADING if(this.colormap > 0) - this.glowmod = colormapPaletteColor(((this.colormap >= 1024) ? this.colormap : stof(getplayerkeyvalue(this.colormap - 1, "colors"))) & 0x0F, true) * 2; + this.glowmod = colormapPaletteColor(((this.colormap >= 1024) ? this.colormap : entcs_GetClientColors(this.colormap - 1)) & 0x0F, true) * 2; else this.glowmod = '1 1 1'; diff --git a/qcsrc/client/hud/_mod.inc b/qcsrc/client/hud/_mod.inc index b90f61b8cc..aa785a9e94 100644 --- a/qcsrc/client/hud/_mod.inc +++ b/qcsrc/client/hud/_mod.inc @@ -1,3 +1,6 @@ // generated file; do not modify #include #include +#include + +#include diff --git a/qcsrc/client/hud/_mod.qh b/qcsrc/client/hud/_mod.qh index ee9ac8f76f..2d4850d521 100644 --- a/qcsrc/client/hud/_mod.qh +++ b/qcsrc/client/hud/_mod.qh @@ -1,3 +1,6 @@ // generated file; do not modify #include #include +#include + +#include diff --git a/qcsrc/client/hud/all.inc b/qcsrc/client/hud/all.inc deleted file mode 100644 index 8305304fe9..0000000000 --- a/qcsrc/client/hud/all.inc +++ /dev/null @@ -1,22 +0,0 @@ -#include "panel/weapons.qc" -#include "panel/ammo.qc" -#include "panel/powerups.qc" -#include "panel/healtharmor.qc" -#include "panel/notify.qc" -#include "panel/timer.qc" -#include "panel/radar.qc" -#include "panel/score.qc" -#include "panel/racetimer.qc" -#include "panel/vote.qc" -#include "panel/modicons.qc" -#include "panel/pressedkeys.qc" -#include "panel/chat.qc" -#include "panel/engineinfo.qc" -#include "panel/infomessages.qc" -#include "panel/physics.qc" -#include "panel/centerprint.qc" -#include "panel/minigame.qc" -// #include "panel/mapvote.qc" -// #include "panel/itemstime.qc" -#include "panel/quickmenu.qc" -#include "panel/scoreboard.qc" diff --git a/qcsrc/client/hud/all.qh b/qcsrc/client/hud/all.qh deleted file mode 100644 index 2e458e6dd5..0000000000 --- a/qcsrc/client/hud/all.qh +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -#include "hud.qh" -#include "hud_config.qh" diff --git a/qcsrc/client/hud/hud.qc b/qcsrc/client/hud/hud.qc index c87e733e1d..f9ac206275 100644 --- a/qcsrc/client/hud/hud.qc +++ b/qcsrc/client/hud/hud.qc @@ -1,11 +1,13 @@ #include "hud.qh" +#include "panel/scoreboard.qh" + #include "hud_config.qh" #include "../mapvoting.qh" #include "../teamradar.qh" #include #include -#include +#include #include #include #include @@ -334,8 +336,6 @@ void DrawNumIcon(vector myPos, vector mySize, float x, string icon, bool vertica DrawNumIcon_expanding(myPos, mySize, x, icon, vertical, icon_right_align, color, theAlpha, 0); } -#include "all.inc" - /* ================== Main HUD system @@ -357,6 +357,8 @@ void HUD_Vehicle() } } +bool HUD_Minigame_Showpanels(); + bool HUD_Panel_CheckFlags(int showflags) { TC(int, showflags); @@ -413,6 +415,7 @@ bool Hud_Shake_Update() return true; } +entity CSQCModel_server2csqc(int i); void calc_followmodel_ofs(entity view); void Hud_Dynamic_Frame() { @@ -556,11 +559,11 @@ void HUD_Main() { string hud_dock_color = autocvar_hud_dock_color; if(hud_dock_color == "shirt") { - f = stof(getplayerkeyvalue(current_player, "colors")); + f = entcs_GetClientColors(current_player); color = colormapPaletteColor(floor(f / 16), 0); } else if(hud_dock_color == "pants") { - f = stof(getplayerkeyvalue(current_player, "colors")); + f = entcs_GetClientColors(current_player); color = colormapPaletteColor(f % 16, 1); } else diff --git a/qcsrc/client/hud/hud.qh b/qcsrc/client/hud/hud.qh index b269312d88..3aa5d8f8c9 100644 --- a/qcsrc/client/hud/hud.qh +++ b/qcsrc/client/hud/hud.qh @@ -1,6 +1,6 @@ #pragma once -#include +#include bool HUD_Radar_Clickable(); void HUD_Radar_Mouse(); @@ -280,9 +280,9 @@ REGISTER_HUD_PANEL(SCOREBOARD, Scoreboard_Draw, scoreboard, PANEL_CO panel_bg_color = autocvar_hud_panel_bg_color; \ } else { \ if (panel_bg_color_str == "shirt") { \ - panel_bg_color = colormapPaletteColor(floor(stof(getplayerkeyvalue(current_player, "colors")) / 16), 0); \ + panel_bg_color = colormapPaletteColor(floor(entcs_GetClientColors(current_player) / 16), 0); \ } else if (panel_bg_color_str == "pants") { \ - panel_bg_color = colormapPaletteColor(stof(getplayerkeyvalue(current_player, "colors")) % 16, 1); \ + panel_bg_color = colormapPaletteColor(entcs_GetClientColors(current_player) % 16, 1); \ } else { \ panel_bg_color = stov(panel_bg_color_str); \ } \ diff --git a/qcsrc/client/hud/panel.qc b/qcsrc/client/hud/panel.qc new file mode 100644 index 0000000000..558f320338 --- /dev/null +++ b/qcsrc/client/hud/panel.qc @@ -0,0 +1 @@ +#include "panel.qh" diff --git a/qcsrc/client/hud/panel/_mod.inc b/qcsrc/client/hud/panel/_mod.inc index d495fdee76..7a95752132 100644 --- a/qcsrc/client/hud/panel/_mod.inc +++ b/qcsrc/client/hud/panel/_mod.inc @@ -15,7 +15,7 @@ #include #include #include +#include #include #include #include -#include diff --git a/qcsrc/client/hud/panel/_mod.qh b/qcsrc/client/hud/panel/_mod.qh index fa9b755309..c24b5c0002 100644 --- a/qcsrc/client/hud/panel/_mod.qh +++ b/qcsrc/client/hud/panel/_mod.qh @@ -15,7 +15,7 @@ #include #include #include +#include #include #include #include -#include diff --git a/qcsrc/client/hud/panel/ammo.qh b/qcsrc/client/hud/panel/ammo.qh index 6db88c68b3..71919d2fb0 100644 --- a/qcsrc/client/hud/panel/ammo.qh +++ b/qcsrc/client/hud/panel/ammo.qh @@ -1,2 +1,4 @@ #pragma once #include "../panel.qh" + +void DrawNadeProgressBar(vector myPos, vector mySize, float progress, vector color); diff --git a/qcsrc/client/hud/panel/centerprint.qh b/qcsrc/client/hud/panel/centerprint.qh index 6db88c68b3..1bec93efa6 100644 --- a/qcsrc/client/hud/panel/centerprint.qh +++ b/qcsrc/client/hud/panel/centerprint.qh @@ -1,2 +1,4 @@ #pragma once #include "../panel.qh" + +void reset_centerprint_messages(); diff --git a/qcsrc/client/hud/panel/powerups.qc b/qcsrc/client/hud/panel/powerups.qc index 7528c2ba2b..4779ab7479 100644 --- a/qcsrc/client/hud/panel/powerups.qc +++ b/qcsrc/client/hud/panel/powerups.qc @@ -1,6 +1,6 @@ #include "powerups.qh" -#include +#include // Powerups (#2) diff --git a/qcsrc/client/hud/panel/powerups.qh b/qcsrc/client/hud/panel/powerups.qh index 6db88c68b3..3235f8da67 100644 --- a/qcsrc/client/hud/panel/powerups.qh +++ b/qcsrc/client/hud/panel/powerups.qh @@ -1,2 +1,4 @@ #pragma once #include "../panel.qh" + +void addPowerupItem(string name, string icon, vector color, float currentTime, float lifeTime); diff --git a/qcsrc/client/hud/panel/quickmenu.qc b/qcsrc/client/hud/panel/quickmenu.qc index b2d3588844..22a9827acc 100644 --- a/qcsrc/client/hud/panel/quickmenu.qc +++ b/qcsrc/client/hud/panel/quickmenu.qc @@ -2,7 +2,7 @@ // QuickMenu (#23) #include -#include +#include #include // QUICKMENU_MAXLINES must be <= 10 diff --git a/qcsrc/client/hud/panel/scoreboard.qc b/qcsrc/client/hud/panel/scoreboard.qc index 9e2acb09ca..22be8b195c 100644 --- a/qcsrc/client/hud/panel/scoreboard.qc +++ b/qcsrc/client/hud/panel/scoreboard.qc @@ -605,7 +605,7 @@ string Scoreboard_GetField(entity pl, PlayerScoreField field) } else if(!teamplay) { - f = stof(getplayerkeyvalue(pl.sv_entnum, "colors")); + f = entcs_GetClientColors(pl.sv_entnum); { sbt_field_icon0 = "gfx/scoreboard/playercolor_base"; sbt_field_icon1 = "gfx/scoreboard/playercolor_shirt"; @@ -786,10 +786,7 @@ vector Scoreboard_DrawHeader(vector pos, vector rgb) if (sbt_highlight) if (!(i % 2)) { - if (i == sbt_num_fields-1) - column_dim.x = sbt_field_size[i] + hud_fontsize.x * 0.5; - else - column_dim.x = sbt_field_size[i] + hud_fontsize.x; + column_dim.x = sbt_field_size[i] + hud_fontsize.x; drawfill(pos - eX * hud_fontsize.x * 0.5, column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL); } @@ -809,8 +806,6 @@ void Scoreboard_DrawItem(vector item_pos, vector rgb, entity pl, bool is_self, i TC(bool, is_self); TC(int, pl_number); string str; bool is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR); - if(is_spec && !is_self) - rgb = '0 0 0'; vector h_pos = item_pos; vector h_size = eX * panel_size.x + eY * hud_fontsize.y * 1.25; @@ -1071,7 +1066,7 @@ vector Scoreboard_AccuracyStats_Draw(vector pos, vector rgb, vector bg_size) // row highlighting for (int i = 0; i < rows; ++i) - drawfill(pos + eY * weapon_height + eY * height * i, eX * tmp.x + eY * fontsize, '1 1 1', sbt_highlight_alpha, DRAWFLAG_NORMAL); + drawfill(pos + eY * weapon_height + eY * height * i, eX * tmp.x + eY * fontsize, rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL); } average_accuracy = 0; @@ -1430,7 +1425,7 @@ void Scoreboard_Draw() if(pl.team != NUM_SPECTATOR) continue; pos.y += 1.25 * hud_fontsize.y; - Scoreboard_DrawItem(pos, panel_bg_color, pl, (pl.sv_entnum == player_localnum), specs); + Scoreboard_DrawItem(pos, '0 0 0', pl, (pl.sv_entnum == player_localnum), specs); ++specs; } diff --git a/qcsrc/client/main.qc b/qcsrc/client/main.qc index c5e47237b1..d49548084a 100644 --- a/qcsrc/client/main.qc +++ b/qcsrc/client/main.qc @@ -1,7 +1,7 @@ #include "main.qh" #include -#include "hud/all.qh" +#include "hud/_mod.qh" #include "mapvoting.qh" #include "mutators/events.qh" #include "hud/panel/scoreboard.qh" @@ -11,7 +11,7 @@ #include "wall.qh" #include "weapons/projectile.qh" #include -#include +#include #include #include #include diff --git a/qcsrc/client/main.qh b/qcsrc/client/main.qh index afd0b5e38c..9da7547299 100644 --- a/qcsrc/client/main.qh +++ b/qcsrc/client/main.qh @@ -1,7 +1,7 @@ #pragma once #include -#include +#include // -------------------------------------------------------------------------- // MENU Functionality @@ -126,7 +126,7 @@ const int HOOK_END = 2; float g_trueaim_minrange; -float hud; +int hud; float view_quality; int num_spectators; diff --git a/qcsrc/client/mapvoting.qc b/qcsrc/client/mapvoting.qc index 19c128a7da..89f540778e 100644 --- a/qcsrc/client/mapvoting.qc +++ b/qcsrc/client/mapvoting.qc @@ -1,6 +1,6 @@ #include "mapvoting.qh" -#include "hud/all.qh" +#include "hud/_mod.qh" #include "hud/panel/scoreboard.qh" #include diff --git a/qcsrc/client/miscfunctions.qc b/qcsrc/client/miscfunctions.qc index 01b0ee3cb4..33fd015cce 100644 --- a/qcsrc/client/miscfunctions.qc +++ b/qcsrc/client/miscfunctions.qc @@ -1,8 +1,8 @@ #include "miscfunctions.qh" -#include "hud/all.qh" +#include "hud/_mod.qh" -#include +#include #include diff --git a/qcsrc/client/mutators/_mod.inc b/qcsrc/client/mutators/_mod.inc index 98fb4815c1..3dfd4f7897 100644 --- a/qcsrc/client/mutators/_mod.inc +++ b/qcsrc/client/mutators/_mod.inc @@ -1 +1,2 @@ // generated file; do not modify +#include diff --git a/qcsrc/client/mutators/_mod.qh b/qcsrc/client/mutators/_mod.qh index 98fb4815c1..b54ee489cf 100644 --- a/qcsrc/client/mutators/_mod.qh +++ b/qcsrc/client/mutators/_mod.qh @@ -1 +1,2 @@ // generated file; do not modify +#include diff --git a/qcsrc/client/mutators/events.qc b/qcsrc/client/mutators/events.qc new file mode 100644 index 0000000000..c2dbb70215 --- /dev/null +++ b/qcsrc/client/mutators/events.qc @@ -0,0 +1 @@ +#include "events.qh" diff --git a/qcsrc/client/mutators/events.qh b/qcsrc/client/mutators/events.qh index b72f7abe1a..a852fdbe47 100644 --- a/qcsrc/client/mutators/events.qh +++ b/qcsrc/client/mutators/events.qh @@ -142,3 +142,6 @@ MUTATOR_HOOKABLE(DrawGrapplingHook, EV_DrawGrapplingHook); /** is new to client */ i(bool, MUTATOR_ARGV_1_bool) \ /**/ MUTATOR_HOOKABLE(Ent_Update, EV_Ent_Update); + +/** Return true to not draw crosshair */ +MUTATOR_HOOKABLE(DrawCrosshair, EV_NO_ARGS); diff --git a/qcsrc/client/progs.inc b/qcsrc/client/progs.inc index 48579ecbb3..327df77c25 100644 --- a/qcsrc/client/progs.inc +++ b/qcsrc/client/progs.inc @@ -1,32 +1,11 @@ #include #if XONOTIC - -#include "_all.qh" - -#include "../client/_mod.inc" -#include "commands/_mod.inc" -#include "hud/_mod.inc" -#include "mutators/_mod.inc" -#include "weapons/_mod.inc" - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - +#include #endif -#include +#include -#if BUILD_MOD -#include "../../mod/client/progs.inc" +#ifdef BUILD_MOD +#include #endif diff --git a/qcsrc/client/shownames.qc b/qcsrc/client/shownames.qc index 165b033fb3..75ef40521e 100644 --- a/qcsrc/client/shownames.qc +++ b/qcsrc/client/shownames.qc @@ -1,6 +1,6 @@ #include "shownames.qh" -#include "hud/all.qh" +#include "hud/_mod.qh" #include #include diff --git a/qcsrc/client/teamradar.qc b/qcsrc/client/teamradar.qc index ab14cd3bf3..9c4544bba0 100644 --- a/qcsrc/client/teamradar.qc +++ b/qcsrc/client/teamradar.qc @@ -1,6 +1,6 @@ #include "teamradar.qh" -#include "hud/all.qh" +#include "hud/_mod.qh" #include diff --git a/qcsrc/client/view.qc b/qcsrc/client/view.qc index 339d05209d..2226200521 100644 --- a/qcsrc/client/view.qc +++ b/qcsrc/client/view.qc @@ -1,7 +1,7 @@ #include "view.qh" #include "announcer.qh" -#include "hud/all.qh" +#include "hud/_mod.qh" #include "mapvoting.qh" #include "shownames.qh" #include "hud/panel/scoreboard.qh" @@ -15,14 +15,14 @@ #include #include #include -#include +#include #include #include #include #include #include -#include +#include #include #include #include @@ -293,7 +293,7 @@ void viewmodel_draw(entity this) else if (wasinvehicle) a = 1; wasinvehicle = invehicle; Weapon wep = activeweapon; - int c = stof(getplayerkeyvalue(current_player, "colors")); + int c = entcs_GetClientColors(current_player); vector g = weaponentity_glowmod(wep, c); entity me = CSQCModel_server2csqc(player_localentnum - 1); int fx = ((me.csqcmodel_effects & EFMASK_CHEAP) @@ -937,7 +937,7 @@ void HUD_Crosshair(entity this) float f, i, j; vector v; if(!scoreboard_active && !camera_active && intermission != 2 && - spectatee_status != -1 && !csqcplayer.viewloc && + spectatee_status != -1 && !csqcplayer.viewloc && !MUTATOR_CALLHOOK(DrawCrosshair) && !HUD_MinigameMenu_IsOpened() ) { if (!autocvar_crosshair_enabled) // main toggle for crosshair rendering @@ -1337,6 +1337,11 @@ void HUD_Draw(entity this) DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", STAT(NADE_TIMER), '0.25 0.90 1' + ('1 0 0' * STAT(NADE_TIMER)) - ('0 1 1' * STAT(NADE_TIMER)), autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE); drawstring_aspect(eY * 0.64 * vid_conheight, ((autocvar_cl_nade_timer == 2) ? _("Nade timer") : ""), eX * vid_conwidth + eY * 0.025 * vid_conheight, '1 1 1', 1, DRAWFLAG_NORMAL); } + else if(STAT(CAPTURE_PROGRESS)) + { + DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", STAT(CAPTURE_PROGRESS), '0.25 0.90 1', autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE); + drawstring_aspect(eY * 0.64 * vid_conheight, _("Capture progress"), eX * vid_conwidth + eY * 0.025 * vid_conheight, '1 1 1', 1, DRAWFLAG_NORMAL); + } else if(STAT(REVIVE_PROGRESS)) { DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", STAT(REVIVE_PROGRESS), '0.25 0.90 1', autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE); @@ -1368,6 +1373,7 @@ float oldr_useportalculling; float oldr_useinfinitefarclip; void cl_notice_run(); + float prev_myteam; int lasthud; float vh_notice_time; @@ -1825,8 +1831,7 @@ void CSQC_UpdateView(entity this, float w, float h) else if(csqcplayer.viewloc) { setproperty(VF_FOV, GetViewLocationFOV(110)); } // enforce 110 fov, so things dont look odd else { setproperty(VF_FOV, GetCurrentFov(fov)); } - // Camera for demo playback - if(camera_active) + if(camera_active) // Camera for demo playback { if(autocvar_camera_enable) CSQC_Demo_Camera(); diff --git a/qcsrc/common/_all.inc b/qcsrc/common/_all.inc index b16c24fb47..f528eee6db 100644 --- a/qcsrc/common/_all.inc +++ b/qcsrc/common/_all.inc @@ -1,6 +1,6 @@ float autocvar_net_connecttimeout = 30; -#ifndef MENUQC +#ifdef GAMEQC #include "anim.qc" #include "animdecide.qc" #include "ent_cs.qc" @@ -19,19 +19,21 @@ float autocvar_net_connecttimeout = 30; #include "campaign_setup.qc" #endif -#ifndef MENUQC +#ifdef GAMEQC #include "physics/all.inc" #include "triggers/include.qc" #include "viewloc.qc" #endif -#ifndef MENUQC +#ifdef GAMEQC #include "minigames/minigames.qc" #endif #include "debug.qh" -#ifndef MENUQC +#include "command/_mod.inc" + +#ifdef GAMEQC #include "deathtypes/all.qc" #include "effects/all.qc" #include "impulses/all.qc" @@ -40,7 +42,7 @@ float autocvar_net_connecttimeout = 30; #endif #include "items/_mod.inc" - #include "weapons/all.qc" + #include "weapons/_all.inc" #include "monsters/all.qc" #include "turrets/all.qc" #include "vehicles/all.qc" diff --git a/qcsrc/common/campaign_file.qc b/qcsrc/common/campaign_file.qc index a8bbe8e856..af81942a20 100644 --- a/qcsrc/common/campaign_file.qc +++ b/qcsrc/common/campaign_file.qc @@ -1,3 +1,4 @@ +#include "campaign_file.qh" #if defined(CSQC) #elif defined(MENUQC) #elif defined(SVQC) diff --git a/qcsrc/common/campaign_file.qh b/qcsrc/common/campaign_file.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/campaign_file.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/campaign_setup.qc b/qcsrc/common/campaign_setup.qc index 8c20c755c3..258d47f452 100644 --- a/qcsrc/common/campaign_setup.qc +++ b/qcsrc/common/campaign_setup.qc @@ -1,3 +1,4 @@ +#include "campaign_setup.qh" #if defined(CSQC) #elif defined(MENUQC) #elif defined(SVQC) diff --git a/qcsrc/common/campaign_setup.qh b/qcsrc/common/campaign_setup.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/campaign_setup.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/command/_mod.inc b/qcsrc/common/command/_mod.inc index d2d24f03d9..e3099980ed 100644 --- a/qcsrc/common/command/_mod.inc +++ b/qcsrc/common/command/_mod.inc @@ -1,5 +1,5 @@ // generated file; do not modify -#include #include #include +#include #include diff --git a/qcsrc/common/command/_mod.qh b/qcsrc/common/command/_mod.qh index 440bdcb134..6ca293773c 100644 --- a/qcsrc/common/command/_mod.qh +++ b/qcsrc/common/command/_mod.qh @@ -1,5 +1,5 @@ // generated file; do not modify -#include #include #include +#include #include diff --git a/qcsrc/common/command/all.qc b/qcsrc/common/command/all.qc deleted file mode 100644 index dc1c0441fa..0000000000 --- a/qcsrc/common/command/all.qc +++ /dev/null @@ -1,3 +0,0 @@ -#include "generic.qc" -#include "markup.qc" -#include "rpn.qc" diff --git a/qcsrc/common/command/all.qh b/qcsrc/common/command/all.qh deleted file mode 100644 index 15285b92ec..0000000000 --- a/qcsrc/common/command/all.qh +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "command.qh" -REGISTRY(GENERIC_COMMANDS, BITS(7)) -#define GENERIC_COMMANDS_from(i) _GENERIC_COMMANDS_from(i, NULL) -REGISTER_REGISTRY(GENERIC_COMMANDS) -REGISTRY_SORT(GENERIC_COMMANDS) - -#define GENERIC_COMMAND(id, description) \ - CLASS(genericcommand_##id, Command) \ - ATTRIB(genericcommand_##id, m_name, string, #id); \ - ATTRIB(genericcommand_##id, m_description, string, description); \ - ENDCLASS(genericcommand_##id) \ - REGISTER(GENERIC_COMMANDS, CMD_G, id, m_id, NEW(genericcommand_##id)); \ - METHOD(genericcommand_##id, m_invokecmd, void(genericcommand_##id this, int request, entity caller, int arguments, string command)) - -STATIC_INIT(GENERIC_COMMANDS_aliases) { - FOREACH(GENERIC_COMMANDS, true, localcmd(sprintf("alias %1$s \"%2$s %1$s ${* ?}\"\n", it.m_name, "qc_cmd_svmenu"))); -} - -#include "generic.qh" -#include "markup.qh" -#include "rpn.qh" diff --git a/qcsrc/common/command/generic.qc b/qcsrc/common/command/generic.qc index 8f3449b3e1..49a9d13098 100644 --- a/qcsrc/common/command/generic.qc +++ b/qcsrc/common/command/generic.qc @@ -1,11 +1,13 @@ -#include "all.qh" +#include "generic.qh" +#include "_mod.qh" +#include "reg.qh" #include "markup.qh" #include "rpn.qh" #include "../mapinfo.qh" -#ifndef MENUQC +#ifdef GAMEQC #include "../notifications/all.qh" #endif @@ -14,10 +16,7 @@ #endif #ifdef SVQC - #include - #include - #include - #include + #include #include #include #endif @@ -374,7 +373,7 @@ void GenericCommand_restartnotifs(float request) { case CMD_REQUEST_COMMAND: { - #ifndef MENUQC + #ifdef GAMEQC int NOTIF_ANNCE_COUNT = 0; FOREACH(Notifications, it.nent_type == MSG_ANNCE, { ++NOTIF_ANNCE_COUNT; }); int NOTIF_INFO_COUNT = 0; FOREACH(Notifications, it.nent_type == MSG_INFO, { ++NOTIF_INFO_COUNT; }); int NOTIF_CENTER_COUNT = 0; FOREACH(Notifications, it.nent_type == MSG_CENTER, { ++NOTIF_CENTER_COUNT; }); diff --git a/qcsrc/common/command/markup.qc b/qcsrc/common/command/markup.qc index 583c61e551..95a3b53c54 100644 --- a/qcsrc/common/command/markup.qc +++ b/qcsrc/common/command/markup.qc @@ -1,5 +1,5 @@ -#include "command.qh" #include "markup.qh" +#include "command.qh" // ========================================================= // Markup chat characters command code, reworked by Samual diff --git a/qcsrc/common/command/reg.qc b/qcsrc/common/command/reg.qc new file mode 100644 index 0000000000..c0af5b5e09 --- /dev/null +++ b/qcsrc/common/command/reg.qc @@ -0,0 +1 @@ +#include "reg.qh" diff --git a/qcsrc/common/command/reg.qh b/qcsrc/common/command/reg.qh new file mode 100644 index 0000000000..9868e2490a --- /dev/null +++ b/qcsrc/common/command/reg.qh @@ -0,0 +1,19 @@ +#pragma once + +#include "command.qh" +REGISTRY(GENERIC_COMMANDS, BITS(7)) +#define GENERIC_COMMANDS_from(i) _GENERIC_COMMANDS_from(i, NULL) +REGISTER_REGISTRY(GENERIC_COMMANDS) +REGISTRY_SORT(GENERIC_COMMANDS) + +#define GENERIC_COMMAND(id, description) \ + CLASS(genericcommand_##id, Command) \ + ATTRIB(genericcommand_##id, m_name, string, #id); \ + ATTRIB(genericcommand_##id, m_description, string, description); \ + ENDCLASS(genericcommand_##id) \ + REGISTER(GENERIC_COMMANDS, CMD_G, id, m_id, NEW(genericcommand_##id)); \ + METHOD(genericcommand_##id, m_invokecmd, void(genericcommand_##id this, int request, entity caller, int arguments, string command)) + +STATIC_INIT(GENERIC_COMMANDS_aliases) { + FOREACH(GENERIC_COMMANDS, true, localcmd(sprintf("alias %1$s \"%2$s %1$s ${* ?}\"\n", it.m_name, "qc_cmd_svmenu"))); +} diff --git a/qcsrc/common/command/rpn.qc b/qcsrc/common/command/rpn.qc index 12bb99d1d3..f88bf635ee 100644 --- a/qcsrc/common/command/rpn.qc +++ b/qcsrc/common/command/rpn.qc @@ -1,5 +1,5 @@ -#include "command.qh" #include "rpn.qh" +#include "command.qh" // ======================================== diff --git a/qcsrc/common/constants.qh b/qcsrc/common/constants.qh index 6c77556039..80a3f5eddd 100644 --- a/qcsrc/common/constants.qh +++ b/qcsrc/common/constants.qh @@ -126,7 +126,7 @@ const int SFL_SORT_PRIO_MASK = 12; * Score indices */ -#ifndef MENUQC +#ifdef GAMEQC #define IS_INCREASING(x) ( (x) & SFL_LOWER_IS_BETTER ) #define IS_DECREASING(x) ( !((x) & SFL_LOWER_IS_BETTER) ) diff --git a/qcsrc/common/debug.qh b/qcsrc/common/debug.qh index 052e00f07c..6d580bd2d6 100644 --- a/qcsrc/common/debug.qh +++ b/qcsrc/common/debug.qh @@ -4,7 +4,7 @@ .entity tag_entity; #endif -#ifndef MENUQC +#ifdef GAMEQC .bool debug; .int sv_entnum; REGISTER_NET_TEMP(net_debug) @@ -47,7 +47,7 @@ REGISTER_NET_TEMP(net_debug) } #endif -#ifndef MENUQC +#ifdef GAMEQC /** * 0: off * 1: on diff --git a/qcsrc/common/effects/_mod.inc b/qcsrc/common/effects/_mod.inc index 6975259c17..d5dab8cad2 100644 --- a/qcsrc/common/effects/_mod.inc +++ b/qcsrc/common/effects/_mod.inc @@ -1,3 +1,5 @@ // generated file; do not modify #include #include + +#include diff --git a/qcsrc/common/effects/_mod.qh b/qcsrc/common/effects/_mod.qh index 3f5ed82198..8d6e8ed18c 100644 --- a/qcsrc/common/effects/_mod.qh +++ b/qcsrc/common/effects/_mod.qh @@ -1,3 +1,5 @@ // generated file; do not modify #include #include + +#include diff --git a/qcsrc/common/effects/effectinfo.qc b/qcsrc/common/effects/effectinfo.qc index cf6d5fca93..f662aee7b2 100644 --- a/qcsrc/common/effects/effectinfo.qc +++ b/qcsrc/common/effects/effectinfo.qc @@ -1,3 +1,4 @@ +#include "effectinfo.qh" #define EFFECTINFO_PARSER(on, MY) \ on(type, MY(type) \ ,{ demand(n == 1 && "type"); MY(type) = strzone(argv(1)); \ diff --git a/qcsrc/common/effects/effectinfo.qh b/qcsrc/common/effects/effectinfo.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/effects/effectinfo.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/effects/qc/casings.qc b/qcsrc/common/effects/qc/casings.qc index f92df7cd0e..d0befbb7ed 100644 --- a/qcsrc/common/effects/qc/casings.qc +++ b/qcsrc/common/effects/qc/casings.qc @@ -1,3 +1,4 @@ +#include "casings.qh" #ifdef SVQC void SpawnCasing(vector vel, float randomvel, vector ang, vector avel, float randomavel, int casingtype, entity casingowner, .entity weaponentity); #endif diff --git a/qcsrc/common/effects/qc/casings.qh b/qcsrc/common/effects/qc/casings.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/effects/qc/casings.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/effects/qc/damageeffects.qc b/qcsrc/common/effects/qc/damageeffects.qc index d17502e81b..71e1e2a7bc 100644 --- a/qcsrc/common/effects/qc/damageeffects.qc +++ b/qcsrc/common/effects/qc/damageeffects.qc @@ -1,15 +1,4 @@ -#ifndef DAMAGEEFFECTS_H -#define DAMAGEEFFECTS_H - -#ifdef CSQC -#include -#include -#include -#include -#include -#endif - -#endif +#include "damageeffects.qh" #ifdef IMPLEMENTATION diff --git a/qcsrc/common/effects/qc/damageeffects.qh b/qcsrc/common/effects/qc/damageeffects.qh new file mode 100644 index 0000000000..2a1d587ca4 --- /dev/null +++ b/qcsrc/common/effects/qc/damageeffects.qh @@ -0,0 +1,9 @@ +#pragma once + +#ifdef CSQC +#include +#include +#include +#include +#include +#endif diff --git a/qcsrc/common/effects/qc/globalsound.qc b/qcsrc/common/effects/qc/globalsound.qc index 573b52f22c..d60b2e8a65 100644 --- a/qcsrc/common/effects/qc/globalsound.qc +++ b/qcsrc/common/effects/qc/globalsound.qc @@ -6,7 +6,7 @@ #include #ifdef SVQC - #include + #include #endif REGISTER_NET_TEMP(globalsound) diff --git a/qcsrc/common/ent_cs.qc b/qcsrc/common/ent_cs.qc index 2a7b3f80c4..41183d85b2 100644 --- a/qcsrc/common/ent_cs.qc +++ b/qcsrc/common/ent_cs.qc @@ -34,6 +34,14 @@ PROP(true, skin, \ { WriteByte(chan, this.skin); }, \ { this.skin = ReadByte(); }) \ + \ + PROP(true, clientcolors, \ + { WriteByte(chan, this.clientcolors); }, \ + { this.colormap = ReadByte(); }) \ + \ + PROP(true, frags, \ + { WriteShort(chan, this.frags); }, \ + { this.frags = ReadShort(); }) \ \ /**/ diff --git a/qcsrc/common/ent_cs.qh b/qcsrc/common/ent_cs.qh index fdaaab2d57..65cdd83d3a 100644 --- a/qcsrc/common/ent_cs.qh +++ b/qcsrc/common/ent_cs.qh @@ -58,10 +58,21 @@ REGISTER_NET_TEMP(CLIENT_ENTCS) /** * @param i zero indexed player */ + .int frags; bool entcs_IsSpectating(int i) { bool unconnected = !playerslots[i].gotscores; - return unconnected || stof(getplayerkeyvalue(i, "frags")) == FRAGS_SPECTATOR; + entity e = entcs_receiver(i); + return unconnected || ((e) ? e.frags : stof(getplayerkeyvalue(i, "frags"))) == FRAGS_SPECTATOR; + } + + /** + * @param i zero indexed player + */ + int entcs_GetClientColors(int i) + { + entity e = entcs_receiver(i); + return e ? e.colormap : stof(getplayerkeyvalue(i, "colors")); } /** @@ -70,7 +81,7 @@ REGISTER_NET_TEMP(CLIENT_ENTCS) */ int entcs_GetTeamColor(int i) { - return (!teamplay) ? 0 : stof(getplayerkeyvalue(i, "colors")) & 15; + return (!teamplay) ? 0 : entcs_GetClientColors(i) & 15; } /** @@ -97,7 +108,8 @@ REGISTER_NET_TEMP(CLIENT_ENTCS) */ string entcs_GetName(int i) { - return ColorTranslateRGB(getplayerkeyvalue(i, "name")); + entity e = entcs_receiver(i); + return ColorTranslateRGB(e ? e.netname : getplayerkeyvalue(i, "name")); } /** @@ -126,7 +138,7 @@ REGISTER_NET_TEMP(CLIENT_ENTCS) ? '1 1 1' : colormapPaletteColor(((e.colormap >= 1024) ? e.colormap - : stof(getplayerkeyvalue(e.colormap - 1, "colors"))) & 15, true) + : entcs_GetClientColors(e.colormap - 1)) & 15, true) ; } diff --git a/qcsrc/common/gamemodes/_mod.inc b/qcsrc/common/gamemodes/_mod.inc index 0b779498b1..c3cec69dc5 100644 --- a/qcsrc/common/gamemodes/_mod.inc +++ b/qcsrc/common/gamemodes/_mod.inc @@ -1,2 +1,3 @@ // generated file; do not modify -#include + +#include diff --git a/qcsrc/common/gamemodes/_mod.qh b/qcsrc/common/gamemodes/_mod.qh index a7b7a54af4..685c277b4e 100644 --- a/qcsrc/common/gamemodes/_mod.qh +++ b/qcsrc/common/gamemodes/_mod.qh @@ -1,2 +1,3 @@ // generated file; do not modify -#include + +#include diff --git a/qcsrc/common/gamemodes/all.inc b/qcsrc/common/gamemodes/all.inc deleted file mode 100644 index bcdcd7c4c0..0000000000 --- a/qcsrc/common/gamemodes/all.inc +++ /dev/null @@ -1,2 +0,0 @@ -#include "gamemode/nexball/module.inc" -#include "gamemode/onslaught/module.inc" diff --git a/qcsrc/common/gamemodes/all.qc b/qcsrc/common/gamemodes/all.qc deleted file mode 100644 index f0fc7195a8..0000000000 --- a/qcsrc/common/gamemodes/all.qc +++ /dev/null @@ -1,5 +0,0 @@ -#include "all.qh" - -#define IMPLEMENTATION -#include "all.inc" -#undef IMPLEMENTATION diff --git a/qcsrc/common/gamemodes/all.qh b/qcsrc/common/gamemodes/all.qh deleted file mode 100644 index 62ba61696e..0000000000 --- a/qcsrc/common/gamemodes/all.qh +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef GAMEMODES_ALL_H -#define GAMEMODES_ALL_H - -#include "all.inc" - -#endif diff --git a/qcsrc/common/gamemodes/gamemode/_mod.inc b/qcsrc/common/gamemodes/gamemode/_mod.inc index 98fb4815c1..2fc2c40467 100644 --- a/qcsrc/common/gamemodes/gamemode/_mod.inc +++ b/qcsrc/common/gamemodes/gamemode/_mod.inc @@ -1 +1,4 @@ // generated file; do not modify + +#include +#include diff --git a/qcsrc/common/gamemodes/gamemode/_mod.qh b/qcsrc/common/gamemodes/gamemode/_mod.qh index 98fb4815c1..d799570126 100644 --- a/qcsrc/common/gamemodes/gamemode/_mod.qh +++ b/qcsrc/common/gamemodes/gamemode/_mod.qh @@ -1 +1,4 @@ // generated file; do not modify + +#include +#include diff --git a/qcsrc/common/gamemodes/gamemode/nexball/module.inc b/qcsrc/common/gamemodes/gamemode/nexball/module.inc deleted file mode 100644 index 0d809629de..0000000000 --- a/qcsrc/common/gamemodes/gamemode/nexball/module.inc +++ /dev/null @@ -1,2 +0,0 @@ -#include "nexball.qc" -#include "weapon.qc" diff --git a/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc b/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc index 0f0ecb4719..9c6c9f0803 100644 --- a/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc +++ b/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc @@ -1,6 +1,5 @@ #include "nexball.qh" -#ifdef IMPLEMENTATION #ifdef CSQC int autocvar_cl_eventchase_nexball = 1; @@ -1157,4 +1156,3 @@ REGISTER_MUTATOR(nb, g_nexball) } #endif -#endif diff --git a/qcsrc/common/gamemodes/gamemode/nexball/nexball.qh b/qcsrc/common/gamemodes/gamemode/nexball/nexball.qh index 9dd8042be3..53797d2bc6 100644 --- a/qcsrc/common/gamemodes/gamemode/nexball/nexball.qh +++ b/qcsrc/common/gamemodes/gamemode/nexball/nexball.qh @@ -1,5 +1,4 @@ -#ifndef GAMEMODE_NEXBALL_H -#define GAMEMODE_NEXBALL_H +#pragma once #ifdef SVQC //EF_BRIGHTFIELD|EF_BRIGHTLIGHT|EF_DIMLIGHT|EF_BLUE|EF_RED|EF_FLAME @@ -35,4 +34,3 @@ float nb_teams; .float teamtime; #endif -#endif diff --git a/qcsrc/common/gamemodes/gamemode/nexball/weapon.qc b/qcsrc/common/gamemodes/gamemode/nexball/weapon.qc index c6aa4be215..f207263535 100644 --- a/qcsrc/common/gamemodes/gamemode/nexball/weapon.qc +++ b/qcsrc/common/gamemodes/gamemode/nexball/weapon.qc @@ -1,12 +1 @@ -#ifndef GAMEMODE_NEXBALL_WEAPON_H -#define GAMEMODE_NEXBALL_WEAPON_H - -CLASS(BallStealer, PortoLaunch) -/* flags */ ATTRIB(BallStealer, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); -/* impulse */ ATTRIB(BallStealer, impulse, int, 0); -/* refname */ ATTRIB(BallStealer, netname, string, "ballstealer"); -/* wepname */ ATTRIB(BallStealer, m_name, string, _("Ball Stealer")); -ENDCLASS(BallStealer) -REGISTER_WEAPON(NEXBALL, NEW(BallStealer)); - -#endif +#include "weapon.qh" diff --git a/qcsrc/common/gamemodes/gamemode/nexball/weapon.qh b/qcsrc/common/gamemodes/gamemode/nexball/weapon.qh new file mode 100644 index 0000000000..73b887260c --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/nexball/weapon.qh @@ -0,0 +1,9 @@ +#pragma once + +CLASS(BallStealer, PortoLaunch) +/* flags */ ATTRIB(BallStealer, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); +/* impulse */ ATTRIB(BallStealer, impulse, int, 0); +/* refname */ ATTRIB(BallStealer, netname, string, "ballstealer"); +/* wepname */ ATTRIB(BallStealer, m_name, string, _("Ball Stealer")); +ENDCLASS(BallStealer) +REGISTER_WEAPON(NEXBALL, NEW(BallStealer)); diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/_mod.inc b/qcsrc/common/gamemodes/gamemode/onslaught/_mod.inc index 334eb561ee..ca8c83c19a 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/_mod.inc +++ b/qcsrc/common/gamemodes/gamemode/onslaught/_mod.inc @@ -1,6 +1,19 @@ // generated file; do not modify -#include -#include +#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif +#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif #include -#include -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/_mod.qh b/qcsrc/common/gamemodes/gamemode/onslaught/_mod.qh index e895495581..bb95416b31 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/_mod.qh +++ b/qcsrc/common/gamemodes/gamemode/onslaught/_mod.qh @@ -1,6 +1,19 @@ // generated file; do not modify -#include -#include +#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif +#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif #include -#include -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qh b/qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qh index 15586ea5dc..d5437338e3 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qh +++ b/qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qh @@ -1,10 +1,7 @@ -#ifndef CLIENT_CONTROLPOINT_H -#define CLIENT_CONTROLPOINT_H +#pragma once const vector CPICON_MIN = '-32 -32 -9'; const vector CPICON_MAX = '32 32 25'; const int CPSF_STATUS = 4; const int CPSF_SETUP = 8; - -#endif diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qh b/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qh index 3c0cf28697..49524687f4 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qh +++ b/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qh @@ -1,9 +1,7 @@ -#ifndef CLIENT_GENERATOR_H -#define CLIENT_GENERATOR_H +#pragma once + const vector GENERATOR_MIN = '-52 -52 -14'; const vector GENERATOR_MAX = '52 52 75'; const int GSF_STATUS = 4; const int GSF_SETUP = 8; - -#endif diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/controlpoint.qc b/qcsrc/common/gamemodes/gamemode/onslaught/controlpoint.qc new file mode 100644 index 0000000000..b21f5fd228 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/onslaught/controlpoint.qc @@ -0,0 +1 @@ +#include "controlpoint.qh" diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/controlpoint.qh b/qcsrc/common/gamemodes/gamemode/onslaught/controlpoint.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/onslaught/controlpoint.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/generator.qc b/qcsrc/common/gamemodes/gamemode/onslaught/generator.qc new file mode 100644 index 0000000000..f9415f619f --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/onslaught/generator.qc @@ -0,0 +1 @@ +#include "generator.qh" diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/generator.qh b/qcsrc/common/gamemodes/gamemode/onslaught/generator.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/onslaught/generator.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/module.inc b/qcsrc/common/gamemodes/gamemode/onslaught/module.inc deleted file mode 100644 index fee33b14e2..0000000000 --- a/qcsrc/common/gamemodes/gamemode/onslaught/module.inc +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef ONS_CONSTANTS - #define ONS_CONSTANTS - REGISTER_NET_LINKED(ENT_CLIENT_GENERATOR) - REGISTER_NET_LINKED(ENT_CLIENT_CONTROLPOINT_ICON) -#endif - -#if defined(SVQC) - #include "onslaught.qc" - #ifndef IMPLEMENTATION - #include "sv_controlpoint.qh" - #include "sv_generator.qh" - #else - #include "sv_controlpoint.qc" - #include "sv_generator.qc" - #endif -#elif defined(CSQC) - #ifndef IMPLEMENTATION - #include "cl_controlpoint.qh" - #include "cl_generator.qh" - #else - #include "cl_controlpoint.qc" - #include "cl_generator.qc" - #endif -#endif diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc b/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc index 713c5d7de0..36926b754b 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc +++ b/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc @@ -1,2258 +1 @@ -#ifndef GAMEMODE_ONSLAUGHT_H -#define GAMEMODE_ONSLAUGHT_H - -float autocvar_g_onslaught_point_limit; -void ons_Initialize(); - -REGISTER_MUTATOR(ons, false) -{ - MUTATOR_ONADD - { - if (time > 1) // game loads at time 1 - error("This is a game type and it cannot be added at runtime."); - ons_Initialize(); - - ActivateTeamplay(); - SetLimits(autocvar_g_onslaught_point_limit, autocvar_leadlimit_override, autocvar_timelimit_override, -1); - have_team_spawns = -1; // request team spawns - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // we actually cannot roll back ons_Initialize here - // BUT: we don't need to! If this gets called, adding always - // succeeds. - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This is a game type and it cannot be removed at runtime."); - return -1; - } - - return false; -} - -#ifdef SVQC - -.entity ons_toucher; // player who touched the control point - -// control point / generator constants -const float ONS_CP_THINKRATE = 0.2; -const float GEN_THINKRATE = 1; -#define CPGEN_SPAWN_OFFSET ('0 0 1' * (PL_MAX_CONST.z - 13)) -const vector CPGEN_WAYPOINT_OFFSET = ('0 0 128'); -const vector CPICON_OFFSET = ('0 0 96'); - -// list of generators on the map -entity ons_worldgeneratorlist; -.entity ons_worldgeneratornext; -.entity ons_stalegeneratornext; - -// list of control points on the map -entity ons_worldcplist; -.entity ons_worldcpnext; -.entity ons_stalecpnext; - -// list of links on the map -entity ons_worldlinklist; -.entity ons_worldlinknext; -.entity ons_stalelinknext; - -// definitions -.entity sprite; -.string target2; -.int iscaptured; -.int islinked; -.int isshielded; -.float lasthealth; -.int lastteam; -.int lastshielded; -.int lastcaptured; - -.bool waslinked; - -bool ons_stalemate; - -.float teleport_antispam; - -.bool ons_roundlost = _STAT(ROUNDLOST); - -// waypoint sprites -.entity bot_basewaypoint; // generator waypointsprite - -.bool isgenneighbor[17]; -.bool iscpneighbor[17]; -float ons_notification_time[17]; - -.float ons_overtime_damagedelay; - -.vector ons_deathloc; - -.entity ons_spawn_by; - -// declarations for functions used outside gamemode_onslaught.qc -void ons_Generator_UpdateSprite(entity e); -void ons_ControlPoint_UpdateSprite(entity e); -bool ons_ControlPoint_Attackable(entity cp, int teamnumber); - -// CaptureShield: Prevent capturing or destroying control point/generator if it is not available yet -float ons_captureshield_force; // push force of the shield - -// bot player logic -const int HAVOCBOT_ONS_ROLE_NONE = 0; -const int HAVOCBOT_ONS_ROLE_DEFENSE = 2; -const int HAVOCBOT_ONS_ROLE_ASSISTANT = 4; -const int HAVOCBOT_ONS_ROLE_OFFENSE = 8; - -.entity havocbot_ons_target; - -.int havocbot_role_flags; -.float havocbot_attack_time; - -void havocbot_role_ons_defense(entity this); -void havocbot_role_ons_offense(entity this); -void havocbot_role_ons_assistant(entity this); - -void havocbot_ons_reset_role(entity this); -void havocbot_goalrating_items(entity this, float ratingscale, vector org, float sradius); -void havocbot_goalrating_enemyplayers(entity this, float ratingscale, vector org, float sradius); - -// score rule declarations -const int ST_ONS_CAPS = 1; - -#endif -#endif - -#ifdef IMPLEMENTATION - -#include "sv_controlpoint.qh" -#include "sv_generator.qh" - -bool g_onslaught; - -float autocvar_g_onslaught_teleport_wait; -bool autocvar_g_onslaught_spawn_at_controlpoints; -bool autocvar_g_onslaught_spawn_at_generator; -float autocvar_g_onslaught_cp_proxydecap; -float autocvar_g_onslaught_cp_proxydecap_distance = 512; -float autocvar_g_onslaught_cp_proxydecap_dps = 100; -float autocvar_g_onslaught_spawn_at_controlpoints_chance = 0.5; -float autocvar_g_onslaught_spawn_at_controlpoints_random; -float autocvar_g_onslaught_spawn_at_generator_chance; -float autocvar_g_onslaught_spawn_at_generator_random; -float autocvar_g_onslaught_cp_buildhealth; -float autocvar_g_onslaught_cp_buildtime; -float autocvar_g_onslaught_cp_health; -float autocvar_g_onslaught_cp_regen; -float autocvar_g_onslaught_gen_health; -float autocvar_g_onslaught_shield_force = 100; -float autocvar_g_onslaught_allow_vehicle_touch; -float autocvar_g_onslaught_round_timelimit; -float autocvar_g_onslaught_warmup; -float autocvar_g_onslaught_teleport_radius; -float autocvar_g_onslaught_spawn_choose; -float autocvar_g_onslaught_click_radius; - -void FixSize(entity e); - -// ======================= -// CaptureShield Functions -// ======================= - -bool ons_CaptureShield_Customize(entity this, entity client) -{ - entity e = WaypointSprite_getviewentity(client); - - if(!this.enemy.isshielded && (ons_ControlPoint_Attackable(this.enemy, e.team) > 0 || this.enemy.classname != "onslaught_controlpoint")) { return false; } - if(SAME_TEAM(this, e)) { return false; } - - return true; -} - -void ons_CaptureShield_Touch(entity this, entity toucher) -{ - if(!this.enemy.isshielded && (ons_ControlPoint_Attackable(this.enemy, toucher.team) > 0 || this.enemy.classname != "onslaught_controlpoint")) { return; } - if(!IS_PLAYER(toucher)) { return; } - if(SAME_TEAM(toucher, this)) { return; } - - vector mymid = (this.absmin + this.absmax) * 0.5; - vector theirmid = (toucher.absmin + toucher.absmax) * 0.5; - - Damage(toucher, this, this, 0, DEATH_HURTTRIGGER.m_id, mymid, normalize(theirmid - mymid) * ons_captureshield_force); - - if(IS_REAL_CLIENT(toucher)) - { - play2(toucher, SND(ONS_DAMAGEBLOCKEDBYSHIELD)); - - if(this.enemy.classname == "onslaught_generator") - Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_GENERATOR_SHIELDED); - else - Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_CONTROLPOINT_SHIELDED); - } -} - -void ons_CaptureShield_Reset(entity this) -{ - this.colormap = this.enemy.colormap; - this.team = this.enemy.team; -} - -void ons_CaptureShield_Spawn(entity generator, bool is_generator) -{ - entity shield = new(ons_captureshield); - - shield.enemy = generator; - shield.team = generator.team; - shield.colormap = generator.colormap; - shield.reset = ons_CaptureShield_Reset; - settouch(shield, ons_CaptureShield_Touch); - setcefc(shield, ons_CaptureShield_Customize); - shield.effects = EF_ADDITIVE; - set_movetype(shield, MOVETYPE_NOCLIP); - shield.solid = SOLID_TRIGGER; - shield.avelocity = '7 0 11'; - shield.scale = 1; - shield.model = ((is_generator) ? "models/onslaught/generator_shield.md3" : "models/onslaught/controlpoint_shield.md3"); - - precache_model(shield.model); - setorigin(shield, generator.origin); - _setmodel(shield, shield.model); - setsize(shield, shield.scale * shield.mins, shield.scale * shield.maxs); -} - - -// ========== -// Junk Pile -// ========== - -void setmodel_fixsize(entity e, Model m) -{ - setmodel(e, m); - FixSize(e); -} - -void onslaught_updatelinks() -{ - entity l; - // first check if the game has ended - LOG_DEBUG("--- updatelinks ---"); - // mark generators as being shielded and networked - for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) - { - if (l.iscaptured) - LOG_DEBUG(etos(l), " (generator) belongs to team ", ftos(l.team)); - else - LOG_DEBUG(etos(l), " (generator) is destroyed"); - l.islinked = l.iscaptured; - l.isshielded = l.iscaptured; - l.sprite.SendFlags |= 16; - } - // mark points as shielded and not networked - for(l = ons_worldcplist; l; l = l.ons_worldcpnext) - { - l.islinked = false; - l.isshielded = true; - int i; - for(i = 0; i < 17; ++i) { l.isgenneighbor[i] = false; l.iscpneighbor[i] = false; } - LOG_DEBUG(etos(l), " (point) belongs to team ", ftos(l.team)); - l.sprite.SendFlags |= 16; - } - // flow power outward from the generators through the network - bool stop = false; - while (!stop) - { - stop = true; - for(l = ons_worldlinklist; l; l = l.ons_worldlinknext) - { - // if both points are captured by the same team, and only one of - // them is powered, mark the other one as powered as well - if (l.enemy.iscaptured && l.goalentity.iscaptured) - if (l.enemy.islinked != l.goalentity.islinked) - if(SAME_TEAM(l.enemy, l.goalentity)) - { - if (!l.goalentity.islinked) - { - stop = false; - l.goalentity.islinked = true; - LOG_DEBUG(etos(l), " (link) is marking ", etos(l.goalentity), " (point) because its team matches ", etos(l.enemy), " (point)"); - } - else if (!l.enemy.islinked) - { - stop = false; - l.enemy.islinked = true; - LOG_DEBUG(etos(l), " (link) is marking ", etos(l.enemy), " (point) because its team matches ", etos(l.goalentity), " (point)"); - } - } - } - } - // now that we know which points are powered we can mark their neighbors - // as unshielded if team differs - for(l = ons_worldlinklist; l; l = l.ons_worldlinknext) - { - if (l.goalentity.islinked) - { - if(DIFF_TEAM(l.goalentity, l.enemy)) - { - LOG_DEBUG(etos(l), " (link) is unshielding ", etos(l.enemy), " (point) because its team does not match ", etos(l.goalentity), " (point)"); - l.enemy.isshielded = false; - } - if(l.goalentity.classname == "onslaught_generator") - l.enemy.isgenneighbor[l.goalentity.team] = true; - else - l.enemy.iscpneighbor[l.goalentity.team] = true; - } - if (l.enemy.islinked) - { - if(DIFF_TEAM(l.goalentity, l.enemy)) - { - LOG_DEBUG(etos(l), " (link) is unshielding ", etos(l.goalentity), " (point) because its team does not match ", etos(l.enemy), " (point)"); - l.goalentity.isshielded = false; - } - if(l.enemy.classname == "onslaught_generator") - l.goalentity.isgenneighbor[l.enemy.team] = true; - else - l.goalentity.iscpneighbor[l.enemy.team] = true; - } - } - // now update the generators - for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) - { - if (l.isshielded) - { - LOG_DEBUG(etos(l), " (generator) is shielded"); - l.takedamage = DAMAGE_NO; - l.bot_attack = false; - } - else - { - LOG_DEBUG(etos(l), " (generator) is not shielded"); - l.takedamage = DAMAGE_AIM; - l.bot_attack = true; - } - - ons_Generator_UpdateSprite(l); - } - // now update the takedamage and alpha variables on control point icons - for(l = ons_worldcplist; l; l = l.ons_worldcpnext) - { - if (l.isshielded) - { - LOG_DEBUG(etos(l), " (point) is shielded"); - if (l.goalentity) - { - l.goalentity.takedamage = DAMAGE_NO; - l.goalentity.bot_attack = false; - } - } - else - { - LOG_DEBUG(etos(l), " (point) is not shielded"); - if (l.goalentity) - { - l.goalentity.takedamage = DAMAGE_AIM; - l.goalentity.bot_attack = true; - } - } - ons_ControlPoint_UpdateSprite(l); - } - FOREACH_ENTITY_CLASS("ons_captureshield", true, - { - it.team = it.enemy.team; - it.colormap = it.enemy.colormap; - }); -} - - -// =================== -// Main Link Functions -// =================== - -bool ons_Link_Send(entity this, entity to, int sendflags) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_RADARLINK); - WriteByte(MSG_ENTITY, sendflags); - if(sendflags & 1) - { - WriteCoord(MSG_ENTITY, this.goalentity.origin_x); - WriteCoord(MSG_ENTITY, this.goalentity.origin_y); - WriteCoord(MSG_ENTITY, this.goalentity.origin_z); - } - if(sendflags & 2) - { - WriteCoord(MSG_ENTITY, this.enemy.origin_x); - WriteCoord(MSG_ENTITY, this.enemy.origin_y); - WriteCoord(MSG_ENTITY, this.enemy.origin_z); - } - if(sendflags & 4) - { - WriteByte(MSG_ENTITY, this.clientcolors); // which is goalentity's color + enemy's color * 16 - } - return true; -} - -void ons_Link_CheckUpdate(entity this) -{ - // TODO check if the two sides have moved (currently they won't move anyway) - float cc = 0, cc1 = 0, cc2 = 0; - - if(this.goalentity.islinked || this.goalentity.iscaptured) { cc1 = (this.goalentity.team - 1) * 0x01; } - if(this.enemy.islinked || this.enemy.iscaptured) { cc2 = (this.enemy.team - 1) * 0x10; } - - cc = cc1 + cc2; - - if(cc != this.clientcolors) - { - this.clientcolors = cc; - this.SendFlags |= 4; - } - - this.nextthink = time; -} - -void ons_DelayedLinkSetup(entity this) -{ - this.goalentity = find(NULL, targetname, this.target); - this.enemy = find(NULL, targetname, this.target2); - if(!this.goalentity) { objerror(this, "can not find target\n"); } - if(!this.enemy) { objerror(this, "can not find target2\n"); } - - LOG_DEBUG(etos(this.goalentity), " linked with ", etos(this.enemy)); - this.SendFlags |= 3; - setthink(this, ons_Link_CheckUpdate); - this.nextthink = time; -} - - -// ============================= -// Main Control Point Functions -// ============================= - -int ons_ControlPoint_CanBeLinked(entity cp, int teamnumber) -{ - if(cp.isgenneighbor[teamnumber]) { return 2; } - if(cp.iscpneighbor[teamnumber]) { return 1; } - - return 0; -} - -int ons_ControlPoint_Attackable(entity cp, int teamnumber) - // -2: SAME TEAM, attackable by enemy! - // -1: SAME TEAM! - // 0: off limits - // 1: attack it - // 2: touch it - // 3: attack it (HIGH PRIO) - // 4: touch it (HIGH PRIO) -{ - int a; - - if(cp.isshielded) - { - return 0; - } - else if(cp.goalentity) - { - // if there's already an icon built, nothing happens - if(cp.team == teamnumber) - { - a = ons_ControlPoint_CanBeLinked(cp, teamnumber); - if(a) // attackable by enemy? - return -2; // EMERGENCY! - return -1; - } - // we know it can be linked, so no need to check - // but... - a = ons_ControlPoint_CanBeLinked(cp, teamnumber); - if(a == 2) // near our generator? - return 3; // EMERGENCY! - return 1; - } - else - { - // free point - if(ons_ControlPoint_CanBeLinked(cp, teamnumber)) - { - a = ons_ControlPoint_CanBeLinked(cp, teamnumber); // why was this here NUM_TEAM_1 + NUM_TEAM_2 - t - if(a == 2) - return 4; // GET THIS ONE NOW! - else - return 2; // TOUCH ME - } - } - return 0; -} - -void ons_ControlPoint_Icon_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) -{ - if(damage <= 0) { return; } - - if (this.owner.isshielded) - { - // this is protected by a shield, so ignore the damage - if (time > this.pain_finished) - if (IS_PLAYER(attacker)) - { - play2(attacker, SND(ONS_DAMAGEBLOCKEDBYSHIELD)); - this.pain_finished = time + 1; - attacker.typehitsound += 1; // play both sounds (shield is way too quiet) - } - - return; - } - - if(IS_PLAYER(attacker)) - if(time - ons_notification_time[this.team] > 10) - { - play2team(this.team, SND(ONS_CONTROLPOINT_UNDERATTACK)); - ons_notification_time[this.team] = time; - } - - this.health = this.health - damage; - if(this.owner.iscaptured) - WaypointSprite_UpdateHealth(this.owner.sprite, this.health); - else - WaypointSprite_UpdateBuildFinished(this.owner.sprite, time + (this.max_health - this.health) / (this.count / ONS_CP_THINKRATE)); - this.pain_finished = time + 1; - // particles on every hit - pointparticles(EFFECT_SPARKS, hitloc, force*-1, 1); - //sound on every hit - if (random() < 0.5) - sound(this, CH_TRIGGER, SND_ONS_HIT1, VOL_BASE+0.3, ATTEN_NORM); - else - sound(this, CH_TRIGGER, SND_ONS_HIT2, VOL_BASE+0.3, ATTEN_NORM); - - if (this.health < 0) - { - sound(this, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM); - pointparticles(EFFECT_ROCKET_EXPLODE, this.origin, '0 0 0', 1); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_ONSLAUGHT_CPDESTROYED), this.owner.message, attacker.netname); - - PlayerScore_Add(attacker, SP_ONS_TAKES, 1); - PlayerScore_Add(attacker, SP_SCORE, 10); - - this.owner.goalentity = NULL; - this.owner.islinked = false; - this.owner.iscaptured = false; - this.owner.team = 0; - this.owner.colormap = 1024; - - WaypointSprite_UpdateMaxHealth(this.owner.sprite, 0); - - onslaught_updatelinks(); - - // Use targets now (somebody make sure this is in the right place..) - SUB_UseTargets(this.owner, this, NULL); - - this.owner.waslinked = this.owner.islinked; - if(this.owner.model != "models/onslaught/controlpoint_pad.md3") - setmodel_fixsize(this.owner, MDL_ONS_CP_PAD1); - //setsize(this, '-32 -32 0', '32 32 8'); - - delete(this); - } - - this.SendFlags |= CPSF_STATUS; -} - -void ons_ControlPoint_Icon_Think(entity this) -{ - this.nextthink = time + ONS_CP_THINKRATE; - - if(autocvar_g_onslaught_cp_proxydecap) - { - int _enemy_count = 0; - int _friendly_count = 0; - - FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), { - if(vdist(it.origin - this.origin, <, autocvar_g_onslaught_cp_proxydecap_distance)) - { - if(SAME_TEAM(it, this)) - ++_friendly_count; - else - ++_enemy_count; - } - }); - - _friendly_count = _friendly_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE); - _enemy_count = _enemy_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE); - - this.health = bound(0, this.health + (_friendly_count - _enemy_count), this.max_health); - this.SendFlags |= CPSF_STATUS; - if(this.health <= 0) - { - ons_ControlPoint_Icon_Damage(this, this, this, 1, 0, this.origin, '0 0 0'); - return; - } - } - - if (time > this.pain_finished + 5) - { - if(this.health < this.max_health) - { - this.health = this.health + this.count; - if (this.health >= this.max_health) - this.health = this.max_health; - WaypointSprite_UpdateHealth(this.owner.sprite, this.health); - } - } - - if(this.owner.islinked != this.owner.waslinked) - { - // unteam the spawnpoint if needed - int t = this.owner.team; - if(!this.owner.islinked) - this.owner.team = 0; - - SUB_UseTargets(this.owner, this, NULL); - - this.owner.team = t; - - this.owner.waslinked = this.owner.islinked; - } - - // damaged fx - if(random() < 0.6 - this.health / this.max_health) - { - Send_Effect(EFFECT_ELECTRIC_SPARKS, this.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1); - - if(random() > 0.8) - sound(this, CH_PAIN, SND_ONS_SPARK1, VOL_BASE, ATTEN_NORM); - else if (random() > 0.5) - sound(this, CH_PAIN, SND_ONS_SPARK2, VOL_BASE, ATTEN_NORM); - } -} - -void ons_ControlPoint_Icon_BuildThink(entity this) -{ - int a; - - this.nextthink = time + ONS_CP_THINKRATE; - - // only do this if there is power - a = ons_ControlPoint_CanBeLinked(this.owner, this.owner.team); - if(!a) - return; - - this.health = this.health + this.count; - - this.SendFlags |= CPSF_STATUS; - - if (this.health >= this.max_health) - { - this.health = this.max_health; - this.count = autocvar_g_onslaught_cp_regen * ONS_CP_THINKRATE; // slow repair rate from now on - setthink(this, ons_ControlPoint_Icon_Think); - sound(this, CH_TRIGGER, SND_ONS_CONTROLPOINT_BUILT, VOL_BASE, ATTEN_NORM); - this.owner.iscaptured = true; - this.solid = SOLID_BBOX; - - Send_Effect(EFFECT_CAP(this.owner.team), this.owner.origin, '0 0 0', 1); - - WaypointSprite_UpdateMaxHealth(this.owner.sprite, this.max_health); - WaypointSprite_UpdateHealth(this.owner.sprite, this.health); - - if(IS_PLAYER(this.owner.ons_toucher)) - { - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ONSLAUGHT_CAPTURE, this.owner.ons_toucher.netname, this.owner.message); - Send_Notification(NOTIF_ALL_EXCEPT, this.owner.ons_toucher, MSG_CENTER, APP_TEAM_NUM(this.owner.ons_toucher.team, CENTER_ONS_CAPTURE), this.owner.message); - Send_Notification(NOTIF_ONE, this.owner.ons_toucher, MSG_CENTER, CENTER_ONS_CAPTURE, this.owner.message); - PlayerScore_Add(this.owner.ons_toucher, SP_ONS_CAPS, 1); - PlayerTeamScore_AddScore(this.owner.ons_toucher, 10); - } - - this.owner.ons_toucher = NULL; - - onslaught_updatelinks(); - - // Use targets now (somebody make sure this is in the right place..) - SUB_UseTargets(this.owner, this, NULL); - - this.SendFlags |= CPSF_SETUP; - } - if(this.owner.model != MDL_ONS_CP_PAD2.model_str()) - setmodel_fixsize(this.owner, MDL_ONS_CP_PAD2); - - if(random() < 0.9 - this.health / this.max_health) - Send_Effect(EFFECT_RAGE, this.origin + 10 * randomvec(), '0 0 -1', 1); -} - -void onslaught_controlpoint_icon_link(entity e, void(entity this) spawnproc); - -void ons_ControlPoint_Icon_Spawn(entity cp, entity player) -{ - entity e = new(onslaught_controlpoint_icon); - - setsize(e, CPICON_MIN, CPICON_MAX); - setorigin(e, cp.origin + CPICON_OFFSET); - - e.owner = cp; - e.max_health = autocvar_g_onslaught_cp_health; - e.health = autocvar_g_onslaught_cp_buildhealth; - e.solid = SOLID_NOT; - e.takedamage = DAMAGE_AIM; - e.bot_attack = true; - e.event_damage = ons_ControlPoint_Icon_Damage; - e.team = player.team; - e.colormap = 1024 + (e.team - 1) * 17; - e.count = (e.max_health - e.health) * ONS_CP_THINKRATE / autocvar_g_onslaught_cp_buildtime; // how long it takes to build - - sound(e, CH_TRIGGER, SND_ONS_CONTROLPOINT_BUILD, VOL_BASE, ATTEN_NORM); - - cp.goalentity = e; - cp.team = e.team; - cp.colormap = e.colormap; - - Send_Effect(EFFECT_FLAG_TOUCH(player.team), e.origin, '0 0 0', 1); - - WaypointSprite_UpdateBuildFinished(cp.sprite, time + (e.max_health - e.health) / (e.count / ONS_CP_THINKRATE)); - WaypointSprite_UpdateRule(cp.sprite,cp.team,SPRITERULE_TEAMPLAY); - cp.sprite.SendFlags |= 16; - - onslaught_controlpoint_icon_link(e, ons_ControlPoint_Icon_BuildThink); -} - -entity ons_ControlPoint_Waypoint(entity e) -{ - if(e.team) - { - int a = ons_ControlPoint_Attackable(e, e.team); - - if(a == -2) { return WP_OnsCPDefend; } // defend now - if(a == -1 || a == 1 || a == 2) { return WP_OnsCP; } // touch - if(a == 3 || a == 4) { return WP_OnsCPAttack; } // attack - } - else - return WP_OnsCP; - - return WP_Null; -} - -void ons_ControlPoint_UpdateSprite(entity e) -{ - entity s1 = ons_ControlPoint_Waypoint(e); - WaypointSprite_UpdateSprites(e.sprite, s1, s1, s1); - - bool sh; - sh = !(ons_ControlPoint_CanBeLinked(e, NUM_TEAM_1) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_2) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_3) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_4)); - - if(e.lastteam != e.team + 2 || e.lastshielded != sh || e.iscaptured != e.lastcaptured) - { - if(e.iscaptured) // don't mess up build bars! - { - if(sh) - { - WaypointSprite_UpdateMaxHealth(e.sprite, 0); - } - else - { - WaypointSprite_UpdateMaxHealth(e.sprite, e.goalentity.max_health); - WaypointSprite_UpdateHealth(e.sprite, e.goalentity.health); - } - } - if(e.lastshielded) - { - if(e.team) - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, 0.5 * colormapPaletteColor(e.team - 1, false)); - else - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.5 0.5 0.5'); - } - else - { - if(e.team) - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, colormapPaletteColor(e.team - 1, false)); - else - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.75 0.75 0.75'); - } - WaypointSprite_Ping(e.sprite); - - e.lastteam = e.team + 2; - e.lastshielded = sh; - e.lastcaptured = e.iscaptured; - } -} - -void ons_ControlPoint_Touch(entity this, entity toucher) -{ - int attackable; - - if(IS_VEHICLE(toucher) && toucher.owner) - if(autocvar_g_onslaught_allow_vehicle_touch) - toucher = toucher.owner; - else - return; - - if(!IS_PLAYER(toucher)) { return; } - if(STAT(FROZEN, toucher)) { return; } - if(IS_DEAD(toucher)) { return; } - - if ( SAME_TEAM(this,toucher) ) - if ( this.iscaptured ) - { - if(time <= toucher.teleport_antispam) - Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT_ANTISPAM, rint(toucher.teleport_antispam - time)); - else - Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT); - } - - attackable = ons_ControlPoint_Attackable(this, toucher.team); - if(attackable != 2 && attackable != 4) - return; - // we've verified that this player has a legitimate claim to this point, - // so start building the captured point icon (which only captures this - // point if it successfully builds without being destroyed first) - ons_ControlPoint_Icon_Spawn(this, toucher); - - this.ons_toucher = toucher; - - onslaught_updatelinks(); -} - -void ons_ControlPoint_Think(entity this) -{ - this.nextthink = time + ONS_CP_THINKRATE; - CSQCMODEL_AUTOUPDATE(this); -} - -void ons_ControlPoint_Reset(entity this) -{ - if(this.goalentity) - delete(this.goalentity); - - this.goalentity = NULL; - this.team = 0; - this.colormap = 1024; - this.iscaptured = false; - this.islinked = false; - this.isshielded = true; - setthink(this, ons_ControlPoint_Think); - this.ons_toucher = NULL; - this.nextthink = time + ONS_CP_THINKRATE; - setmodel_fixsize(this, MDL_ONS_CP_PAD1); - - WaypointSprite_UpdateMaxHealth(this.sprite, 0); - WaypointSprite_UpdateRule(this.sprite,this.team,SPRITERULE_TEAMPLAY); - - onslaught_updatelinks(); - - SUB_UseTargets(this, this, NULL); // to reset the structures, playerspawns etc. - - CSQCMODEL_AUTOUPDATE(this); -} - -void ons_DelayedControlPoint_Setup(entity this) -{ - onslaught_updatelinks(); - - // captureshield setup - ons_CaptureShield_Spawn(this, false); - - CSQCMODEL_AUTOINIT(this); -} - -void ons_ControlPoint_Setup(entity cp) -{ - // main setup - cp.ons_worldcpnext = ons_worldcplist; // link control point into ons_worldcplist - ons_worldcplist = cp; - - cp.netname = "Control point"; - cp.team = 0; - cp.solid = SOLID_BBOX; - set_movetype(cp, MOVETYPE_NONE); - settouch(cp, ons_ControlPoint_Touch); - setthink(cp, ons_ControlPoint_Think); - cp.nextthink = time + ONS_CP_THINKRATE; - cp.reset = ons_ControlPoint_Reset; - cp.colormap = 1024; - cp.iscaptured = false; - cp.islinked = false; - cp.isshielded = true; - - if(cp.message == "") { cp.message = "a"; } - - // appearence - setmodel_fixsize(cp, MDL_ONS_CP_PAD1); - - // control point placement - if((cp.spawnflags & 1) || cp.noalign) // don't drop to floor, just stay at fixed location - { - cp.noalign = true; - set_movetype(cp, MOVETYPE_NONE); - } - else // drop to floor, automatically find a platform and set that as spawn origin - { - setorigin(cp, cp.origin + '0 0 20'); - cp.noalign = false; - droptofloor(cp); - set_movetype(cp, MOVETYPE_TOSS); - } - - // waypointsprites - WaypointSprite_SpawnFixed(WP_Null, cp.origin + CPGEN_WAYPOINT_OFFSET, cp, sprite, RADARICON_NONE); - WaypointSprite_UpdateRule(cp.sprite, cp.team, SPRITERULE_TEAMPLAY); - - InitializeEntity(cp, ons_DelayedControlPoint_Setup, INITPRIO_SETLOCATION); -} - - -// ========================= -// Main Generator Functions -// ========================= - -entity ons_Generator_Waypoint(entity e) -{ - if (e.isshielded) - return WP_OnsGenShielded; - return WP_OnsGen; -} - -void ons_Generator_UpdateSprite(entity e) -{ - entity s1 = ons_Generator_Waypoint(e); - WaypointSprite_UpdateSprites(e.sprite, s1, s1, s1); - - if(e.lastteam != e.team + 2 || e.lastshielded != e.isshielded) - { - e.lastteam = e.team + 2; - e.lastshielded = e.isshielded; - if(e.lastshielded) - { - if(e.team) - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, false)); - else - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.5 0.5 0.5'); - } - else - { - if(e.team) - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, false)); - else - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.75 0.75 0.75'); - } - WaypointSprite_Ping(e.sprite); - } -} - -void ons_GeneratorDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) -{ - if(damage <= 0) { return; } - if(warmup_stage || gameover) { return; } - if(!round_handler_IsRoundStarted()) { return; } - - if (attacker != this) - { - if (this.isshielded) - { - // this is protected by a shield, so ignore the damage - if (time > this.pain_finished) - if (IS_PLAYER(attacker)) - { - play2(attacker, SND(ONS_DAMAGEBLOCKEDBYSHIELD)); - attacker.typehitsound += 1; - this.pain_finished = time + 1; - } - return; - } - if (time > this.pain_finished) - { - this.pain_finished = time + 10; - FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && SAME_TEAM(it, this), Send_Notification(NOTIF_ONE, it, MSG_CENTER, CENTER_GENERATOR_UNDERATTACK)); - play2team(this.team, SND(ONS_GENERATOR_UNDERATTACK)); - } - } - this.health = this.health - damage; - WaypointSprite_UpdateHealth(this.sprite, this.health); - // choose an animation frame based on health - this.frame = 10 * bound(0, (1 - this.health / this.max_health), 1); - // see if the generator is still functional, or dying - if (this.health > 0) - { - this.lasthealth = this.health; - } - else - { - if (attacker == this) - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_ONSLAUGHT_GENDESTROYED_OVERTIME)); - else - { - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_ONSLAUGHT_GENDESTROYED)); - PlayerScore_Add(attacker, SP_SCORE, 100); - } - this.iscaptured = false; - this.islinked = false; - this.isshielded = false; - this.takedamage = DAMAGE_NO; // can't be hurt anymore - this.event_damage = func_null; // won't do anything if hurt - this.count = 0; // reset counter - setthink(this, func_null); - this.nextthink = 0; - //this.think(); // do the first explosion now - - WaypointSprite_UpdateMaxHealth(this.sprite, 0); - WaypointSprite_Ping(this.sprite); - //WaypointSprite_Kill(this.sprite); // can't do this yet, code too poor - - onslaught_updatelinks(); - } - - // Throw some flaming gibs on damage, more damage = more chance for gib - if(random() < damage/220) - { - sound(this, CH_TRIGGER, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); - } - else - { - // particles on every hit - Send_Effect(EFFECT_SPARKS, hitloc, force * -1, 1); - - //sound on every hit - if (random() < 0.5) - sound(this, CH_TRIGGER, SND_ONS_HIT1, VOL_BASE, ATTEN_NORM); - else - sound(this, CH_TRIGGER, SND_ONS_HIT2, VOL_BASE, ATTEN_NORM); - } - - this.SendFlags |= GSF_STATUS; -} - -void ons_GeneratorThink(entity this) -{ - this.nextthink = time + GEN_THINKRATE; - if (!gameover) - { - if(!this.isshielded && this.wait < time) - { - this.wait = time + 5; - FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { - if(SAME_TEAM(it, this)) - { - Send_Notification(NOTIF_ONE, it, MSG_CENTER, CENTER_ONS_NOTSHIELDED_TEAM); - soundto(MSG_ONE, it, CHAN_AUTO, SND(KH_ALARM), VOL_BASE, ATTEN_NONE); // FIXME: unique sound? - } - else - Send_Notification(NOTIF_ONE, it, MSG_CENTER, APP_TEAM_NUM(this.team, CENTER_ONS_NOTSHIELDED)); - }); - } - } -} - -void ons_GeneratorReset(entity this) -{ - this.team = this.team_saved; - this.lasthealth = this.max_health = this.health = autocvar_g_onslaught_gen_health; - this.takedamage = DAMAGE_AIM; - this.bot_attack = true; - this.iscaptured = true; - this.islinked = true; - this.isshielded = true; - this.event_damage = ons_GeneratorDamage; - setthink(this, ons_GeneratorThink); - this.nextthink = time + GEN_THINKRATE; - - Net_LinkEntity(this, false, 0, generator_send); - - this.SendFlags = GSF_SETUP; // just incase - this.SendFlags |= GSF_STATUS; - - WaypointSprite_UpdateMaxHealth(this.sprite, this.max_health); - WaypointSprite_UpdateHealth(this.sprite, this.health); - WaypointSprite_UpdateRule(this.sprite,this.team,SPRITERULE_TEAMPLAY); - - onslaught_updatelinks(); -} - -void ons_DelayedGeneratorSetup(entity this) -{ - // bot waypoints - waypoint_spawnforitem_force(this, this.origin); - this.nearestwaypointtimeout = 0; // activate waypointing again - this.bot_basewaypoint = this.nearestwaypoint; - - // captureshield setup - ons_CaptureShield_Spawn(this, true); - - onslaught_updatelinks(); - - Net_LinkEntity(this, false, 0, generator_send); -} - - -void onslaught_generator_touch(entity this, entity toucher) -{ - if ( IS_PLAYER(toucher) ) - if ( SAME_TEAM(this,toucher) ) - if ( this.iscaptured ) - { - Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT); - } -} - -void ons_GeneratorSetup(entity gen) // called when spawning a generator entity on the map as a spawnfunc -{ - // declarations - int teamnumber = gen.team; - - // main setup - gen.ons_worldgeneratornext = ons_worldgeneratorlist; // link generator into ons_worldgeneratorlist - ons_worldgeneratorlist = gen; - - gen.netname = sprintf("%s generator", Team_ColoredFullName(teamnumber)); - gen.classname = "onslaught_generator"; - gen.solid = SOLID_BBOX; - gen.team_saved = teamnumber; - set_movetype(gen, MOVETYPE_NONE); - gen.lasthealth = gen.max_health = gen.health = autocvar_g_onslaught_gen_health; - gen.takedamage = DAMAGE_AIM; - gen.bot_attack = true; - gen.event_damage = ons_GeneratorDamage; - gen.reset = ons_GeneratorReset; - setthink(gen, ons_GeneratorThink); - gen.nextthink = time + GEN_THINKRATE; - gen.iscaptured = true; - gen.islinked = true; - gen.isshielded = true; - settouch(gen, onslaught_generator_touch); - - // appearence - // model handled by CSQC - setsize(gen, GENERATOR_MIN, GENERATOR_MAX); - setorigin(gen, (gen.origin + CPGEN_SPAWN_OFFSET)); - gen.colormap = 1024 + (teamnumber - 1) * 17; - - // generator placement - droptofloor(gen); - - // waypointsprites - WaypointSprite_SpawnFixed(WP_Null, gen.origin + CPGEN_WAYPOINT_OFFSET, gen, sprite, RADARICON_NONE); - WaypointSprite_UpdateRule(gen.sprite, gen.team, SPRITERULE_TEAMPLAY); - WaypointSprite_UpdateMaxHealth(gen.sprite, gen.max_health); - WaypointSprite_UpdateHealth(gen.sprite, gen.health); - - InitializeEntity(gen, ons_DelayedGeneratorSetup, INITPRIO_SETLOCATION); -} - - -// =============== -// Round Handler -// =============== - -int total_generators; -void Onslaught_count_generators() -{ - entity e; - total_generators = redowned = blueowned = yellowowned = pinkowned = 0; - for(e = ons_worldgeneratorlist; e; e = e.ons_worldgeneratornext) - { - ++total_generators; - redowned += (e.team == NUM_TEAM_1 && e.health > 0); - blueowned += (e.team == NUM_TEAM_2 && e.health > 0); - yellowowned += (e.team == NUM_TEAM_3 && e.health > 0); - pinkowned += (e.team == NUM_TEAM_4 && e.health > 0); - } -} - -int Onslaught_GetWinnerTeam() -{ - int winner_team = 0; - if(redowned > 0) - winner_team = NUM_TEAM_1; - if(blueowned > 0) - { - if(winner_team) return 0; - winner_team = NUM_TEAM_2; - } - if(yellowowned > 0) - { - if(winner_team) return 0; - winner_team = NUM_TEAM_3; - } - if(pinkowned > 0) - { - if(winner_team) return 0; - winner_team = NUM_TEAM_4; - } - if(winner_team) - return winner_team; - return -1; // no generators left? -} - -void nades_Clear(entity e); - -#define ONS_OWNED_GENERATORS() ((redowned > 0) + (blueowned > 0) + (yellowowned > 0) + (pinkowned > 0)) -#define ONS_OWNED_GENERATORS_OK() (ONS_OWNED_GENERATORS() > 1) -bool Onslaught_CheckWinner() -{ - if ((autocvar_timelimit && time > game_starttime + autocvar_timelimit * 60) || (round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)) - { - ons_stalemate = true; - - if (!wpforenemy_announced) - { - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT); - sound(NULL, CH_INFO, SND_ONS_GENERATOR_DECAY, VOL_BASE, ATTEN_NONE); - - wpforenemy_announced = true; - } - - entity tmp_entity; // temporary entity - float d; - for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) if(time >= tmp_entity.ons_overtime_damagedelay) - { - // tmp_entity.max_health / 300 gives 5 minutes of overtime. - // control points reduce the overtime duration. - d = 1; - entity e; - for(e = ons_worldcplist; e; e = e.ons_worldcpnext) - { - if(DIFF_TEAM(e, tmp_entity)) - if(e.islinked) - d = d + 1; - } - - if(autocvar_g_campaign && autocvar__campaign_testrun) - d = d * tmp_entity.max_health; - else - d = d * tmp_entity.max_health / max(30, 60 * autocvar_timelimit_suddendeath); - - Damage(tmp_entity, tmp_entity, tmp_entity, d, DEATH_HURTTRIGGER.m_id, tmp_entity.origin, '0 0 0'); - - tmp_entity.sprite.SendFlags |= 16; - - tmp_entity.ons_overtime_damagedelay = time + 1; - } - } - else { wpforenemy_announced = false; ons_stalemate = false; } - - Onslaught_count_generators(); - - if(ONS_OWNED_GENERATORS_OK()) - return 0; - - int winner_team = Onslaught_GetWinnerTeam(); - - if(winner_team > 0) - { - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(winner_team, CENTER_ROUND_TEAM_WIN)); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_ROUND_TEAM_WIN)); - TeamScore_AddToTeam(winner_team, ST_ONS_CAPS, +1); - } - else if(winner_team == -1) - { - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_TIED); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_TIED); - } - - ons_stalemate = false; - - play2all(SND(CTF_CAPTURE(winner_team))); - - round_handler_Init(7, autocvar_g_onslaught_warmup, autocvar_g_onslaught_round_timelimit); - - FOREACH_CLIENT(IS_PLAYER(it), { - it.ons_roundlost = true; - it.player_blocked = true; - - nades_Clear(it); - }); - - return 1; -} - -bool Onslaught_CheckPlayers() -{ - return 1; -} - -void Onslaught_RoundStart() -{ - entity tmp_entity; - FOREACH_CLIENT(IS_PLAYER(it), it.player_blocked = false); - - for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext) - tmp_entity.sprite.SendFlags |= 16; - - for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) - tmp_entity.sprite.SendFlags |= 16; -} - - -// ================ -// Bot player logic -// ================ - -// NOTE: LEGACY CODE, needs to be re-written! - -void havocbot_goalrating_ons_offenseitems(entity this, float ratingscale, vector org, float sradius) -{ - bool needarmor = false, needweapons = false; - - // Needs armor/health? - if(this.health<100) - needarmor = true; - - // Needs weapons? - int c = 0; - FOREACH(Weapons, it != WEP_Null, { - if(this.weapons & (it.m_wepset)) - if(++c >= 4) - break; - }); - - if(c<4) - needweapons = true; - - if(!needweapons && !needarmor) - return; - - LOG_DEBUG(this.netname, " needs weapons ", ftos(needweapons)); - LOG_DEBUG(this.netname, " needs armor ", ftos(needarmor)); - - // See what is around - FOREACH_ENTITY_FLOAT(bot_pickup, true, - { - // gather health and armor only - if (it.solid) - if ( ((it.health || it.armorvalue) && needarmor) || (it.weapons && needweapons ) ) - if (vdist(it.origin - org, <, sradius)) - { - int t = it.bot_pickupevalfunc(this, it); - if (t > 0) - navigation_routerating(this, it, t * ratingscale, 500); - } - }); -} - -void havocbot_role_ons_setrole(entity this, int role) -{ - LOG_DEBUG(this.netname," switched to "); - switch(role) - { - case HAVOCBOT_ONS_ROLE_DEFENSE: - LOG_DEBUG("defense"); - this.havocbot_role = havocbot_role_ons_defense; - this.havocbot_role_flags = HAVOCBOT_ONS_ROLE_DEFENSE; - this.havocbot_role_timeout = 0; - break; - case HAVOCBOT_ONS_ROLE_ASSISTANT: - LOG_DEBUG("assistant"); - this.havocbot_role = havocbot_role_ons_assistant; - this.havocbot_role_flags = HAVOCBOT_ONS_ROLE_ASSISTANT; - this.havocbot_role_timeout = 0; - break; - case HAVOCBOT_ONS_ROLE_OFFENSE: - LOG_DEBUG("offense"); - this.havocbot_role = havocbot_role_ons_offense; - this.havocbot_role_flags = HAVOCBOT_ONS_ROLE_OFFENSE; - this.havocbot_role_timeout = 0; - break; - } - LOG_DEBUG(""); -} - -void havocbot_goalrating_ons_controlpoints_attack(entity this, float ratingscale) -{ - entity cp, cp1, cp2, best, wp; - float radius, bestvalue; - int c; - bool found; - - // Filter control points - for(cp2 = ons_worldcplist; cp2; cp2 = cp2.ons_worldcpnext) - { - cp2.wpcost = c = 0; - cp2.wpconsidered = false; - - if(cp2.isshielded) - continue; - - // Ignore owned controlpoints - if(!(cp2.isgenneighbor[this.team] || cp2.iscpneighbor[this.team])) - continue; - - // Count team mates interested in this control point - // (easier and cleaner than keeping counters per cp and teams) - FOREACH_CLIENT(IS_PLAYER(it), { - if(SAME_TEAM(it, this)) - if(it.havocbot_role_flags & HAVOCBOT_ONS_ROLE_OFFENSE) - if(it.havocbot_ons_target == cp2) - ++c; - }); - - // NOTE: probably decrease the cost of attackable control points - cp2.wpcost = c; - cp2.wpconsidered = true; - } - - // We'll consider only the best case - bestvalue = 99999999999; - cp = NULL; - for(cp1 = ons_worldcplist; cp1; cp1 = cp1.ons_worldcpnext) - { - if (!cp1.wpconsidered) - continue; - - if(cp1.wpcost this.havocbot_role_timeout) - { - havocbot_ons_reset_role(this); - return; - } - - if(this.havocbot_attack_time>time) - return; - - if (this.bot_strategytime < time) - { - navigation_goalrating_start(this); - havocbot_goalrating_enemyplayers(this, 20000, this.origin, 650); - if(!havocbot_goalrating_ons_generator_attack(this, 20000)) - havocbot_goalrating_ons_controlpoints_attack(this, 20000); - havocbot_goalrating_ons_offenseitems(this, 10000, this.origin, 10000); - navigation_goalrating_end(this); - - this.bot_strategytime = time + autocvar_bot_ai_strategyinterval; - } -} - -void havocbot_role_ons_assistant(entity this) -{ - havocbot_ons_reset_role(this); -} - -void havocbot_role_ons_defense(entity this) -{ - havocbot_ons_reset_role(this); -} - -void havocbot_ons_reset_role(entity this) -{ - if(IS_DEAD(this)) - return; - - this.havocbot_ons_target = NULL; - - // TODO: Defend control points or generator if necessary - - havocbot_role_ons_setrole(this, HAVOCBOT_ONS_ROLE_OFFENSE); -} - - -/* - * Find control point or generator owned by the same team self which is nearest to pos - * if max_dist is positive, only control points within this range will be considered - */ -entity ons_Nearest_ControlPoint(entity this, vector pos, float max_dist) -{ - entity closest_target = NULL; - FOREACH_ENTITY_CLASS("onslaught_controlpoint", true, - { - if(SAME_TEAM(it, this)) - if(it.iscaptured) - if(max_dist <= 0 || vdist(it.origin - pos, <=, max_dist)) - if(vlen2(it.origin - pos) <= vlen2(closest_target.origin - pos) || closest_target == NULL) - closest_target = it; - }); - FOREACH_ENTITY_CLASS("onslaught_generator", true, - { - if(SAME_TEAM(it, this)) - if(max_dist <= 0 || vdist(it.origin - pos, <, max_dist)) - if(vlen2(it.origin - pos) <= vlen2(closest_target.origin - pos) || closest_target == NULL) - closest_target = it; - }); - - return closest_target; -} - -/* - * Find control point or generator owned by the same team self which is nearest to pos - * if max_dist is positive, only control points within this range will be considered - * This function only check distances on the XY plane, disregarding Z - */ -entity ons_Nearest_ControlPoint_2D(entity this, vector pos, float max_dist) -{ - entity closest_target = NULL; - vector delta; - float smallest_distance = 0, distance; - - FOREACH_ENTITY_CLASS("onslaught_controlpoint", true, - { - delta = it.origin - pos; - delta_z = 0; - distance = vlen(delta); - - if(SAME_TEAM(it, this)) - if(it.iscaptured) - if(max_dist <= 0 || distance <= max_dist) - if(closest_target == NULL || distance <= smallest_distance ) - { - closest_target = it; - smallest_distance = distance; - } - }); - FOREACH_ENTITY_CLASS("onslaught_generator", true, - { - delta = it.origin - pos; - delta_z = 0; - distance = vlen(delta); - - if(SAME_TEAM(it, this)) - if(max_dist <= 0 || distance <= max_dist) - if(closest_target == NULL || distance <= smallest_distance ) - { - closest_target = it; - smallest_distance = distance; - } - }); - - return closest_target; -} -/** - * find the number of control points and generators in the same team as this - */ -int ons_Count_SelfControlPoints(entity this) -{ - int n = 0; - FOREACH_ENTITY_CLASS("onslaught_controlpoint", true, - { - if(SAME_TEAM(it, this)) - if(it.iscaptured) - n++; - }); - FOREACH_ENTITY_CLASS("onslaught_generator", true, - { - if(SAME_TEAM(it, this)) - n++; - }); - return n; -} - -/** - * Teleport player to a random position near tele_target - * if tele_effects is true, teleport sound+particles are created - * return false on failure - */ -bool ons_Teleport(entity player, entity tele_target, float range, bool tele_effects) -{ - if ( !tele_target ) - return false; - - int i; - vector loc; - float theta; - // narrow the range for each iteration to increase chances that a spawnpoint - // can be found even if there's little room around the control point - float iteration_scale = 1; - for(i = 0; i < 16; ++i) - { - iteration_scale -= i / 16; - theta = random() * 2 * M_PI; - loc_y = sin(theta); - loc_x = cos(theta); - loc_z = 0; - loc *= random() * range * iteration_scale; - - loc += tele_target.origin + '0 0 128' * iteration_scale; - - tracebox(loc, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), loc, MOVE_NORMAL, player); - if(trace_fraction == 1.0 && !trace_startsolid) - { - traceline(tele_target.origin, loc, MOVE_NOMONSTERS, tele_target); // double check to make sure we're not spawning outside the NULL - if(trace_fraction == 1.0 && !trace_startsolid) - { - if ( tele_effects ) - { - Send_Effect(EFFECT_TELEPORT, player.origin, '0 0 0', 1); - sound (player, CH_TRIGGER, SND_TELEPORT, VOL_BASE, ATTEN_NORM); - } - setorigin(player, loc); - player.angles = '0 1 0' * ( theta * RAD2DEG + 180 ); - makevectors(player.angles); - player.fixangle = true; - player.teleport_antispam = time + autocvar_g_onslaught_teleport_wait; - - if ( tele_effects ) - Send_Effect(EFFECT_TELEPORT, player.origin + v_forward * 32, '0 0 0', 1); - return true; - } - } - } - - return false; -} - -// ============== -// Hook Functions -// ============== - -MUTATOR_HOOKFUNCTION(ons, reset_map_global) -{ - FOREACH_CLIENT(IS_PLAYER(it), { - it.ons_roundlost = false; - it.ons_deathloc = '0 0 0'; - PutClientInServer(it); - }); - return false; -} - -MUTATOR_HOOKFUNCTION(ons, ClientDisconnect) -{ - entity player = M_ARGV(0, entity); - - player.ons_deathloc = '0 0 0'; -} - -MUTATOR_HOOKFUNCTION(ons, MakePlayerObserver) -{ - entity player = M_ARGV(0, entity); - - player.ons_deathloc = '0 0 0'; -} - -MUTATOR_HOOKFUNCTION(ons, PlayerSpawn) -{ - entity player = M_ARGV(0, entity); - - if(!round_handler_IsRoundStarted()) - { - player.player_blocked = true; - return false; - } - - entity l; - for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) - { - l.sprite.SendFlags |= 16; - } - for(l = ons_worldcplist; l; l = l.ons_worldcpnext) - { - l.sprite.SendFlags |= 16; - } - - if(ons_stalemate) { Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT); } - - if ( autocvar_g_onslaught_spawn_choose ) - if ( player.ons_spawn_by ) - if ( ons_Teleport(player,player.ons_spawn_by,autocvar_g_onslaught_teleport_radius,false) ) - { - player.ons_spawn_by = NULL; - return false; - } - - if(autocvar_g_onslaught_spawn_at_controlpoints) - if(random() <= autocvar_g_onslaught_spawn_at_controlpoints_chance) - { - float random_target = autocvar_g_onslaught_spawn_at_controlpoints_random; - entity tmp_entity, closest_target = NULL; - vector spawn_loc = player.ons_deathloc; - - // new joining player or round reset, don't bother checking - if(spawn_loc == '0 0 0') { return false; } - - if(random_target) { RandomSelection_Init(); } - - for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext) - { - if(SAME_TEAM(tmp_entity, player)) - if(random_target) - RandomSelection_Add(tmp_entity, 0, string_null, 1, 1); - else if(vlen2(tmp_entity.origin - spawn_loc) <= vlen2(closest_target.origin - spawn_loc) || closest_target == NULL) - closest_target = tmp_entity; - } - - if(random_target) { closest_target = RandomSelection_chosen_ent; } - - if(closest_target) - { - float i; - vector loc; - float iteration_scale = 1; - for(i = 0; i < 10; ++i) - { - iteration_scale -= i / 10; - loc = closest_target.origin + '0 0 96' * iteration_scale; - loc += ('0 1 0' * random()) * 128 * iteration_scale; - tracebox(loc, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), loc, MOVE_NORMAL, player); - if(trace_fraction == 1.0 && !trace_startsolid) - { - traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the NULL - if(trace_fraction == 1.0 && !trace_startsolid) - { - setorigin(player, loc); - player.angles = normalize(loc - closest_target.origin) * RAD2DEG; - return false; - } - } - } - } - } - - if(autocvar_g_onslaught_spawn_at_generator) - if(random() <= autocvar_g_onslaught_spawn_at_generator_chance) - { - float random_target = autocvar_g_onslaught_spawn_at_generator_random; - entity tmp_entity, closest_target = NULL; - vector spawn_loc = player.ons_deathloc; - - // new joining player or round reset, don't bother checking - if(spawn_loc == '0 0 0') { return false; } - - if(random_target) { RandomSelection_Init(); } - - for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) - { - if(random_target) - RandomSelection_Add(tmp_entity, 0, string_null, 1, 1); - else - { - if(SAME_TEAM(tmp_entity, player)) - if(vlen2(tmp_entity.origin - spawn_loc) <= vlen2(closest_target.origin - spawn_loc) || closest_target == NULL) - closest_target = tmp_entity; - } - } - - if(random_target) { closest_target = RandomSelection_chosen_ent; } - - if(closest_target) - { - float i; - vector loc; - float iteration_scale = 1; - for(i = 0; i < 10; ++i) - { - iteration_scale -= i / 10; - loc = closest_target.origin + '0 0 128' * iteration_scale; - loc += ('0 1 0' * random()) * 256 * iteration_scale; - tracebox(loc, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), loc, MOVE_NORMAL, player); - if(trace_fraction == 1.0 && !trace_startsolid) - { - traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the NULL - if(trace_fraction == 1.0 && !trace_startsolid) - { - setorigin(player, loc); - player.angles = normalize(loc - closest_target.origin) * RAD2DEG; - return false; - } - } - } - } - } - - return false; -} - -MUTATOR_HOOKFUNCTION(ons, PlayerDies) -{ - entity frag_target = M_ARGV(2, entity); - - frag_target.ons_deathloc = frag_target.origin; - entity l; - for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) - { - l.sprite.SendFlags |= 16; - } - for(l = ons_worldcplist; l; l = l.ons_worldcpnext) - { - l.sprite.SendFlags |= 16; - } - - if ( autocvar_g_onslaught_spawn_choose ) - if ( ons_Count_SelfControlPoints(frag_target) > 1 ) - stuffcmd(frag_target, "qc_cmd_cl hud clickradar\n"); - - return false; -} - -MUTATOR_HOOKFUNCTION(ons, MonsterMove) -{ - entity mon = M_ARGV(0, entity); - - entity e = find(NULL, targetname, mon.target); - if (e != NULL) - mon.team = e.team; -} - -void ons_MonsterSpawn_Delayed(entity this) -{ - entity own = this.owner; - - if(!own) { delete(this); return; } - - if(own.targetname) - { - entity e = find(NULL, target, own.targetname); - if(e != NULL) - { - own.team = e.team; - - own.use(own, e, NULL); - } - } - - delete(this); -} - -MUTATOR_HOOKFUNCTION(ons, MonsterSpawn) -{ - entity mon = M_ARGV(0, entity); - - entity e = spawn(); - e.owner = mon; - InitializeEntity(e, ons_MonsterSpawn_Delayed, INITPRIO_FINDTARGET); -} - -void ons_TurretSpawn_Delayed(entity this) -{ - entity own = this.owner; - - if(!own) { delete(this); return; } - - if(own.targetname) - { - entity e = find(NULL, target, own.targetname); - if(e != NULL) - { - own.team = e.team; - own.active = ACTIVE_NOT; - - own.use(own, e, NULL); - } - } - - delete(this); -} - -MUTATOR_HOOKFUNCTION(ons, TurretSpawn) -{ - entity turret = M_ARGV(0, entity); - - entity e = spawn(); - e.owner = turret; - InitializeEntity(e, ons_TurretSpawn_Delayed, INITPRIO_FINDTARGET); - - return false; -} - -MUTATOR_HOOKFUNCTION(ons, HavocBot_ChooseRole) -{ - entity bot = M_ARGV(0, entity); - - havocbot_ons_reset_role(bot); - return true; -} - -MUTATOR_HOOKFUNCTION(ons, GetTeamCount) -{ - // onslaught is special - for(entity tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) - { - switch(tmp_entity.team) - { - case NUM_TEAM_1: c1 = 0; break; - case NUM_TEAM_2: c2 = 0; break; - case NUM_TEAM_3: c3 = 0; break; - case NUM_TEAM_4: c4 = 0; break; - } - } - - return true; -} - -MUTATOR_HOOKFUNCTION(ons, SpectateCopy) -{ - entity spectatee = M_ARGV(0, entity); - entity client = M_ARGV(1, entity); - - client.ons_roundlost = spectatee.ons_roundlost; // make spectators see it too -} - -MUTATOR_HOOKFUNCTION(ons, SV_ParseClientCommand) -{ - if(MUTATOR_RETURNVALUE) // command was already handled? - return false; - - entity player = M_ARGV(0, entity); - string cmd_name = M_ARGV(1, string); - int cmd_argc = M_ARGV(2, int); - - if ( cmd_name == "ons_spawn" ) - { - vector pos = player.origin; - if(cmd_argc > 1) - pos_x = stof(argv(1)); - if(cmd_argc > 2) - pos_y = stof(argv(2)); - if(cmd_argc > 3) - pos_z = stof(argv(3)); - - if ( IS_PLAYER(player) ) - { - if ( !STAT(FROZEN, player) ) - { - entity source_point = ons_Nearest_ControlPoint(player, player.origin, autocvar_g_onslaught_teleport_radius); - - if ( !source_point && player.health > 0 ) - { - sprint(player, "\nYou need to be next to a control point\n"); - return true; - } - - - entity closest_target = ons_Nearest_ControlPoint_2D(player, pos, autocvar_g_onslaught_click_radius); - - if ( closest_target == NULL ) - { - sprint(player, "\nNo control point found\n"); - return true; - } - - if ( player.health <= 0 ) - { - player.ons_spawn_by = closest_target; - player.respawn_flags = player.respawn_flags | RESPAWN_FORCE; - } - else - { - if ( source_point == closest_target ) - { - sprint(player, "\nTeleporting to the same point\n"); - return true; - } - - if ( !ons_Teleport(player,closest_target,autocvar_g_onslaught_teleport_radius,true) ) - sprint(player, "\nUnable to teleport there\n"); - } - - return true; - } - - sprint(player, "\nNo teleportation for you\n"); - } - - return true; - } - return false; -} - -MUTATOR_HOOKFUNCTION(ons, PlayerUseKey) -{ - if(MUTATOR_RETURNVALUE || gameover) { return false; } - - entity player = M_ARGV(0, entity); - - if((time > player.teleport_antispam) && (!IS_DEAD(player)) && !player.vehicle) - { - entity source_point = ons_Nearest_ControlPoint(player, player.origin, autocvar_g_onslaught_teleport_radius); - if ( source_point ) - { - stuffcmd(player, "qc_cmd_cl hud clickradar\n"); - return true; - } - } -} - -MUTATOR_HOOKFUNCTION(ons, PlayHitsound) -{ - entity frag_victim = M_ARGV(0, entity); - - return (frag_victim.classname == "onslaught_generator" && !frag_victim.isshielded) - || (frag_victim.classname == "onslaught_controlpoint_icon" && !frag_victim.owner.isshielded); -} - -MUTATOR_HOOKFUNCTION(ons, SendWaypoint) -{ - entity wp = M_ARGV(0, entity); - entity to = M_ARGV(1, entity); - int sf = M_ARGV(2, int); - int wp_flag = M_ARGV(3, int); - - if(sf & 16) - { - if(wp.owner.classname == "onslaught_controlpoint") - { - entity wp_owner = wp.owner; - entity e = WaypointSprite_getviewentity(to); - if(SAME_TEAM(e, wp_owner) && wp_owner.goalentity.health >= wp_owner.goalentity.max_health) { wp_flag |= 2; } - if(!ons_ControlPoint_Attackable(wp_owner, e.team)) { wp_flag |= 2; } - } - if(wp.owner.classname == "onslaught_generator") - { - entity wp_owner = wp.owner; - if(wp_owner.isshielded && wp_owner.health >= wp_owner.max_health) { wp_flag |= 2; } - if(wp_owner.health <= 0) { wp_flag |= 2; } - } - } - - M_ARGV(3, int) = wp_flag; -} - -MUTATOR_HOOKFUNCTION(ons, TurretValidateTarget) -{ - entity turret_target = M_ARGV(1, entity); - - if(substring(turret_target.classname, 0, 10) == "onslaught_") // don't attack onslaught targets, that's the player's job! - { - M_ARGV(3, float) = -3; - return true; - } - - return false; -} - -MUTATOR_HOOKFUNCTION(ons, TurretThink) -{ - entity turret = M_ARGV(0, entity); - - // ONS uses somewhat backwards linking. - if(turret.target) - { - entity e = find(NULL, targetname, turret.target); - if (e != NULL) - turret.team = e.team; - } - - if(turret.team != turret.tur_head.team) - turret_respawn(turret); -} - - -// ========== -// Spawnfuncs -// ========== - -/*QUAKED spawnfunc_onslaught_link (0 .5 .8) (-16 -16 -16) (16 16 16) - Link between control points. - - This entity targets two different spawnfunc_onslaught_controlpoint or spawnfunc_onslaught_generator entities, and suppresses shielding on both if they are owned by different teams. - -keys: -"target" - first control point. -"target2" - second control point. - */ -spawnfunc(onslaught_link) -{ - if(!g_onslaught) { delete(this); return; } - - if (this.target == "" || this.target2 == "") - objerror(this, "target and target2 must be set\n"); - - this.ons_worldlinknext = ons_worldlinklist; // link into ons_worldlinklist - ons_worldlinklist = this; - - InitializeEntity(this, ons_DelayedLinkSetup, INITPRIO_FINDTARGET); - Net_LinkEntity(this, false, 0, ons_Link_Send); -} - -/*QUAKED spawnfunc_onslaught_controlpoint (0 .5 .8) (-32 -32 0) (32 32 128) - Control point. Be sure to give this enough clearance so that the shootable part has room to exist - - This should link to an spawnfunc_onslaught_controlpoint entity or spawnfunc_onslaught_generator entity. - -keys: -"targetname" - name that spawnfunc_onslaught_link entities will use to target this. -"target" - target any entities that are tied to this control point, such as vehicles and buildable structure entities. -"message" - name of this control point (should reflect the location in the map, such as "center bridge", "north tower", etc) - */ - -spawnfunc(onslaught_controlpoint) -{ - if(!g_onslaught) { delete(this); return; } - - ons_ControlPoint_Setup(this); -} - -/*QUAKED spawnfunc_onslaught_generator (0 .5 .8) (-32 -32 -24) (32 32 64) - Base generator. - - spawnfunc_onslaught_link entities can target this. - -keys: -"team" - team that owns this generator (5 = red, 14 = blue, etc), MUST BE SET. -"targetname" - name that spawnfunc_onslaught_link entities will use to target this. - */ -spawnfunc(onslaught_generator) -{ - if(!g_onslaught) { delete(this); return; } - if(!this.team) { objerror(this, "team must be set"); } - - ons_GeneratorSetup(this); -} - -// scoreboard setup -void ons_ScoreRules() -{ - CheckAllowedTeams(NULL); - int teams = 0; - if(c1 >= 0) teams |= BIT(0); - if(c2 >= 0) teams |= BIT(1); - if(c3 >= 0) teams |= BIT(2); - if(c4 >= 0) teams |= BIT(3); - ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, true); - ScoreInfo_SetLabel_TeamScore (ST_ONS_CAPS, "destroyed", SFL_SORT_PRIO_PRIMARY); - ScoreInfo_SetLabel_PlayerScore(SP_ONS_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); - ScoreInfo_SetLabel_PlayerScore(SP_ONS_TAKES, "takes", 0); - ScoreRules_basics_end(); -} - -void ons_DelayedInit(entity this) // Do this check with a delay so we can wait for teams to be set up -{ - ons_ScoreRules(); - - round_handler_Spawn(Onslaught_CheckPlayers, Onslaught_CheckWinner, Onslaught_RoundStart); - round_handler_Init(5, autocvar_g_onslaught_warmup, autocvar_g_onslaught_round_timelimit); -} - -void ons_Initialize() -{ - g_onslaught = true; - ons_captureshield_force = autocvar_g_onslaught_shield_force; - - InitializeEntity(NULL, ons_DelayedInit, INITPRIO_GAMETYPE); -} - -#endif +#include "onslaught.qh" diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qh b/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qh new file mode 100644 index 0000000000..be9b8203c6 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qh @@ -0,0 +1,4 @@ +#pragma once + +REGISTER_NET_LINKED(ENT_CLIENT_GENERATOR) +REGISTER_NET_LINKED(ENT_CLIENT_CONTROLPOINT_ICON) diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qh b/qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qh index d76f0ea069..d5437338e3 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qh +++ b/qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qh @@ -1,10 +1,7 @@ -#ifndef CONTROLPOINT_H -#define CONTROLPOINT_H +#pragma once const vector CPICON_MIN = '-32 -32 -9'; const vector CPICON_MAX = '32 32 25'; const int CPSF_STATUS = 4; const int CPSF_SETUP = 8; - -#endif diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/sv_generator.qh b/qcsrc/common/gamemodes/gamemode/onslaught/sv_generator.qh index 003c2b1d68..8356689e20 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/sv_generator.qh +++ b/qcsrc/common/gamemodes/gamemode/onslaught/sv_generator.qh @@ -1,5 +1,5 @@ -#ifndef GENERATOR_H -#define GENERATOR_H +#pragma once + const vector GENERATOR_MIN = '-52 -52 -14'; const vector GENERATOR_MAX = '52 52 75'; @@ -7,4 +7,3 @@ const int GSF_STATUS = 4; const int GSF_SETUP = 8; bool generator_send(entity this, entity to, int sf); -#endif diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc b/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc new file mode 100644 index 0000000000..570bc3645f --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc @@ -0,0 +1,2129 @@ +#include "sv_onslaught.qh" +#include "sv_controlpoint.qh" +#include "sv_generator.qh" + +bool g_onslaught; + +float autocvar_g_onslaught_teleport_wait; +bool autocvar_g_onslaught_spawn_at_controlpoints; +bool autocvar_g_onslaught_spawn_at_generator; +float autocvar_g_onslaught_cp_proxydecap; +float autocvar_g_onslaught_cp_proxydecap_distance = 512; +float autocvar_g_onslaught_cp_proxydecap_dps = 100; +float autocvar_g_onslaught_spawn_at_controlpoints_chance = 0.5; +float autocvar_g_onslaught_spawn_at_controlpoints_random; +float autocvar_g_onslaught_spawn_at_generator_chance; +float autocvar_g_onslaught_spawn_at_generator_random; +float autocvar_g_onslaught_cp_buildhealth; +float autocvar_g_onslaught_cp_buildtime; +float autocvar_g_onslaught_cp_health; +float autocvar_g_onslaught_cp_regen; +float autocvar_g_onslaught_gen_health; +float autocvar_g_onslaught_shield_force = 100; +float autocvar_g_onslaught_allow_vehicle_touch; +float autocvar_g_onslaught_round_timelimit; +float autocvar_g_onslaught_warmup; +float autocvar_g_onslaught_teleport_radius; +float autocvar_g_onslaught_spawn_choose; +float autocvar_g_onslaught_click_radius; + +void FixSize(entity e); + +// ======================= +// CaptureShield Functions +// ======================= + +bool ons_CaptureShield_Customize(entity this, entity client) +{ + entity e = WaypointSprite_getviewentity(client); + + if(!this.enemy.isshielded && (ons_ControlPoint_Attackable(this.enemy, e.team) > 0 || this.enemy.classname != "onslaught_controlpoint")) { return false; } + if(SAME_TEAM(this, e)) { return false; } + + return true; +} + +void ons_CaptureShield_Touch(entity this, entity toucher) +{ + if(!this.enemy.isshielded && (ons_ControlPoint_Attackable(this.enemy, toucher.team) > 0 || this.enemy.classname != "onslaught_controlpoint")) { return; } + if(!IS_PLAYER(toucher)) { return; } + if(SAME_TEAM(toucher, this)) { return; } + + vector mymid = (this.absmin + this.absmax) * 0.5; + vector theirmid = (toucher.absmin + toucher.absmax) * 0.5; + + Damage(toucher, this, this, 0, DEATH_HURTTRIGGER.m_id, mymid, normalize(theirmid - mymid) * ons_captureshield_force); + + if(IS_REAL_CLIENT(toucher)) + { + play2(toucher, SND(ONS_DAMAGEBLOCKEDBYSHIELD)); + + if(this.enemy.classname == "onslaught_generator") + Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_GENERATOR_SHIELDED); + else + Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_CONTROLPOINT_SHIELDED); + } +} + +void ons_CaptureShield_Reset(entity this) +{ + this.colormap = this.enemy.colormap; + this.team = this.enemy.team; +} + +void ons_CaptureShield_Spawn(entity generator, bool is_generator) +{ + entity shield = new(ons_captureshield); + + shield.enemy = generator; + shield.team = generator.team; + shield.colormap = generator.colormap; + shield.reset = ons_CaptureShield_Reset; + settouch(shield, ons_CaptureShield_Touch); + setcefc(shield, ons_CaptureShield_Customize); + shield.effects = EF_ADDITIVE; + set_movetype(shield, MOVETYPE_NOCLIP); + shield.solid = SOLID_TRIGGER; + shield.avelocity = '7 0 11'; + shield.scale = 1; + shield.model = ((is_generator) ? "models/onslaught/generator_shield.md3" : "models/onslaught/controlpoint_shield.md3"); + + precache_model(shield.model); + setorigin(shield, generator.origin); + _setmodel(shield, shield.model); + setsize(shield, shield.scale * shield.mins, shield.scale * shield.maxs); +} + + +// ========== +// Junk Pile +// ========== + +void setmodel_fixsize(entity e, Model m) +{ + setmodel(e, m); + FixSize(e); +} + +void onslaught_updatelinks() +{ + entity l; + // first check if the game has ended + LOG_DEBUG("--- updatelinks ---"); + // mark generators as being shielded and networked + for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) + { + if (l.iscaptured) + LOG_DEBUG(etos(l), " (generator) belongs to team ", ftos(l.team)); + else + LOG_DEBUG(etos(l), " (generator) is destroyed"); + l.islinked = l.iscaptured; + l.isshielded = l.iscaptured; + l.sprite.SendFlags |= 16; + } + // mark points as shielded and not networked + for(l = ons_worldcplist; l; l = l.ons_worldcpnext) + { + l.islinked = false; + l.isshielded = true; + int i; + for(i = 0; i < 17; ++i) { l.isgenneighbor[i] = false; l.iscpneighbor[i] = false; } + LOG_DEBUG(etos(l), " (point) belongs to team ", ftos(l.team)); + l.sprite.SendFlags |= 16; + } + // flow power outward from the generators through the network + bool stop = false; + while (!stop) + { + stop = true; + for(l = ons_worldlinklist; l; l = l.ons_worldlinknext) + { + // if both points are captured by the same team, and only one of + // them is powered, mark the other one as powered as well + if (l.enemy.iscaptured && l.goalentity.iscaptured) + if (l.enemy.islinked != l.goalentity.islinked) + if(SAME_TEAM(l.enemy, l.goalentity)) + { + if (!l.goalentity.islinked) + { + stop = false; + l.goalentity.islinked = true; + LOG_DEBUG(etos(l), " (link) is marking ", etos(l.goalentity), " (point) because its team matches ", etos(l.enemy), " (point)"); + } + else if (!l.enemy.islinked) + { + stop = false; + l.enemy.islinked = true; + LOG_DEBUG(etos(l), " (link) is marking ", etos(l.enemy), " (point) because its team matches ", etos(l.goalentity), " (point)"); + } + } + } + } + // now that we know which points are powered we can mark their neighbors + // as unshielded if team differs + for(l = ons_worldlinklist; l; l = l.ons_worldlinknext) + { + if (l.goalentity.islinked) + { + if(DIFF_TEAM(l.goalentity, l.enemy)) + { + LOG_DEBUG(etos(l), " (link) is unshielding ", etos(l.enemy), " (point) because its team does not match ", etos(l.goalentity), " (point)"); + l.enemy.isshielded = false; + } + if(l.goalentity.classname == "onslaught_generator") + l.enemy.isgenneighbor[l.goalentity.team] = true; + else + l.enemy.iscpneighbor[l.goalentity.team] = true; + } + if (l.enemy.islinked) + { + if(DIFF_TEAM(l.goalentity, l.enemy)) + { + LOG_DEBUG(etos(l), " (link) is unshielding ", etos(l.goalentity), " (point) because its team does not match ", etos(l.enemy), " (point)"); + l.goalentity.isshielded = false; + } + if(l.enemy.classname == "onslaught_generator") + l.goalentity.isgenneighbor[l.enemy.team] = true; + else + l.goalentity.iscpneighbor[l.enemy.team] = true; + } + } + // now update the generators + for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) + { + if (l.isshielded) + { + LOG_DEBUG(etos(l), " (generator) is shielded"); + l.takedamage = DAMAGE_NO; + l.bot_attack = false; + } + else + { + LOG_DEBUG(etos(l), " (generator) is not shielded"); + l.takedamage = DAMAGE_AIM; + l.bot_attack = true; + } + + ons_Generator_UpdateSprite(l); + } + // now update the takedamage and alpha variables on control point icons + for(l = ons_worldcplist; l; l = l.ons_worldcpnext) + { + if (l.isshielded) + { + LOG_DEBUG(etos(l), " (point) is shielded"); + if (l.goalentity) + { + l.goalentity.takedamage = DAMAGE_NO; + l.goalentity.bot_attack = false; + } + } + else + { + LOG_DEBUG(etos(l), " (point) is not shielded"); + if (l.goalentity) + { + l.goalentity.takedamage = DAMAGE_AIM; + l.goalentity.bot_attack = true; + } + } + ons_ControlPoint_UpdateSprite(l); + } + FOREACH_ENTITY_CLASS("ons_captureshield", true, + { + it.team = it.enemy.team; + it.colormap = it.enemy.colormap; + }); +} + + +// =================== +// Main Link Functions +// =================== + +bool ons_Link_Send(entity this, entity to, int sendflags) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_RADARLINK); + WriteByte(MSG_ENTITY, sendflags); + if(sendflags & 1) + { + WriteCoord(MSG_ENTITY, this.goalentity.origin_x); + WriteCoord(MSG_ENTITY, this.goalentity.origin_y); + WriteCoord(MSG_ENTITY, this.goalentity.origin_z); + } + if(sendflags & 2) + { + WriteCoord(MSG_ENTITY, this.enemy.origin_x); + WriteCoord(MSG_ENTITY, this.enemy.origin_y); + WriteCoord(MSG_ENTITY, this.enemy.origin_z); + } + if(sendflags & 4) + { + WriteByte(MSG_ENTITY, this.clientcolors); // which is goalentity's color + enemy's color * 16 + } + return true; +} + +void ons_Link_CheckUpdate(entity this) +{ + // TODO check if the two sides have moved (currently they won't move anyway) + float cc = 0, cc1 = 0, cc2 = 0; + + if(this.goalentity.islinked || this.goalentity.iscaptured) { cc1 = (this.goalentity.team - 1) * 0x01; } + if(this.enemy.islinked || this.enemy.iscaptured) { cc2 = (this.enemy.team - 1) * 0x10; } + + cc = cc1 + cc2; + + if(cc != this.clientcolors) + { + this.clientcolors = cc; + this.SendFlags |= 4; + } + + this.nextthink = time; +} + +void ons_DelayedLinkSetup(entity this) +{ + this.goalentity = find(NULL, targetname, this.target); + this.enemy = find(NULL, targetname, this.target2); + if(!this.goalentity) { objerror(this, "can not find target\n"); } + if(!this.enemy) { objerror(this, "can not find target2\n"); } + + LOG_DEBUG(etos(this.goalentity), " linked with ", etos(this.enemy)); + this.SendFlags |= 3; + setthink(this, ons_Link_CheckUpdate); + this.nextthink = time; +} + + +// ============================= +// Main Control Point Functions +// ============================= + +int ons_ControlPoint_CanBeLinked(entity cp, int teamnumber) +{ + if(cp.isgenneighbor[teamnumber]) { return 2; } + if(cp.iscpneighbor[teamnumber]) { return 1; } + + return 0; +} + +int ons_ControlPoint_Attackable(entity cp, int teamnumber) + // -2: SAME TEAM, attackable by enemy! + // -1: SAME TEAM! + // 0: off limits + // 1: attack it + // 2: touch it + // 3: attack it (HIGH PRIO) + // 4: touch it (HIGH PRIO) +{ + int a; + + if(cp.isshielded) + { + return 0; + } + else if(cp.goalentity) + { + // if there's already an icon built, nothing happens + if(cp.team == teamnumber) + { + a = ons_ControlPoint_CanBeLinked(cp, teamnumber); + if(a) // attackable by enemy? + return -2; // EMERGENCY! + return -1; + } + // we know it can be linked, so no need to check + // but... + a = ons_ControlPoint_CanBeLinked(cp, teamnumber); + if(a == 2) // near our generator? + return 3; // EMERGENCY! + return 1; + } + else + { + // free point + if(ons_ControlPoint_CanBeLinked(cp, teamnumber)) + { + a = ons_ControlPoint_CanBeLinked(cp, teamnumber); // why was this here NUM_TEAM_1 + NUM_TEAM_2 - t + if(a == 2) + return 4; // GET THIS ONE NOW! + else + return 2; // TOUCH ME + } + } + return 0; +} + +void ons_ControlPoint_Icon_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) +{ + if(damage <= 0) { return; } + + if (this.owner.isshielded) + { + // this is protected by a shield, so ignore the damage + if (time > this.pain_finished) + if (IS_PLAYER(attacker)) + { + play2(attacker, SND(ONS_DAMAGEBLOCKEDBYSHIELD)); + this.pain_finished = time + 1; + attacker.typehitsound += 1; // play both sounds (shield is way too quiet) + } + + return; + } + + if(IS_PLAYER(attacker)) + if(time - ons_notification_time[this.team] > 10) + { + play2team(this.team, SND(ONS_CONTROLPOINT_UNDERATTACK)); + ons_notification_time[this.team] = time; + } + + this.health = this.health - damage; + if(this.owner.iscaptured) + WaypointSprite_UpdateHealth(this.owner.sprite, this.health); + else + WaypointSprite_UpdateBuildFinished(this.owner.sprite, time + (this.max_health - this.health) / (this.count / ONS_CP_THINKRATE)); + this.pain_finished = time + 1; + // particles on every hit + pointparticles(EFFECT_SPARKS, hitloc, force*-1, 1); + //sound on every hit + if (random() < 0.5) + sound(this, CH_TRIGGER, SND_ONS_HIT1, VOL_BASE+0.3, ATTEN_NORM); + else + sound(this, CH_TRIGGER, SND_ONS_HIT2, VOL_BASE+0.3, ATTEN_NORM); + + if (this.health < 0) + { + sound(this, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM); + pointparticles(EFFECT_ROCKET_EXPLODE, this.origin, '0 0 0', 1); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_ONSLAUGHT_CPDESTROYED), this.owner.message, attacker.netname); + + PlayerScore_Add(attacker, SP_ONS_TAKES, 1); + PlayerScore_Add(attacker, SP_SCORE, 10); + + this.owner.goalentity = NULL; + this.owner.islinked = false; + this.owner.iscaptured = false; + this.owner.team = 0; + this.owner.colormap = 1024; + + WaypointSprite_UpdateMaxHealth(this.owner.sprite, 0); + + onslaught_updatelinks(); + + // Use targets now (somebody make sure this is in the right place..) + SUB_UseTargets(this.owner, this, NULL); + + this.owner.waslinked = this.owner.islinked; + if(this.owner.model != "models/onslaught/controlpoint_pad.md3") + setmodel_fixsize(this.owner, MDL_ONS_CP_PAD1); + //setsize(this, '-32 -32 0', '32 32 8'); + + delete(this); + } + + this.SendFlags |= CPSF_STATUS; +} + +void ons_ControlPoint_Icon_Think(entity this) +{ + this.nextthink = time + ONS_CP_THINKRATE; + + if(autocvar_g_onslaught_cp_proxydecap) + { + int _enemy_count = 0; + int _friendly_count = 0; + + FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), { + if(vdist(it.origin - this.origin, <, autocvar_g_onslaught_cp_proxydecap_distance)) + { + if(SAME_TEAM(it, this)) + ++_friendly_count; + else + ++_enemy_count; + } + }); + + _friendly_count = _friendly_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE); + _enemy_count = _enemy_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE); + + this.health = bound(0, this.health + (_friendly_count - _enemy_count), this.max_health); + this.SendFlags |= CPSF_STATUS; + if(this.health <= 0) + { + ons_ControlPoint_Icon_Damage(this, this, this, 1, 0, this.origin, '0 0 0'); + return; + } + } + + if (time > this.pain_finished + 5) + { + if(this.health < this.max_health) + { + this.health = this.health + this.count; + if (this.health >= this.max_health) + this.health = this.max_health; + WaypointSprite_UpdateHealth(this.owner.sprite, this.health); + } + } + + if(this.owner.islinked != this.owner.waslinked) + { + // unteam the spawnpoint if needed + int t = this.owner.team; + if(!this.owner.islinked) + this.owner.team = 0; + + SUB_UseTargets(this.owner, this, NULL); + + this.owner.team = t; + + this.owner.waslinked = this.owner.islinked; + } + + // damaged fx + if(random() < 0.6 - this.health / this.max_health) + { + Send_Effect(EFFECT_ELECTRIC_SPARKS, this.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1); + + if(random() > 0.8) + sound(this, CH_PAIN, SND_ONS_SPARK1, VOL_BASE, ATTEN_NORM); + else if (random() > 0.5) + sound(this, CH_PAIN, SND_ONS_SPARK2, VOL_BASE, ATTEN_NORM); + } +} + +void ons_ControlPoint_Icon_BuildThink(entity this) +{ + int a; + + this.nextthink = time + ONS_CP_THINKRATE; + + // only do this if there is power + a = ons_ControlPoint_CanBeLinked(this.owner, this.owner.team); + if(!a) + return; + + this.health = this.health + this.count; + + this.SendFlags |= CPSF_STATUS; + + if (this.health >= this.max_health) + { + this.health = this.max_health; + this.count = autocvar_g_onslaught_cp_regen * ONS_CP_THINKRATE; // slow repair rate from now on + setthink(this, ons_ControlPoint_Icon_Think); + sound(this, CH_TRIGGER, SND_ONS_CONTROLPOINT_BUILT, VOL_BASE, ATTEN_NORM); + this.owner.iscaptured = true; + this.solid = SOLID_BBOX; + + Send_Effect(EFFECT_CAP(this.owner.team), this.owner.origin, '0 0 0', 1); + + WaypointSprite_UpdateMaxHealth(this.owner.sprite, this.max_health); + WaypointSprite_UpdateHealth(this.owner.sprite, this.health); + + if(IS_PLAYER(this.owner.ons_toucher)) + { + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ONSLAUGHT_CAPTURE, this.owner.ons_toucher.netname, this.owner.message); + Send_Notification(NOTIF_ALL_EXCEPT, this.owner.ons_toucher, MSG_CENTER, APP_TEAM_NUM(this.owner.ons_toucher.team, CENTER_ONS_CAPTURE), this.owner.message); + Send_Notification(NOTIF_ONE, this.owner.ons_toucher, MSG_CENTER, CENTER_ONS_CAPTURE, this.owner.message); + PlayerScore_Add(this.owner.ons_toucher, SP_ONS_CAPS, 1); + PlayerTeamScore_AddScore(this.owner.ons_toucher, 10); + } + + this.owner.ons_toucher = NULL; + + onslaught_updatelinks(); + + // Use targets now (somebody make sure this is in the right place..) + SUB_UseTargets(this.owner, this, NULL); + + this.SendFlags |= CPSF_SETUP; + } + if(this.owner.model != MDL_ONS_CP_PAD2.model_str()) + setmodel_fixsize(this.owner, MDL_ONS_CP_PAD2); + + if(random() < 0.9 - this.health / this.max_health) + Send_Effect(EFFECT_RAGE, this.origin + 10 * randomvec(), '0 0 -1', 1); +} + +void onslaught_controlpoint_icon_link(entity e, void(entity this) spawnproc); + +void ons_ControlPoint_Icon_Spawn(entity cp, entity player) +{ + entity e = new(onslaught_controlpoint_icon); + + setsize(e, CPICON_MIN, CPICON_MAX); + setorigin(e, cp.origin + CPICON_OFFSET); + + e.owner = cp; + e.max_health = autocvar_g_onslaught_cp_health; + e.health = autocvar_g_onslaught_cp_buildhealth; + e.solid = SOLID_NOT; + e.takedamage = DAMAGE_AIM; + e.bot_attack = true; + e.event_damage = ons_ControlPoint_Icon_Damage; + e.team = player.team; + e.colormap = 1024 + (e.team - 1) * 17; + e.count = (e.max_health - e.health) * ONS_CP_THINKRATE / autocvar_g_onslaught_cp_buildtime; // how long it takes to build + + sound(e, CH_TRIGGER, SND_ONS_CONTROLPOINT_BUILD, VOL_BASE, ATTEN_NORM); + + cp.goalentity = e; + cp.team = e.team; + cp.colormap = e.colormap; + + Send_Effect(EFFECT_FLAG_TOUCH(player.team), e.origin, '0 0 0', 1); + + WaypointSprite_UpdateBuildFinished(cp.sprite, time + (e.max_health - e.health) / (e.count / ONS_CP_THINKRATE)); + WaypointSprite_UpdateRule(cp.sprite,cp.team,SPRITERULE_TEAMPLAY); + cp.sprite.SendFlags |= 16; + + onslaught_controlpoint_icon_link(e, ons_ControlPoint_Icon_BuildThink); +} + +entity ons_ControlPoint_Waypoint(entity e) +{ + if(e.team) + { + int a = ons_ControlPoint_Attackable(e, e.team); + + if(a == -2) { return WP_OnsCPDefend; } // defend now + if(a == -1 || a == 1 || a == 2) { return WP_OnsCP; } // touch + if(a == 3 || a == 4) { return WP_OnsCPAttack; } // attack + } + else + return WP_OnsCP; + + return WP_Null; +} + +void ons_ControlPoint_UpdateSprite(entity e) +{ + entity s1 = ons_ControlPoint_Waypoint(e); + WaypointSprite_UpdateSprites(e.sprite, s1, s1, s1); + + bool sh; + sh = !(ons_ControlPoint_CanBeLinked(e, NUM_TEAM_1) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_2) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_3) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_4)); + + if(e.lastteam != e.team + 2 || e.lastshielded != sh || e.iscaptured != e.lastcaptured) + { + if(e.iscaptured) // don't mess up build bars! + { + if(sh) + { + WaypointSprite_UpdateMaxHealth(e.sprite, 0); + } + else + { + WaypointSprite_UpdateMaxHealth(e.sprite, e.goalentity.max_health); + WaypointSprite_UpdateHealth(e.sprite, e.goalentity.health); + } + } + if(e.lastshielded) + { + if(e.team) + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, 0.5 * colormapPaletteColor(e.team - 1, false)); + else + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.5 0.5 0.5'); + } + else + { + if(e.team) + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, colormapPaletteColor(e.team - 1, false)); + else + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.75 0.75 0.75'); + } + WaypointSprite_Ping(e.sprite); + + e.lastteam = e.team + 2; + e.lastshielded = sh; + e.lastcaptured = e.iscaptured; + } +} + +void ons_ControlPoint_Touch(entity this, entity toucher) +{ + int attackable; + + if(IS_VEHICLE(toucher) && toucher.owner) + if(autocvar_g_onslaught_allow_vehicle_touch) + toucher = toucher.owner; + else + return; + + if(!IS_PLAYER(toucher)) { return; } + if(STAT(FROZEN, toucher)) { return; } + if(IS_DEAD(toucher)) { return; } + + if ( SAME_TEAM(this,toucher) ) + if ( this.iscaptured ) + { + if(time <= toucher.teleport_antispam) + Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT_ANTISPAM, rint(toucher.teleport_antispam - time)); + else + Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT); + } + + attackable = ons_ControlPoint_Attackable(this, toucher.team); + if(attackable != 2 && attackable != 4) + return; + // we've verified that this player has a legitimate claim to this point, + // so start building the captured point icon (which only captures this + // point if it successfully builds without being destroyed first) + ons_ControlPoint_Icon_Spawn(this, toucher); + + this.ons_toucher = toucher; + + onslaught_updatelinks(); +} + +void ons_ControlPoint_Think(entity this) +{ + this.nextthink = time + ONS_CP_THINKRATE; + CSQCMODEL_AUTOUPDATE(this); +} + +void ons_ControlPoint_Reset(entity this) +{ + if(this.goalentity) + delete(this.goalentity); + + this.goalentity = NULL; + this.team = 0; + this.colormap = 1024; + this.iscaptured = false; + this.islinked = false; + this.isshielded = true; + setthink(this, ons_ControlPoint_Think); + this.ons_toucher = NULL; + this.nextthink = time + ONS_CP_THINKRATE; + setmodel_fixsize(this, MDL_ONS_CP_PAD1); + + WaypointSprite_UpdateMaxHealth(this.sprite, 0); + WaypointSprite_UpdateRule(this.sprite,this.team,SPRITERULE_TEAMPLAY); + + onslaught_updatelinks(); + + SUB_UseTargets(this, this, NULL); // to reset the structures, playerspawns etc. + + CSQCMODEL_AUTOUPDATE(this); +} + +void ons_DelayedControlPoint_Setup(entity this) +{ + onslaught_updatelinks(); + + // captureshield setup + ons_CaptureShield_Spawn(this, false); + + CSQCMODEL_AUTOINIT(this); +} + +void ons_ControlPoint_Setup(entity cp) +{ + // main setup + cp.ons_worldcpnext = ons_worldcplist; // link control point into ons_worldcplist + ons_worldcplist = cp; + + cp.netname = "Control point"; + cp.team = 0; + cp.solid = SOLID_BBOX; + set_movetype(cp, MOVETYPE_NONE); + settouch(cp, ons_ControlPoint_Touch); + setthink(cp, ons_ControlPoint_Think); + cp.nextthink = time + ONS_CP_THINKRATE; + cp.reset = ons_ControlPoint_Reset; + cp.colormap = 1024; + cp.iscaptured = false; + cp.islinked = false; + cp.isshielded = true; + + if(cp.message == "") { cp.message = "a"; } + + // appearence + setmodel_fixsize(cp, MDL_ONS_CP_PAD1); + + // control point placement + if((cp.spawnflags & 1) || cp.noalign) // don't drop to floor, just stay at fixed location + { + cp.noalign = true; + set_movetype(cp, MOVETYPE_NONE); + } + else // drop to floor, automatically find a platform and set that as spawn origin + { + setorigin(cp, cp.origin + '0 0 20'); + cp.noalign = false; + droptofloor(cp); + set_movetype(cp, MOVETYPE_TOSS); + } + + // waypointsprites + WaypointSprite_SpawnFixed(WP_Null, cp.origin + CPGEN_WAYPOINT_OFFSET, cp, sprite, RADARICON_NONE); + WaypointSprite_UpdateRule(cp.sprite, cp.team, SPRITERULE_TEAMPLAY); + + InitializeEntity(cp, ons_DelayedControlPoint_Setup, INITPRIO_SETLOCATION); +} + + +// ========================= +// Main Generator Functions +// ========================= + +entity ons_Generator_Waypoint(entity e) +{ + if (e.isshielded) + return WP_OnsGenShielded; + return WP_OnsGen; +} + +void ons_Generator_UpdateSprite(entity e) +{ + entity s1 = ons_Generator_Waypoint(e); + WaypointSprite_UpdateSprites(e.sprite, s1, s1, s1); + + if(e.lastteam != e.team + 2 || e.lastshielded != e.isshielded) + { + e.lastteam = e.team + 2; + e.lastshielded = e.isshielded; + if(e.lastshielded) + { + if(e.team) + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, false)); + else + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.5 0.5 0.5'); + } + else + { + if(e.team) + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, false)); + else + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.75 0.75 0.75'); + } + WaypointSprite_Ping(e.sprite); + } +} + +void ons_GeneratorDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) +{ + if(damage <= 0) { return; } + if(warmup_stage || gameover) { return; } + if(!round_handler_IsRoundStarted()) { return; } + + if (attacker != this) + { + if (this.isshielded) + { + // this is protected by a shield, so ignore the damage + if (time > this.pain_finished) + if (IS_PLAYER(attacker)) + { + play2(attacker, SND(ONS_DAMAGEBLOCKEDBYSHIELD)); + attacker.typehitsound += 1; + this.pain_finished = time + 1; + } + return; + } + if (time > this.pain_finished) + { + this.pain_finished = time + 10; + FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && SAME_TEAM(it, this), Send_Notification(NOTIF_ONE, it, MSG_CENTER, CENTER_GENERATOR_UNDERATTACK)); + play2team(this.team, SND(ONS_GENERATOR_UNDERATTACK)); + } + } + this.health = this.health - damage; + WaypointSprite_UpdateHealth(this.sprite, this.health); + // choose an animation frame based on health + this.frame = 10 * bound(0, (1 - this.health / this.max_health), 1); + // see if the generator is still functional, or dying + if (this.health > 0) + { + this.lasthealth = this.health; + } + else + { + if (attacker == this) + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_ONSLAUGHT_GENDESTROYED_OVERTIME)); + else + { + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_ONSLAUGHT_GENDESTROYED)); + PlayerScore_Add(attacker, SP_SCORE, 100); + } + this.iscaptured = false; + this.islinked = false; + this.isshielded = false; + this.takedamage = DAMAGE_NO; // can't be hurt anymore + this.event_damage = func_null; // won't do anything if hurt + this.count = 0; // reset counter + setthink(this, func_null); + this.nextthink = 0; + //this.think(); // do the first explosion now + + WaypointSprite_UpdateMaxHealth(this.sprite, 0); + WaypointSprite_Ping(this.sprite); + //WaypointSprite_Kill(this.sprite); // can't do this yet, code too poor + + onslaught_updatelinks(); + } + + // Throw some flaming gibs on damage, more damage = more chance for gib + if(random() < damage/220) + { + sound(this, CH_TRIGGER, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); + } + else + { + // particles on every hit + Send_Effect(EFFECT_SPARKS, hitloc, force * -1, 1); + + //sound on every hit + if (random() < 0.5) + sound(this, CH_TRIGGER, SND_ONS_HIT1, VOL_BASE, ATTEN_NORM); + else + sound(this, CH_TRIGGER, SND_ONS_HIT2, VOL_BASE, ATTEN_NORM); + } + + this.SendFlags |= GSF_STATUS; +} + +void ons_GeneratorThink(entity this) +{ + this.nextthink = time + GEN_THINKRATE; + if (!gameover) + { + if(!this.isshielded && this.wait < time) + { + this.wait = time + 5; + FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { + if(SAME_TEAM(it, this)) + { + Send_Notification(NOTIF_ONE, it, MSG_CENTER, CENTER_ONS_NOTSHIELDED_TEAM); + soundto(MSG_ONE, it, CHAN_AUTO, SND(KH_ALARM), VOL_BASE, ATTEN_NONE); // FIXME: unique sound? + } + else + Send_Notification(NOTIF_ONE, it, MSG_CENTER, APP_TEAM_NUM(this.team, CENTER_ONS_NOTSHIELDED)); + }); + } + } +} + +void ons_GeneratorReset(entity this) +{ + this.team = this.team_saved; + this.lasthealth = this.max_health = this.health = autocvar_g_onslaught_gen_health; + this.takedamage = DAMAGE_AIM; + this.bot_attack = true; + this.iscaptured = true; + this.islinked = true; + this.isshielded = true; + this.event_damage = ons_GeneratorDamage; + setthink(this, ons_GeneratorThink); + this.nextthink = time + GEN_THINKRATE; + + Net_LinkEntity(this, false, 0, generator_send); + + this.SendFlags = GSF_SETUP; // just incase + this.SendFlags |= GSF_STATUS; + + WaypointSprite_UpdateMaxHealth(this.sprite, this.max_health); + WaypointSprite_UpdateHealth(this.sprite, this.health); + WaypointSprite_UpdateRule(this.sprite,this.team,SPRITERULE_TEAMPLAY); + + onslaught_updatelinks(); +} + +void ons_DelayedGeneratorSetup(entity this) +{ + // bot waypoints + waypoint_spawnforitem_force(this, this.origin); + this.nearestwaypointtimeout = 0; // activate waypointing again + this.bot_basewaypoint = this.nearestwaypoint; + + // captureshield setup + ons_CaptureShield_Spawn(this, true); + + onslaught_updatelinks(); + + Net_LinkEntity(this, false, 0, generator_send); +} + + +void onslaught_generator_touch(entity this, entity toucher) +{ + if ( IS_PLAYER(toucher) ) + if ( SAME_TEAM(this,toucher) ) + if ( this.iscaptured ) + { + Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT); + } +} + +void ons_GeneratorSetup(entity gen) // called when spawning a generator entity on the map as a spawnfunc +{ + // declarations + int teamnumber = gen.team; + + // main setup + gen.ons_worldgeneratornext = ons_worldgeneratorlist; // link generator into ons_worldgeneratorlist + ons_worldgeneratorlist = gen; + + gen.netname = sprintf("%s generator", Team_ColoredFullName(teamnumber)); + gen.classname = "onslaught_generator"; + gen.solid = SOLID_BBOX; + gen.team_saved = teamnumber; + set_movetype(gen, MOVETYPE_NONE); + gen.lasthealth = gen.max_health = gen.health = autocvar_g_onslaught_gen_health; + gen.takedamage = DAMAGE_AIM; + gen.bot_attack = true; + gen.event_damage = ons_GeneratorDamage; + gen.reset = ons_GeneratorReset; + setthink(gen, ons_GeneratorThink); + gen.nextthink = time + GEN_THINKRATE; + gen.iscaptured = true; + gen.islinked = true; + gen.isshielded = true; + settouch(gen, onslaught_generator_touch); + + // appearence + // model handled by CSQC + setsize(gen, GENERATOR_MIN, GENERATOR_MAX); + setorigin(gen, (gen.origin + CPGEN_SPAWN_OFFSET)); + gen.colormap = 1024 + (teamnumber - 1) * 17; + + // generator placement + droptofloor(gen); + + // waypointsprites + WaypointSprite_SpawnFixed(WP_Null, gen.origin + CPGEN_WAYPOINT_OFFSET, gen, sprite, RADARICON_NONE); + WaypointSprite_UpdateRule(gen.sprite, gen.team, SPRITERULE_TEAMPLAY); + WaypointSprite_UpdateMaxHealth(gen.sprite, gen.max_health); + WaypointSprite_UpdateHealth(gen.sprite, gen.health); + + InitializeEntity(gen, ons_DelayedGeneratorSetup, INITPRIO_SETLOCATION); +} + + +// =============== +// Round Handler +// =============== + +int total_generators; +void Onslaught_count_generators() +{ + entity e; + total_generators = redowned = blueowned = yellowowned = pinkowned = 0; + for(e = ons_worldgeneratorlist; e; e = e.ons_worldgeneratornext) + { + ++total_generators; + redowned += (e.team == NUM_TEAM_1 && e.health > 0); + blueowned += (e.team == NUM_TEAM_2 && e.health > 0); + yellowowned += (e.team == NUM_TEAM_3 && e.health > 0); + pinkowned += (e.team == NUM_TEAM_4 && e.health > 0); + } +} + +int Onslaught_GetWinnerTeam() +{ + int winner_team = 0; + if(redowned > 0) + winner_team = NUM_TEAM_1; + if(blueowned > 0) + { + if(winner_team) return 0; + winner_team = NUM_TEAM_2; + } + if(yellowowned > 0) + { + if(winner_team) return 0; + winner_team = NUM_TEAM_3; + } + if(pinkowned > 0) + { + if(winner_team) return 0; + winner_team = NUM_TEAM_4; + } + if(winner_team) + return winner_team; + return -1; // no generators left? +} + +void nades_Clear(entity e); + +#define ONS_OWNED_GENERATORS() ((redowned > 0) + (blueowned > 0) + (yellowowned > 0) + (pinkowned > 0)) +#define ONS_OWNED_GENERATORS_OK() (ONS_OWNED_GENERATORS() > 1) +bool Onslaught_CheckWinner() +{ + if ((autocvar_timelimit && time > game_starttime + autocvar_timelimit * 60) || (round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)) + { + ons_stalemate = true; + + if (!wpforenemy_announced) + { + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT); + sound(NULL, CH_INFO, SND_ONS_GENERATOR_DECAY, VOL_BASE, ATTEN_NONE); + + wpforenemy_announced = true; + } + + entity tmp_entity; // temporary entity + float d; + for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) if(time >= tmp_entity.ons_overtime_damagedelay) + { + // tmp_entity.max_health / 300 gives 5 minutes of overtime. + // control points reduce the overtime duration. + d = 1; + entity e; + for(e = ons_worldcplist; e; e = e.ons_worldcpnext) + { + if(DIFF_TEAM(e, tmp_entity)) + if(e.islinked) + d = d + 1; + } + + if(autocvar_g_campaign && autocvar__campaign_testrun) + d = d * tmp_entity.max_health; + else + d = d * tmp_entity.max_health / max(30, 60 * autocvar_timelimit_suddendeath); + + Damage(tmp_entity, tmp_entity, tmp_entity, d, DEATH_HURTTRIGGER.m_id, tmp_entity.origin, '0 0 0'); + + tmp_entity.sprite.SendFlags |= 16; + + tmp_entity.ons_overtime_damagedelay = time + 1; + } + } + else { wpforenemy_announced = false; ons_stalemate = false; } + + Onslaught_count_generators(); + + if(ONS_OWNED_GENERATORS_OK()) + return 0; + + int winner_team = Onslaught_GetWinnerTeam(); + + if(winner_team > 0) + { + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(winner_team, CENTER_ROUND_TEAM_WIN)); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_ROUND_TEAM_WIN)); + TeamScore_AddToTeam(winner_team, ST_ONS_CAPS, +1); + } + else if(winner_team == -1) + { + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_TIED); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_TIED); + } + + ons_stalemate = false; + + play2all(SND(CTF_CAPTURE(winner_team))); + + round_handler_Init(7, autocvar_g_onslaught_warmup, autocvar_g_onslaught_round_timelimit); + + FOREACH_CLIENT(IS_PLAYER(it), { + STAT(ROUNDLOST, it) = true; + it.player_blocked = true; + + nades_Clear(it); + }); + + return 1; +} + +bool Onslaught_CheckPlayers() +{ + return 1; +} + +void Onslaught_RoundStart() +{ + entity tmp_entity; + FOREACH_CLIENT(IS_PLAYER(it), it.player_blocked = false); + + for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext) + tmp_entity.sprite.SendFlags |= 16; + + for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) + tmp_entity.sprite.SendFlags |= 16; +} + + +// ================ +// Bot player logic +// ================ + +// NOTE: LEGACY CODE, needs to be re-written! + +void havocbot_goalrating_ons_offenseitems(entity this, float ratingscale, vector org, float sradius) +{ + bool needarmor = false, needweapons = false; + + // Needs armor/health? + if(this.health<100) + needarmor = true; + + // Needs weapons? + int c = 0; + FOREACH(Weapons, it != WEP_Null, { + if(this.weapons & (it.m_wepset)) + if(++c >= 4) + break; + }); + + if(c<4) + needweapons = true; + + if(!needweapons && !needarmor) + return; + + LOG_DEBUG(this.netname, " needs weapons ", ftos(needweapons)); + LOG_DEBUG(this.netname, " needs armor ", ftos(needarmor)); + + // See what is around + FOREACH_ENTITY_FLOAT(bot_pickup, true, + { + // gather health and armor only + if (it.solid) + if ( ((it.health || it.armorvalue) && needarmor) || (it.weapons && needweapons ) ) + if (vdist(it.origin - org, <, sradius)) + { + int t = it.bot_pickupevalfunc(this, it); + if (t > 0) + navigation_routerating(this, it, t * ratingscale, 500); + } + }); +} + +void havocbot_role_ons_setrole(entity this, int role) +{ + LOG_DEBUG(this.netname," switched to "); + switch(role) + { + case HAVOCBOT_ONS_ROLE_DEFENSE: + LOG_DEBUG("defense"); + this.havocbot_role = havocbot_role_ons_defense; + this.havocbot_role_flags = HAVOCBOT_ONS_ROLE_DEFENSE; + this.havocbot_role_timeout = 0; + break; + case HAVOCBOT_ONS_ROLE_ASSISTANT: + LOG_DEBUG("assistant"); + this.havocbot_role = havocbot_role_ons_assistant; + this.havocbot_role_flags = HAVOCBOT_ONS_ROLE_ASSISTANT; + this.havocbot_role_timeout = 0; + break; + case HAVOCBOT_ONS_ROLE_OFFENSE: + LOG_DEBUG("offense"); + this.havocbot_role = havocbot_role_ons_offense; + this.havocbot_role_flags = HAVOCBOT_ONS_ROLE_OFFENSE; + this.havocbot_role_timeout = 0; + break; + } + LOG_DEBUG(""); +} + +void havocbot_goalrating_ons_controlpoints_attack(entity this, float ratingscale) +{ + entity cp, cp1, cp2, best, wp; + float radius, bestvalue; + int c; + bool found; + + // Filter control points + for(cp2 = ons_worldcplist; cp2; cp2 = cp2.ons_worldcpnext) + { + cp2.wpcost = c = 0; + cp2.wpconsidered = false; + + if(cp2.isshielded) + continue; + + // Ignore owned controlpoints + if(!(cp2.isgenneighbor[this.team] || cp2.iscpneighbor[this.team])) + continue; + + // Count team mates interested in this control point + // (easier and cleaner than keeping counters per cp and teams) + FOREACH_CLIENT(IS_PLAYER(it), { + if(SAME_TEAM(it, this)) + if(it.havocbot_role_flags & HAVOCBOT_ONS_ROLE_OFFENSE) + if(it.havocbot_ons_target == cp2) + ++c; + }); + + // NOTE: probably decrease the cost of attackable control points + cp2.wpcost = c; + cp2.wpconsidered = true; + } + + // We'll consider only the best case + bestvalue = 99999999999; + cp = NULL; + for(cp1 = ons_worldcplist; cp1; cp1 = cp1.ons_worldcpnext) + { + if (!cp1.wpconsidered) + continue; + + if(cp1.wpcost this.havocbot_role_timeout) + { + havocbot_ons_reset_role(this); + return; + } + + if(this.havocbot_attack_time>time) + return; + + if (this.bot_strategytime < time) + { + navigation_goalrating_start(this); + havocbot_goalrating_enemyplayers(this, 20000, this.origin, 650); + if(!havocbot_goalrating_ons_generator_attack(this, 20000)) + havocbot_goalrating_ons_controlpoints_attack(this, 20000); + havocbot_goalrating_ons_offenseitems(this, 10000, this.origin, 10000); + navigation_goalrating_end(this); + + this.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + } +} + +void havocbot_role_ons_assistant(entity this) +{ + havocbot_ons_reset_role(this); +} + +void havocbot_role_ons_defense(entity this) +{ + havocbot_ons_reset_role(this); +} + +void havocbot_ons_reset_role(entity this) +{ + if(IS_DEAD(this)) + return; + + this.havocbot_ons_target = NULL; + + // TODO: Defend control points or generator if necessary + + havocbot_role_ons_setrole(this, HAVOCBOT_ONS_ROLE_OFFENSE); +} + + +/* + * Find control point or generator owned by the same team self which is nearest to pos + * if max_dist is positive, only control points within this range will be considered + */ +entity ons_Nearest_ControlPoint(entity this, vector pos, float max_dist) +{ + entity closest_target = NULL; + FOREACH_ENTITY_CLASS("onslaught_controlpoint", true, + { + if(SAME_TEAM(it, this)) + if(it.iscaptured) + if(max_dist <= 0 || vdist(it.origin - pos, <=, max_dist)) + if(vlen2(it.origin - pos) <= vlen2(closest_target.origin - pos) || closest_target == NULL) + closest_target = it; + }); + FOREACH_ENTITY_CLASS("onslaught_generator", true, + { + if(SAME_TEAM(it, this)) + if(max_dist <= 0 || vdist(it.origin - pos, <, max_dist)) + if(vlen2(it.origin - pos) <= vlen2(closest_target.origin - pos) || closest_target == NULL) + closest_target = it; + }); + + return closest_target; +} + +/* + * Find control point or generator owned by the same team self which is nearest to pos + * if max_dist is positive, only control points within this range will be considered + * This function only check distances on the XY plane, disregarding Z + */ +entity ons_Nearest_ControlPoint_2D(entity this, vector pos, float max_dist) +{ + entity closest_target = NULL; + vector delta; + float smallest_distance = 0, distance; + + FOREACH_ENTITY_CLASS("onslaught_controlpoint", true, + { + delta = it.origin - pos; + delta_z = 0; + distance = vlen(delta); + + if(SAME_TEAM(it, this)) + if(it.iscaptured) + if(max_dist <= 0 || distance <= max_dist) + if(closest_target == NULL || distance <= smallest_distance ) + { + closest_target = it; + smallest_distance = distance; + } + }); + FOREACH_ENTITY_CLASS("onslaught_generator", true, + { + delta = it.origin - pos; + delta_z = 0; + distance = vlen(delta); + + if(SAME_TEAM(it, this)) + if(max_dist <= 0 || distance <= max_dist) + if(closest_target == NULL || distance <= smallest_distance ) + { + closest_target = it; + smallest_distance = distance; + } + }); + + return closest_target; +} +/** + * find the number of control points and generators in the same team as this + */ +int ons_Count_SelfControlPoints(entity this) +{ + int n = 0; + FOREACH_ENTITY_CLASS("onslaught_controlpoint", true, + { + if(SAME_TEAM(it, this)) + if(it.iscaptured) + n++; + }); + FOREACH_ENTITY_CLASS("onslaught_generator", true, + { + if(SAME_TEAM(it, this)) + n++; + }); + return n; +} + +/** + * Teleport player to a random position near tele_target + * if tele_effects is true, teleport sound+particles are created + * return false on failure + */ +bool ons_Teleport(entity player, entity tele_target, float range, bool tele_effects) +{ + if ( !tele_target ) + return false; + + int i; + vector loc; + float theta; + // narrow the range for each iteration to increase chances that a spawnpoint + // can be found even if there's little room around the control point + float iteration_scale = 1; + for(i = 0; i < 16; ++i) + { + iteration_scale -= i / 16; + theta = random() * 2 * M_PI; + loc_y = sin(theta); + loc_x = cos(theta); + loc_z = 0; + loc *= random() * range * iteration_scale; + + loc += tele_target.origin + '0 0 128' * iteration_scale; + + tracebox(loc, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), loc, MOVE_NORMAL, player); + if(trace_fraction == 1.0 && !trace_startsolid) + { + traceline(tele_target.origin, loc, MOVE_NOMONSTERS, tele_target); // double check to make sure we're not spawning outside the NULL + if(trace_fraction == 1.0 && !trace_startsolid) + { + if ( tele_effects ) + { + Send_Effect(EFFECT_TELEPORT, player.origin, '0 0 0', 1); + sound (player, CH_TRIGGER, SND_TELEPORT, VOL_BASE, ATTEN_NORM); + } + setorigin(player, loc); + player.angles = '0 1 0' * ( theta * RAD2DEG + 180 ); + makevectors(player.angles); + player.fixangle = true; + player.teleport_antispam = time + autocvar_g_onslaught_teleport_wait; + + if ( tele_effects ) + Send_Effect(EFFECT_TELEPORT, player.origin + v_forward * 32, '0 0 0', 1); + return true; + } + } + } + + return false; +} + +// ============== +// Hook Functions +// ============== + +MUTATOR_HOOKFUNCTION(ons, reset_map_global) +{ + FOREACH_CLIENT(IS_PLAYER(it), { + STAT(ROUNDLOST, it) = false; + it.ons_deathloc = '0 0 0'; + PutClientInServer(it); + }); + return false; +} + +MUTATOR_HOOKFUNCTION(ons, ClientDisconnect) +{ + entity player = M_ARGV(0, entity); + + player.ons_deathloc = '0 0 0'; +} + +MUTATOR_HOOKFUNCTION(ons, MakePlayerObserver) +{ + entity player = M_ARGV(0, entity); + + player.ons_deathloc = '0 0 0'; +} + +MUTATOR_HOOKFUNCTION(ons, PlayerSpawn) +{ + entity player = M_ARGV(0, entity); + + if(!round_handler_IsRoundStarted()) + { + player.player_blocked = true; + return false; + } + + entity l; + for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) + { + l.sprite.SendFlags |= 16; + } + for(l = ons_worldcplist; l; l = l.ons_worldcpnext) + { + l.sprite.SendFlags |= 16; + } + + if(ons_stalemate) { Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT); } + + if ( autocvar_g_onslaught_spawn_choose ) + if ( player.ons_spawn_by ) + if ( ons_Teleport(player,player.ons_spawn_by,autocvar_g_onslaught_teleport_radius,false) ) + { + player.ons_spawn_by = NULL; + return false; + } + + if(autocvar_g_onslaught_spawn_at_controlpoints) + if(random() <= autocvar_g_onslaught_spawn_at_controlpoints_chance) + { + float random_target = autocvar_g_onslaught_spawn_at_controlpoints_random; + entity tmp_entity, closest_target = NULL; + vector spawn_loc = player.ons_deathloc; + + // new joining player or round reset, don't bother checking + if(spawn_loc == '0 0 0') { return false; } + + if(random_target) { RandomSelection_Init(); } + + for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext) + { + if(SAME_TEAM(tmp_entity, player)) + if(random_target) + RandomSelection_Add(tmp_entity, 0, string_null, 1, 1); + else if(vlen2(tmp_entity.origin - spawn_loc) <= vlen2(closest_target.origin - spawn_loc) || closest_target == NULL) + closest_target = tmp_entity; + } + + if(random_target) { closest_target = RandomSelection_chosen_ent; } + + if(closest_target) + { + float i; + vector loc; + float iteration_scale = 1; + for(i = 0; i < 10; ++i) + { + iteration_scale -= i / 10; + loc = closest_target.origin + '0 0 96' * iteration_scale; + loc += ('0 1 0' * random()) * 128 * iteration_scale; + tracebox(loc, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), loc, MOVE_NORMAL, player); + if(trace_fraction == 1.0 && !trace_startsolid) + { + traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the NULL + if(trace_fraction == 1.0 && !trace_startsolid) + { + setorigin(player, loc); + player.angles = normalize(loc - closest_target.origin) * RAD2DEG; + return false; + } + } + } + } + } + + if(autocvar_g_onslaught_spawn_at_generator) + if(random() <= autocvar_g_onslaught_spawn_at_generator_chance) + { + float random_target = autocvar_g_onslaught_spawn_at_generator_random; + entity tmp_entity, closest_target = NULL; + vector spawn_loc = player.ons_deathloc; + + // new joining player or round reset, don't bother checking + if(spawn_loc == '0 0 0') { return false; } + + if(random_target) { RandomSelection_Init(); } + + for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) + { + if(random_target) + RandomSelection_Add(tmp_entity, 0, string_null, 1, 1); + else + { + if(SAME_TEAM(tmp_entity, player)) + if(vlen2(tmp_entity.origin - spawn_loc) <= vlen2(closest_target.origin - spawn_loc) || closest_target == NULL) + closest_target = tmp_entity; + } + } + + if(random_target) { closest_target = RandomSelection_chosen_ent; } + + if(closest_target) + { + float i; + vector loc; + float iteration_scale = 1; + for(i = 0; i < 10; ++i) + { + iteration_scale -= i / 10; + loc = closest_target.origin + '0 0 128' * iteration_scale; + loc += ('0 1 0' * random()) * 256 * iteration_scale; + tracebox(loc, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), loc, MOVE_NORMAL, player); + if(trace_fraction == 1.0 && !trace_startsolid) + { + traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the NULL + if(trace_fraction == 1.0 && !trace_startsolid) + { + setorigin(player, loc); + player.angles = normalize(loc - closest_target.origin) * RAD2DEG; + return false; + } + } + } + } + } + + return false; +} + +MUTATOR_HOOKFUNCTION(ons, PlayerDies) +{ + entity frag_target = M_ARGV(2, entity); + + frag_target.ons_deathloc = frag_target.origin; + entity l; + for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) + { + l.sprite.SendFlags |= 16; + } + for(l = ons_worldcplist; l; l = l.ons_worldcpnext) + { + l.sprite.SendFlags |= 16; + } + + if ( autocvar_g_onslaught_spawn_choose ) + if ( ons_Count_SelfControlPoints(frag_target) > 1 ) + stuffcmd(frag_target, "qc_cmd_cl hud clickradar\n"); + + return false; +} + +MUTATOR_HOOKFUNCTION(ons, MonsterMove) +{ + entity mon = M_ARGV(0, entity); + + entity e = find(NULL, targetname, mon.target); + if (e != NULL) + mon.team = e.team; +} + +void ons_MonsterSpawn_Delayed(entity this) +{ + entity own = this.owner; + + if(!own) { delete(this); return; } + + if(own.targetname) + { + entity e = find(NULL, target, own.targetname); + if(e != NULL) + { + own.team = e.team; + + own.use(own, e, NULL); + } + } + + delete(this); +} + +MUTATOR_HOOKFUNCTION(ons, MonsterSpawn) +{ + entity mon = M_ARGV(0, entity); + + entity e = spawn(); + e.owner = mon; + InitializeEntity(e, ons_MonsterSpawn_Delayed, INITPRIO_FINDTARGET); +} + +void ons_TurretSpawn_Delayed(entity this) +{ + entity own = this.owner; + + if(!own) { delete(this); return; } + + if(own.targetname) + { + entity e = find(NULL, target, own.targetname); + if(e != NULL) + { + own.team = e.team; + own.active = ACTIVE_NOT; + + own.use(own, e, NULL); + } + } + + delete(this); +} + +MUTATOR_HOOKFUNCTION(ons, TurretSpawn) +{ + entity turret = M_ARGV(0, entity); + + entity e = spawn(); + e.owner = turret; + InitializeEntity(e, ons_TurretSpawn_Delayed, INITPRIO_FINDTARGET); + + return false; +} + +MUTATOR_HOOKFUNCTION(ons, HavocBot_ChooseRole) +{ + entity bot = M_ARGV(0, entity); + + havocbot_ons_reset_role(bot); + return true; +} + +MUTATOR_HOOKFUNCTION(ons, GetTeamCount) +{ + // onslaught is special + for(entity tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) + { + switch(tmp_entity.team) + { + case NUM_TEAM_1: c1 = 0; break; + case NUM_TEAM_2: c2 = 0; break; + case NUM_TEAM_3: c3 = 0; break; + case NUM_TEAM_4: c4 = 0; break; + } + } + + return true; +} + +MUTATOR_HOOKFUNCTION(ons, SpectateCopy) +{ + entity spectatee = M_ARGV(0, entity); + entity client = M_ARGV(1, entity); + + STAT(ROUNDLOST, client) = STAT(ROUNDLOST, spectatee); // make spectators see it too +} + +MUTATOR_HOOKFUNCTION(ons, SV_ParseClientCommand) +{ + if(MUTATOR_RETURNVALUE) // command was already handled? + return false; + + entity player = M_ARGV(0, entity); + string cmd_name = M_ARGV(1, string); + int cmd_argc = M_ARGV(2, int); + + if ( cmd_name == "ons_spawn" ) + { + vector pos = player.origin; + if(cmd_argc > 1) + pos_x = stof(argv(1)); + if(cmd_argc > 2) + pos_y = stof(argv(2)); + if(cmd_argc > 3) + pos_z = stof(argv(3)); + + if ( IS_PLAYER(player) ) + { + if ( !STAT(FROZEN, player) ) + { + entity source_point = ons_Nearest_ControlPoint(player, player.origin, autocvar_g_onslaught_teleport_radius); + + if ( !source_point && player.health > 0 ) + { + sprint(player, "\nYou need to be next to a control point\n"); + return true; + } + + + entity closest_target = ons_Nearest_ControlPoint_2D(player, pos, autocvar_g_onslaught_click_radius); + + if ( closest_target == NULL ) + { + sprint(player, "\nNo control point found\n"); + return true; + } + + if ( player.health <= 0 ) + { + player.ons_spawn_by = closest_target; + player.respawn_flags = player.respawn_flags | RESPAWN_FORCE; + } + else + { + if ( source_point == closest_target ) + { + sprint(player, "\nTeleporting to the same point\n"); + return true; + } + + if ( !ons_Teleport(player,closest_target,autocvar_g_onslaught_teleport_radius,true) ) + sprint(player, "\nUnable to teleport there\n"); + } + + return true; + } + + sprint(player, "\nNo teleportation for you\n"); + } + + return true; + } + return false; +} + +MUTATOR_HOOKFUNCTION(ons, PlayerUseKey) +{ + if(MUTATOR_RETURNVALUE || gameover) { return false; } + + entity player = M_ARGV(0, entity); + + if((time > player.teleport_antispam) && (!IS_DEAD(player)) && !player.vehicle) + { + entity source_point = ons_Nearest_ControlPoint(player, player.origin, autocvar_g_onslaught_teleport_radius); + if ( source_point ) + { + stuffcmd(player, "qc_cmd_cl hud clickradar\n"); + return true; + } + } +} + +MUTATOR_HOOKFUNCTION(ons, PlayHitsound) +{ + entity frag_victim = M_ARGV(0, entity); + + return (frag_victim.classname == "onslaught_generator" && !frag_victim.isshielded) + || (frag_victim.classname == "onslaught_controlpoint_icon" && !frag_victim.owner.isshielded); +} + +MUTATOR_HOOKFUNCTION(ons, SendWaypoint) +{ + entity wp = M_ARGV(0, entity); + entity to = M_ARGV(1, entity); + int sf = M_ARGV(2, int); + int wp_flag = M_ARGV(3, int); + + if(sf & 16) + { + if(wp.owner.classname == "onslaught_controlpoint") + { + entity wp_owner = wp.owner; + entity e = WaypointSprite_getviewentity(to); + if(SAME_TEAM(e, wp_owner) && wp_owner.goalentity.health >= wp_owner.goalentity.max_health) { wp_flag |= 2; } + if(!ons_ControlPoint_Attackable(wp_owner, e.team)) { wp_flag |= 2; } + } + if(wp.owner.classname == "onslaught_generator") + { + entity wp_owner = wp.owner; + if(wp_owner.isshielded && wp_owner.health >= wp_owner.max_health) { wp_flag |= 2; } + if(wp_owner.health <= 0) { wp_flag |= 2; } + } + } + + M_ARGV(3, int) = wp_flag; +} + +MUTATOR_HOOKFUNCTION(ons, TurretValidateTarget) +{ + entity turret_target = M_ARGV(1, entity); + + if(substring(turret_target.classname, 0, 10) == "onslaught_") // don't attack onslaught targets, that's the player's job! + { + M_ARGV(3, float) = -3; + return true; + } + + return false; +} + +MUTATOR_HOOKFUNCTION(ons, TurretThink) +{ + entity turret = M_ARGV(0, entity); + + // ONS uses somewhat backwards linking. + if(turret.target) + { + entity e = find(NULL, targetname, turret.target); + if (e != NULL) + turret.team = e.team; + } + + if(turret.team != turret.tur_head.team) + turret_respawn(turret); +} + + +// ========== +// Spawnfuncs +// ========== + +/*QUAKED spawnfunc_onslaught_link (0 .5 .8) (-16 -16 -16) (16 16 16) + Link between control points. + + This entity targets two different spawnfunc_onslaught_controlpoint or spawnfunc_onslaught_generator entities, and suppresses shielding on both if they are owned by different teams. + +keys: +"target" - first control point. +"target2" - second control point. + */ +spawnfunc(onslaught_link) +{ + if(!g_onslaught) { delete(this); return; } + + if (this.target == "" || this.target2 == "") + objerror(this, "target and target2 must be set\n"); + + this.ons_worldlinknext = ons_worldlinklist; // link into ons_worldlinklist + ons_worldlinklist = this; + + InitializeEntity(this, ons_DelayedLinkSetup, INITPRIO_FINDTARGET); + Net_LinkEntity(this, false, 0, ons_Link_Send); +} + +/*QUAKED spawnfunc_onslaught_controlpoint (0 .5 .8) (-32 -32 0) (32 32 128) + Control point. Be sure to give this enough clearance so that the shootable part has room to exist + + This should link to an spawnfunc_onslaught_controlpoint entity or spawnfunc_onslaught_generator entity. + +keys: +"targetname" - name that spawnfunc_onslaught_link entities will use to target this. +"target" - target any entities that are tied to this control point, such as vehicles and buildable structure entities. +"message" - name of this control point (should reflect the location in the map, such as "center bridge", "north tower", etc) + */ + +spawnfunc(onslaught_controlpoint) +{ + if(!g_onslaught) { delete(this); return; } + + ons_ControlPoint_Setup(this); +} + +/*QUAKED spawnfunc_onslaught_generator (0 .5 .8) (-32 -32 -24) (32 32 64) + Base generator. + + spawnfunc_onslaught_link entities can target this. + +keys: +"team" - team that owns this generator (5 = red, 14 = blue, etc), MUST BE SET. +"targetname" - name that spawnfunc_onslaught_link entities will use to target this. + */ +spawnfunc(onslaught_generator) +{ + if(!g_onslaught) { delete(this); return; } + if(!this.team) { objerror(this, "team must be set"); } + + ons_GeneratorSetup(this); +} + +// scoreboard setup +void ons_ScoreRules() +{ + CheckAllowedTeams(NULL); + int teams = 0; + if(c1 >= 0) teams |= BIT(0); + if(c2 >= 0) teams |= BIT(1); + if(c3 >= 0) teams |= BIT(2); + if(c4 >= 0) teams |= BIT(3); + ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, true); + ScoreInfo_SetLabel_TeamScore (ST_ONS_CAPS, "destroyed", SFL_SORT_PRIO_PRIMARY); + ScoreInfo_SetLabel_PlayerScore(SP_ONS_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); + ScoreInfo_SetLabel_PlayerScore(SP_ONS_TAKES, "takes", 0); + ScoreRules_basics_end(); +} + +void ons_DelayedInit(entity this) // Do this check with a delay so we can wait for teams to be set up +{ + ons_ScoreRules(); + + round_handler_Spawn(Onslaught_CheckPlayers, Onslaught_CheckWinner, Onslaught_RoundStart); + round_handler_Init(5, autocvar_g_onslaught_warmup, autocvar_g_onslaught_round_timelimit); +} + +void ons_Initialize() +{ + g_onslaught = true; + ons_captureshield_force = autocvar_g_onslaught_shield_force; + + InitializeEntity(NULL, ons_DelayedInit, INITPRIO_GAMETYPE); +} diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qh b/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qh new file mode 100644 index 0000000000..70a5c8b6ba --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qh @@ -0,0 +1,117 @@ +#pragma once + +float autocvar_g_onslaught_point_limit; +void ons_Initialize(); + +REGISTER_MUTATOR(ons, false) +{ + MUTATOR_ONADD + { + if (time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + ons_Initialize(); + + ActivateTeamplay(); + SetLimits(autocvar_g_onslaught_point_limit, autocvar_leadlimit_override, autocvar_timelimit_override, -1); + have_team_spawns = -1; // request team spawns + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back ons_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This is a game type and it cannot be removed at runtime."); + return -1; + } + + return false; +} + +.entity ons_toucher; // player who touched the control point + +// control point / generator constants +const float ONS_CP_THINKRATE = 0.2; +const float GEN_THINKRATE = 1; +#define CPGEN_SPAWN_OFFSET ('0 0 1' * (PL_MAX_CONST.z - 13)) +const vector CPGEN_WAYPOINT_OFFSET = ('0 0 128'); +const vector CPICON_OFFSET = ('0 0 96'); + +// list of generators on the map +entity ons_worldgeneratorlist; +.entity ons_worldgeneratornext; +.entity ons_stalegeneratornext; + +// list of control points on the map +entity ons_worldcplist; +.entity ons_worldcpnext; +.entity ons_stalecpnext; + +// list of links on the map +entity ons_worldlinklist; +.entity ons_worldlinknext; +.entity ons_stalelinknext; + +// definitions +.entity sprite; +.string target2; +.int iscaptured; +.int islinked; +.int isshielded; +.float lasthealth; +.int lastteam; +.int lastshielded; +.int lastcaptured; + +.bool waslinked; + +bool ons_stalemate; + +.float teleport_antispam; + +// waypoint sprites +.entity bot_basewaypoint; // generator waypointsprite + +.bool isgenneighbor[17]; +.bool iscpneighbor[17]; +float ons_notification_time[17]; + +.float ons_overtime_damagedelay; + +.vector ons_deathloc; + +.entity ons_spawn_by; + +// declarations for functions used outside gamemode_onslaught.qc +void ons_Generator_UpdateSprite(entity e); +void ons_ControlPoint_UpdateSprite(entity e); +bool ons_ControlPoint_Attackable(entity cp, int teamnumber); + +// CaptureShield: Prevent capturing or destroying control point/generator if it is not available yet +float ons_captureshield_force; // push force of the shield + +// bot player logic +const int HAVOCBOT_ONS_ROLE_NONE = 0; +const int HAVOCBOT_ONS_ROLE_DEFENSE = 2; +const int HAVOCBOT_ONS_ROLE_ASSISTANT = 4; +const int HAVOCBOT_ONS_ROLE_OFFENSE = 8; + +.entity havocbot_ons_target; + +.int havocbot_role_flags; +.float havocbot_attack_time; + +void havocbot_role_ons_defense(entity this); +void havocbot_role_ons_offense(entity this); +void havocbot_role_ons_assistant(entity this); + +void havocbot_ons_reset_role(entity this); +void havocbot_goalrating_items(entity this, float ratingscale, vector org, float sradius); +void havocbot_goalrating_enemyplayers(entity this, float ratingscale, vector org, float sradius); + +// score rule declarations +const int ST_ONS_CAPS = 1; diff --git a/qcsrc/common/items/_mod.inc b/qcsrc/common/items/_mod.inc index 3b5dd9550f..6214fac042 100644 --- a/qcsrc/common/items/_mod.inc +++ b/qcsrc/common/items/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify #include + +#include diff --git a/qcsrc/common/items/_mod.qh b/qcsrc/common/items/_mod.qh index 158814c557..a04e90b1dc 100644 --- a/qcsrc/common/items/_mod.qh +++ b/qcsrc/common/items/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify #include + +#include diff --git a/qcsrc/common/items/all.qc b/qcsrc/common/items/all.qc index 7a3e500469..7782cda0d6 100644 --- a/qcsrc/common/items/all.qc +++ b/qcsrc/common/items/all.qc @@ -1,9 +1,5 @@ -#ifndef ITEMS_ALL_C -#define ITEMS_ALL_C #include "all.qh" -#include "item/_mod.inc" - void Dump_Items() { FOREACH(Items, true, ITEM_HANDLE(Show, it)); @@ -18,5 +14,3 @@ string Item_Model(string item_mdl) #endif return output; } - -#endif diff --git a/qcsrc/common/items/all.qh b/qcsrc/common/items/all.qh index ff54a3a299..d377776cd5 100644 --- a/qcsrc/common/items/all.qh +++ b/qcsrc/common/items/all.qh @@ -1,6 +1,6 @@ #pragma once -#include +#include #include "item.qh" @@ -29,6 +29,6 @@ GENERIC_COMMAND(dumpitems, "Dump all items to the console") { } } -#ifndef MENUQC +#ifdef GAMEQC string Item_Model(string item_mdl); #endif diff --git a/qcsrc/common/items/item/ammo.qc b/qcsrc/common/items/item/ammo.qc index 525a5b3d93..d7e0dcc687 100644 --- a/qcsrc/common/items/item/ammo.qc +++ b/qcsrc/common/items/item/ammo.qc @@ -1,84 +1 @@ #include "ammo.qh" -#ifdef SVQC - #include -#endif - -#ifndef MENUQC -MODEL(Bullets_ITEM, Item_Model("a_bullets.mdl")); -#endif - -REGISTER_ITEM(Bullets, Ammo) { -#ifndef MENUQC - this.m_model = MDL_Bullets_ITEM; -#endif - this.m_name = "bullets"; - this.m_icon = "ammo_bullets"; -#ifdef SVQC - this.m_botvalue = 2000; - this.m_itemid = IT_NAILS; -#endif -} - -#ifndef MENUQC -MODEL(Cells_ITEM, Item_Model("a_cells.md3")); -#endif - -REGISTER_ITEM(Cells, Ammo) { -#ifndef MENUQC - this.m_model = MDL_Cells_ITEM; -#endif - this.m_name = "cells"; - this.m_icon = "ammo_cells"; -#ifdef SVQC - this.m_botvalue = 2000; - this.m_itemid = IT_CELLS; -#endif -} - -#ifndef MENUQC -MODEL(Plasma_ITEM, Item_Model("a_cells.md3")); -#endif - -REGISTER_ITEM(Plasma, Ammo) { -#ifndef MENUQC - this.m_model = MDL_Plasma_ITEM; -#endif - this.m_name = "plasma"; - this.m_icon = "ammo_plasma"; -#ifdef SVQC - this.m_botvalue = 2000; - this.m_itemid = IT_PLASMA; -#endif -} - -#ifndef MENUQC -MODEL(Rockets_ITEM, Item_Model("a_rockets.md3")); -#endif - -REGISTER_ITEM(Rockets, Ammo) { -#ifndef MENUQC - this.m_model = MDL_Rockets_ITEM; -#endif - this.m_name = "rockets"; - this.m_icon = "ammo_rockets"; -#ifdef SVQC - this.m_botvalue = 3000; - this.m_itemid = IT_ROCKETS; -#endif -} - -#ifndef MENUQC -MODEL(Shells_ITEM, Item_Model("a_shells.md3")); -#endif - -REGISTER_ITEM(Shells, Ammo) { -#ifndef MENUQC - this.m_model = MDL_Shells_ITEM; -#endif - this.m_name = "shells"; - this.m_icon = "ammo_shells"; -#ifdef SVQC - this.m_botvalue = 500; - this.m_itemid = IT_SHELLS; -#endif -} diff --git a/qcsrc/common/items/item/ammo.qh b/qcsrc/common/items/item/ammo.qh index 56c9369192..e1d493fe97 100644 --- a/qcsrc/common/items/item/ammo.qh +++ b/qcsrc/common/items/item/ammo.qh @@ -8,3 +8,87 @@ CLASS(Ammo, Pickup) ATTRIB(Ammo, m_respawntimejitter, float(), GET(g_pickup_respawntimejitter_ammo)); #endif ENDCLASS(Ammo) + +#ifdef SVQC + #include +#endif + +#ifdef GAMEQC +MODEL(Bullets_ITEM, Item_Model("a_bullets.mdl")); +#endif + +REGISTER_ITEM(Bullets, Ammo) { +#ifdef GAMEQC + this.m_model = MDL_Bullets_ITEM; +#endif + this.m_name = "bullets"; + this.m_icon = "ammo_bullets"; +#ifdef SVQC + this.m_botvalue = 2000; + this.m_itemid = IT_NAILS; +#endif +} + +#ifdef GAMEQC +MODEL(Cells_ITEM, Item_Model("a_cells.md3")); +#endif + +REGISTER_ITEM(Cells, Ammo) { +#ifdef GAMEQC + this.m_model = MDL_Cells_ITEM; +#endif + this.m_name = "cells"; + this.m_icon = "ammo_cells"; +#ifdef SVQC + this.m_botvalue = 2000; + this.m_itemid = IT_CELLS; +#endif +} + +#ifdef GAMEQC +MODEL(Plasma_ITEM, Item_Model("a_cells.md3")); +#endif + +REGISTER_ITEM(Plasma, Ammo) { +#ifdef GAMEQC + this.m_model = MDL_Plasma_ITEM; +#endif + this.m_name = "plasma"; + this.m_icon = "ammo_plasma"; +#ifdef SVQC + this.m_botvalue = 2000; + this.m_itemid = IT_PLASMA; +#endif +} + +#ifdef GAMEQC +MODEL(Rockets_ITEM, Item_Model("a_rockets.md3")); +#endif + +REGISTER_ITEM(Rockets, Ammo) { +#ifdef GAMEQC + this.m_model = MDL_Rockets_ITEM; +#endif + this.m_name = "rockets"; + this.m_icon = "ammo_rockets"; +#ifdef SVQC + this.m_botvalue = 3000; + this.m_itemid = IT_ROCKETS; +#endif +} + +#ifdef GAMEQC +MODEL(Shells_ITEM, Item_Model("a_shells.md3")); +#endif + +REGISTER_ITEM(Shells, Ammo) { +#ifdef GAMEQC + this.m_model = MDL_Shells_ITEM; +#endif + this.m_name = "shells"; + this.m_icon = "ammo_shells"; +#ifdef SVQC + this.m_botvalue = 500; + this.m_itemid = IT_SHELLS; +#endif +} diff --git a/qcsrc/common/items/item/armor.qc b/qcsrc/common/items/item/armor.qc index f8669e0e08..cca0b54184 100644 --- a/qcsrc/common/items/item/armor.qc +++ b/qcsrc/common/items/item/armor.qc @@ -1,89 +1 @@ #include "armor.qh" -#ifdef SVQC - #include -#endif - -#ifndef MENUQC -MODEL(ArmorSmall_ITEM, Item_Model("item_armor_small.md3")); -SOUND(ArmorSmall, "misc/armor1"); -#endif - -REGISTER_ITEM(ArmorSmall, Armor) { -#ifndef MENUQC - this.m_model = MDL_ArmorSmall_ITEM; - this.m_sound = SND_ArmorSmall; -#endif - this.m_name = "5 Armor"; - this.m_icon = "armor"; -#ifdef SVQC - this.m_botvalue = BOT_PICKUP_RATING_LOW; - this.m_itemid = IT_ARMOR_SHARD; - this.m_respawntime = GET(g_pickup_respawntime_short); - this.m_respawntimejitter = GET(g_pickup_respawntimejitter_short); -#endif -} - -#ifndef MENUQC -MODEL(ArmorMedium_ITEM, Item_Model("item_armor_medium.md3")); -SOUND(ArmorMedium, "misc/armor10"); -#endif - -REGISTER_ITEM(ArmorMedium, Armor) { -#ifndef MENUQC - this.m_model = MDL_ArmorMedium_ITEM; - this.m_sound = SND_ArmorMedium; -#endif - this.m_name = "25 Armor"; - this.m_icon = "armor"; -#ifdef SVQC - this.m_botvalue = BOT_PICKUP_RATING_MID; - this.m_itemid = IT_ARMOR; - this.m_respawntime = GET(g_pickup_respawntime_medium); - this.m_respawntimejitter = GET(g_pickup_respawntimejitter_medium); -#endif -} - -#ifndef MENUQC -MODEL(ArmorLarge_ITEM, Item_Model("item_armor_big.md3")); -SOUND(ArmorLarge, "misc/armor17_5"); -#endif - -REGISTER_ITEM(ArmorLarge, Armor) { -#ifndef MENUQC - this.m_model = MDL_ArmorLarge_ITEM; - this.m_sound = SND_ArmorLarge; -#endif - this.m_name = "50 Armor"; - this.m_icon = "armor"; - this.m_color = '0 1 0'; - this.m_waypoint = _("Large armor"); -#ifdef SVQC - this.m_botvalue = 20000; // FIXME: higher than BOT_PICKUP_RATING_HIGH? - this.m_itemid = IT_ARMOR; - this.m_respawntime = GET(g_pickup_respawntime_long); - this.m_respawntimejitter = GET(g_pickup_respawntimejitter_long); -#endif -} - -#ifndef MENUQC -MODEL(ArmorMega_ITEM, Item_Model("item_armor_large.md3")); -SOUND(ArmorMega, "misc/armor25"); -#endif - -REGISTER_ITEM(ArmorMega, Armor) { -#ifndef MENUQC - this.m_model = MDL_ArmorMega_ITEM; - this.m_sound = SND_ArmorMega; -#endif - this.m_name = "100 Armor"; - this.m_icon = "item_large_armor"; - this.m_color = '0 1 0'; - this.m_waypoint = _("Mega armor"); - this.m_waypointblink = 2; -#ifdef SVQC - this.m_botvalue = BOT_PICKUP_RATING_HIGH; - this.m_itemid = IT_ARMOR; - this.m_respawntime = GET(g_pickup_respawntime_long); - this.m_respawntimejitter = GET(g_pickup_respawntimejitter_long); -#endif -} diff --git a/qcsrc/common/items/item/armor.qh b/qcsrc/common/items/item/armor.qh index 9078b636d4..7946fb7b5d 100644 --- a/qcsrc/common/items/item/armor.qh +++ b/qcsrc/common/items/item/armor.qh @@ -8,3 +8,92 @@ CLASS(Armor, Pickup) ATTRIB(Armor, m_pickupevalfunc, float(entity player, entity item), commodity_pickupevalfunc); #endif ENDCLASS(Armor) + +#ifdef SVQC + #include +#endif + +#ifdef GAMEQC +MODEL(ArmorSmall_ITEM, Item_Model("item_armor_small.md3")); +SOUND(ArmorSmall, "misc/armor1"); +#endif + +REGISTER_ITEM(ArmorSmall, Armor) { +#ifdef GAMEQC + this.m_model = MDL_ArmorSmall_ITEM; + this.m_sound = SND_ArmorSmall; +#endif + this.m_name = "5 Armor"; + this.m_icon = "armor"; +#ifdef SVQC + this.m_botvalue = BOT_PICKUP_RATING_LOW; + this.m_itemid = IT_ARMOR_SHARD; + this.m_respawntime = GET(g_pickup_respawntime_short); + this.m_respawntimejitter = GET(g_pickup_respawntimejitter_short); +#endif +} + +#ifdef GAMEQC +MODEL(ArmorMedium_ITEM, Item_Model("item_armor_medium.md3")); +SOUND(ArmorMedium, "misc/armor10"); +#endif + +REGISTER_ITEM(ArmorMedium, Armor) { +#ifdef GAMEQC + this.m_model = MDL_ArmorMedium_ITEM; + this.m_sound = SND_ArmorMedium; +#endif + this.m_name = "25 Armor"; + this.m_icon = "armor"; +#ifdef SVQC + this.m_botvalue = BOT_PICKUP_RATING_MID; + this.m_itemid = IT_ARMOR; + this.m_respawntime = GET(g_pickup_respawntime_medium); + this.m_respawntimejitter = GET(g_pickup_respawntimejitter_medium); +#endif +} + +#ifdef GAMEQC +MODEL(ArmorLarge_ITEM, Item_Model("item_armor_big.md3")); +SOUND(ArmorLarge, "misc/armor17_5"); +#endif + +REGISTER_ITEM(ArmorLarge, Armor) { +#ifdef GAMEQC + this.m_model = MDL_ArmorLarge_ITEM; + this.m_sound = SND_ArmorLarge; +#endif + this.m_name = "50 Armor"; + this.m_icon = "armor"; + this.m_color = '0 1 0'; + this.m_waypoint = _("Large armor"); +#ifdef SVQC + this.m_botvalue = 20000; // FIXME: higher than BOT_PICKUP_RATING_HIGH? + this.m_itemid = IT_ARMOR; + this.m_respawntime = GET(g_pickup_respawntime_long); + this.m_respawntimejitter = GET(g_pickup_respawntimejitter_long); +#endif +} + +#ifdef GAMEQC +MODEL(ArmorMega_ITEM, Item_Model("item_armor_large.md3")); +SOUND(ArmorMega, "misc/armor25"); +#endif + +REGISTER_ITEM(ArmorMega, Armor) { +#ifdef GAMEQC + this.m_model = MDL_ArmorMega_ITEM; + this.m_sound = SND_ArmorMega; +#endif + this.m_name = "100 Armor"; + this.m_icon = "item_large_armor"; + this.m_color = '0 1 0'; + this.m_waypoint = _("Mega armor"); + this.m_waypointblink = 2; +#ifdef SVQC + this.m_botvalue = BOT_PICKUP_RATING_HIGH; + this.m_itemid = IT_ARMOR; + this.m_respawntime = GET(g_pickup_respawntime_long); + this.m_respawntimejitter = GET(g_pickup_respawntimejitter_long); +#endif +} diff --git a/qcsrc/common/items/item/health.qc b/qcsrc/common/items/item/health.qc index 93ba2f36e2..49a34c1c8e 100644 --- a/qcsrc/common/items/item/health.qc +++ b/qcsrc/common/items/item/health.qc @@ -1,89 +1 @@ #include "health.qh" -#ifdef SVQC - #include -#endif - -#ifndef MENUQC -MODEL(HealthSmall_ITEM, Item_Model("g_h1.md3")); -SOUND(HealthSmall, "misc/minihealth"); -#endif - -REGISTER_ITEM(HealthSmall, Health) { -#ifndef MENUQC - this.m_model = MDL_HealthSmall_ITEM; - this.m_sound = SND_HealthSmall; -#endif - this.m_name = "5 Health"; - this.m_icon = "health"; -#ifdef SVQC - this.m_botvalue = BOT_PICKUP_RATING_LOW; - this.m_itemid = IT_5HP; - this.m_respawntime = GET(g_pickup_respawntime_short); - this.m_respawntimejitter = GET(g_pickup_respawntimejitter_short); -#endif -} - -#ifndef MENUQC -MODEL(HealthMedium_ITEM, Item_Model("g_h25.md3")); -SOUND(HealthMedium, "misc/mediumhealth"); -#endif - -REGISTER_ITEM(HealthMedium, Health) { -#ifndef MENUQC - this.m_model = MDL_HealthMedium_ITEM; - this.m_sound = SND_HealthMedium; -#endif - this.m_name = "25 Health"; - this.m_icon = "health"; -#ifdef SVQC - this.m_botvalue = BOT_PICKUP_RATING_MID; - this.m_itemid = IT_25HP; - this.m_respawntime = GET(g_pickup_respawntime_short); - this.m_respawntimejitter = GET(g_pickup_respawntimejitter_short); -#endif -} - -#ifndef MENUQC -MODEL(HealthLarge_ITEM, Item_Model("g_h50.md3")); -SOUND(HealthLarge, "misc/mediumhealth"); -#endif - -REGISTER_ITEM(HealthLarge, Health) { -#ifndef MENUQC - this.m_model = MDL_HealthLarge_ITEM; - this.m_sound = SND_HealthLarge; -#endif - this.m_name = "50 Health"; - this.m_icon = "health"; - this.m_color = '1 0 0'; - this.m_waypoint = _("Large health"); -#ifdef SVQC - this.m_botvalue = BOT_PICKUP_RATING_MID; - this.m_itemid = IT_25HP; - this.m_respawntime = GET(g_pickup_respawntime_medium); - this.m_respawntimejitter = GET(g_pickup_respawntimejitter_medium); -#endif -} - -#ifndef MENUQC -MODEL(HealthMega_ITEM, Item_Model("g_h100.md3")); -SOUND(HealthMega, "misc/megahealth"); -#endif - -REGISTER_ITEM(HealthMega, Health) { -#ifndef MENUQC - this.m_model = MDL_HealthMega_ITEM; - this.m_sound = SND_HealthMega; -#endif - this.m_name = "100 Health"; - this.m_icon = "item_mega_health"; - this.m_color = '1 0 0'; - this.m_waypoint = _("Mega health"); - this.m_waypointblink = 2; -#ifdef SVQC - this.m_botvalue = BOT_PICKUP_RATING_HIGH; - this.m_itemid = IT_HEALTH; - this.m_respawntime = GET(g_pickup_respawntime_long); - this.m_respawntimejitter = GET(g_pickup_respawntimejitter_long); -#endif -} diff --git a/qcsrc/common/items/item/health.qh b/qcsrc/common/items/item/health.qh index 8eb463a8f9..1597ba6057 100644 --- a/qcsrc/common/items/item/health.qh +++ b/qcsrc/common/items/item/health.qh @@ -8,3 +8,92 @@ CLASS(Health, Pickup) ATTRIB(Health, m_pickupevalfunc, float(entity player, entity item), commodity_pickupevalfunc); #endif ENDCLASS(Health) + +#ifdef SVQC + #include +#endif + +#ifdef GAMEQC +MODEL(HealthSmall_ITEM, Item_Model("g_h1.md3")); +SOUND(HealthSmall, "misc/minihealth"); +#endif + +REGISTER_ITEM(HealthSmall, Health) { +#ifdef GAMEQC + this.m_model = MDL_HealthSmall_ITEM; + this.m_sound = SND_HealthSmall; +#endif + this.m_name = "5 Health"; + this.m_icon = "health"; +#ifdef SVQC + this.m_botvalue = BOT_PICKUP_RATING_LOW; + this.m_itemid = IT_5HP; + this.m_respawntime = GET(g_pickup_respawntime_short); + this.m_respawntimejitter = GET(g_pickup_respawntimejitter_short); +#endif +} + +#ifdef GAMEQC +MODEL(HealthMedium_ITEM, Item_Model("g_h25.md3")); +SOUND(HealthMedium, "misc/mediumhealth"); +#endif + +REGISTER_ITEM(HealthMedium, Health) { +#ifdef GAMEQC + this.m_model = MDL_HealthMedium_ITEM; + this.m_sound = SND_HealthMedium; +#endif + this.m_name = "25 Health"; + this.m_icon = "health"; +#ifdef SVQC + this.m_botvalue = BOT_PICKUP_RATING_MID; + this.m_itemid = IT_25HP; + this.m_respawntime = GET(g_pickup_respawntime_short); + this.m_respawntimejitter = GET(g_pickup_respawntimejitter_short); +#endif +} + +#ifdef GAMEQC +MODEL(HealthLarge_ITEM, Item_Model("g_h50.md3")); +SOUND(HealthLarge, "misc/mediumhealth"); +#endif + +REGISTER_ITEM(HealthLarge, Health) { +#ifdef GAMEQC + this.m_model = MDL_HealthLarge_ITEM; + this.m_sound = SND_HealthLarge; +#endif + this.m_name = "50 Health"; + this.m_icon = "health"; + this.m_color = '1 0 0'; + this.m_waypoint = _("Large health"); +#ifdef SVQC + this.m_botvalue = BOT_PICKUP_RATING_MID; + this.m_itemid = IT_25HP; + this.m_respawntime = GET(g_pickup_respawntime_medium); + this.m_respawntimejitter = GET(g_pickup_respawntimejitter_medium); +#endif +} + +#ifdef GAMEQC +MODEL(HealthMega_ITEM, Item_Model("g_h100.md3")); +SOUND(HealthMega, "misc/megahealth"); +#endif + +REGISTER_ITEM(HealthMega, Health) { +#ifdef GAMEQC + this.m_model = MDL_HealthMega_ITEM; + this.m_sound = SND_HealthMega; +#endif + this.m_name = "100 Health"; + this.m_icon = "item_mega_health"; + this.m_color = '1 0 0'; + this.m_waypoint = _("Mega health"); + this.m_waypointblink = 2; +#ifdef SVQC + this.m_botvalue = BOT_PICKUP_RATING_HIGH; + this.m_itemid = IT_HEALTH; + this.m_respawntime = GET(g_pickup_respawntime_long); + this.m_respawntimejitter = GET(g_pickup_respawntimejitter_long); +#endif +} diff --git a/qcsrc/common/items/item/jetpack.qc b/qcsrc/common/items/item/jetpack.qc index 7fd29e299f..ec09d5c45d 100644 --- a/qcsrc/common/items/item/jetpack.qc +++ b/qcsrc/common/items/item/jetpack.qc @@ -1,66 +1 @@ -#ifdef SVQC - #include -#endif - -#include "ammo.qh" -#include "powerup.qh" - -#ifndef SVQC -.int m_itemid; -#endif - -#ifndef MENUQC -MODEL(Jetpack_ITEM, Item_Model("g_jetpack.md3")); -#endif - -REGISTER_ITEM(Jetpack, Powerup) { -#ifndef MENUQC - this.m_model = MDL_Jetpack_ITEM; - this.m_itemid = IT_JETPACK; -#endif - this.m_name = "Jet pack"; - this.m_icon = "jetpack"; - this.m_color = '0.5 0.5 0.5'; - this.m_waypoint = _("Jet Pack"); - this.m_waypointblink = 2; -#ifdef SVQC - this.m_botvalue = BOT_PICKUP_RATING_LOW; - this.m_pickupevalfunc = commodity_pickupevalfunc; -#endif -} - -#ifndef MENUQC -MODEL(JetpackFuel_ITEM, Item_Model("g_fuel.md3")); -#endif - -REGISTER_ITEM(JetpackFuel, Ammo) { -#ifndef MENUQC - this.m_model = MDL_JetpackFuel_ITEM; -#endif - this.m_name = "Fuel"; - this.m_icon = "ammo_fuel"; -#ifdef SVQC - this.m_botvalue = BOT_PICKUP_RATING_LOW; - this.m_itemid = IT_FUEL; -#endif -} - -#ifndef MENUQC -MODEL(JetpackRegen_ITEM, Item_Model("g_fuelregen.md3")); -#endif - -REGISTER_ITEM(JetpackRegen, Powerup) { -#ifndef MENUQC - this.m_model = MDL_JetpackRegen_ITEM; -#endif - this.m_name = "Fuel regenerator"; - this.m_icon = "fuelregen"; - this.m_color = '1 0.5 0'; - this.m_waypoint = _("Fuel regen"); - this.m_waypointblink = 2; -#ifdef SVQC - this.m_botvalue = BOT_PICKUP_RATING_LOW; - this.m_itemid = IT_FUEL_REGEN; - this.m_pickupevalfunc = commodity_pickupevalfunc; -#endif -} +#include "jetpack.qh" diff --git a/qcsrc/common/items/item/jetpack.qh b/qcsrc/common/items/item/jetpack.qh new file mode 100644 index 0000000000..8334c791ab --- /dev/null +++ b/qcsrc/common/items/item/jetpack.qh @@ -0,0 +1,68 @@ +#pragma once + +#ifdef SVQC + #include +#endif + +#include "ammo.qh" +#include "powerup.qh" + +#ifndef SVQC +.int m_itemid; +#endif + +#ifdef GAMEQC +MODEL(Jetpack_ITEM, Item_Model("g_jetpack.md3")); +#endif + +REGISTER_ITEM(Jetpack, Powerup) { +#ifdef GAMEQC + this.m_model = MDL_Jetpack_ITEM; + this.m_itemid = IT_JETPACK; +#endif + this.m_name = "Jet pack"; + this.m_icon = "jetpack"; + this.m_color = '0.5 0.5 0.5'; + this.m_waypoint = _("Jet Pack"); + this.m_waypointblink = 2; +#ifdef SVQC + this.m_botvalue = BOT_PICKUP_RATING_LOW; + this.m_pickupevalfunc = commodity_pickupevalfunc; +#endif +} + +#ifdef GAMEQC +MODEL(JetpackFuel_ITEM, Item_Model("g_fuel.md3")); +#endif + +REGISTER_ITEM(JetpackFuel, Ammo) { +#ifdef GAMEQC + this.m_model = MDL_JetpackFuel_ITEM; +#endif + this.m_name = "Fuel"; + this.m_icon = "ammo_fuel"; +#ifdef SVQC + this.m_botvalue = BOT_PICKUP_RATING_LOW; + this.m_itemid = IT_FUEL; +#endif +} + +#ifdef GAMEQC +MODEL(JetpackRegen_ITEM, Item_Model("g_fuelregen.md3")); +#endif + +REGISTER_ITEM(JetpackRegen, Powerup) { +#ifdef GAMEQC + this.m_model = MDL_JetpackRegen_ITEM; +#endif + this.m_name = "Fuel regenerator"; + this.m_icon = "fuelregen"; + this.m_color = '1 0.5 0'; + this.m_waypoint = _("Fuel regen"); + this.m_waypointblink = 2; +#ifdef SVQC + this.m_botvalue = BOT_PICKUP_RATING_LOW; + this.m_itemid = IT_FUEL_REGEN; + this.m_pickupevalfunc = commodity_pickupevalfunc; +#endif +} diff --git a/qcsrc/common/items/item/pickup.qh b/qcsrc/common/items/item/pickup.qh index 136c8dab6d..204a49921d 100644 --- a/qcsrc/common/items/item/pickup.qh +++ b/qcsrc/common/items/item/pickup.qh @@ -1,9 +1,33 @@ #pragma once +#ifdef SVQC +PROPERTY(float, g_pickup_respawntime_weapon) +PROPERTY(float, g_pickup_respawntime_superweapon) +PROPERTY(float, g_pickup_respawntime_ammo) +PROPERTY(float, g_pickup_respawntime_short) +PROPERTY(float, g_pickup_respawntime_medium) +PROPERTY(float, g_pickup_respawntime_long) +PROPERTY(float, g_pickup_respawntime_powerup) +PROPERTY(float, g_pickup_respawntimejitter_weapon) +PROPERTY(float, g_pickup_respawntimejitter_superweapon) +PROPERTY(float, g_pickup_respawntimejitter_ammo) +PROPERTY(float, g_pickup_respawntimejitter_short) +PROPERTY(float, g_pickup_respawntimejitter_medium) +PROPERTY(float, g_pickup_respawntimejitter_long) +PROPERTY(float, g_pickup_respawntimejitter_powerup) +#endif + +// pickup ratings for bot logic +const int BOT_PICKUP_RATING_LOW = 2500; +const int BOT_PICKUP_RATING_MID = 5000; +const int BOT_PICKUP_RATING_HIGH = 10000; + #include #include +#include + CLASS(Pickup, GameItem) -#ifndef MENUQC +#ifdef GAMEQC ATTRIB(Pickup, m_model, Model); ATTRIB(Pickup, m_sound, Sound, SND_ITEMPICKUP); #endif @@ -13,12 +37,12 @@ CLASS(Pickup, GameItem) TC(Pickup, this); LOG_INFOF("%s: %s\n", etos(this), this.m_name); } + ATTRIB(Pickup, m_itemid, int, 0); #ifdef SVQC ATTRIB(Pickup, m_mins, vector, '-16 -16 0'); ATTRIB(Pickup, m_maxs, vector, '16 16 32'); ATTRIB(Pickup, m_botvalue, int, 0); ATTRIB(Pickup, m_itemflags, int, 0); - ATTRIB(Pickup, m_itemid, int, 0); float generic_pickupevalfunc(entity player, entity item); ATTRIB(Pickup, m_pickupevalfunc, float(entity player, entity item), generic_pickupevalfunc); ATTRIB(Pickup, m_respawntime, float()); diff --git a/qcsrc/common/items/item/powerup.qc b/qcsrc/common/items/item/powerup.qc index 375f958a16..7c7405b75b 100644 --- a/qcsrc/common/items/item/powerup.qc +++ b/qcsrc/common/items/item/powerup.qc @@ -1,37 +1 @@ #include "powerup.qh" - -#ifndef MENUQC -MODEL(Strength_ITEM, Item_Model("g_strength.md3")); -SOUND(Strength, "misc/powerup"); -#endif - -REGISTER_ITEM(Strength, Powerup) { -#ifndef MENUQC - this.m_model = MDL_Strength_ITEM; - this.m_sound = SND_Strength; -#endif - this.m_name = "Strength Powerup"; - this.m_icon = "strength"; - this.m_color = '0 0 1'; - this.m_waypoint = _("Strength"); - this.m_waypointblink = 2; - this.m_itemid = IT_STRENGTH; -} - -#ifndef MENUQC -MODEL(Shield_ITEM, Item_Model("g_invincible.md3")); -SOUND(Shield, "misc/powerup_shield"); -#endif - -REGISTER_ITEM(Shield, Powerup) { -#ifndef MENUQC - this.m_model = MDL_Shield_ITEM; - this.m_sound = SND_Shield; -#endif - this.m_name = "Shield"; - this.m_icon = "shield"; - this.m_color = '1 0 1'; - this.m_waypoint = _("Shield"); - this.m_waypointblink = 2; - this.m_itemid = IT_INVINCIBLE; -} diff --git a/qcsrc/common/items/item/powerup.qh b/qcsrc/common/items/item/powerup.qh index ca17c970ba..002be54f80 100644 --- a/qcsrc/common/items/item/powerup.qh +++ b/qcsrc/common/items/item/powerup.qh @@ -16,3 +16,39 @@ CLASS(Powerup, Pickup) ATTRIB(Powerup, m_respawntimejitter, float(), GET(g_pickup_respawntimejitter_powerup)); #endif ENDCLASS(Powerup) + +#ifdef GAMEQC +MODEL(Strength_ITEM, Item_Model("g_strength.md3")); +SOUND(Strength, "misc/powerup"); +#endif + +REGISTER_ITEM(Strength, Powerup) { +#ifdef GAMEQC + this.m_model = MDL_Strength_ITEM; + this.m_sound = SND_Strength; +#endif + this.m_name = "Strength Powerup"; + this.m_icon = "strength"; + this.m_color = '0 0 1'; + this.m_waypoint = _("Strength"); + this.m_waypointblink = 2; + this.m_itemid = IT_STRENGTH; +} + +#ifdef GAMEQC +MODEL(Shield_ITEM, Item_Model("g_invincible.md3")); +SOUND(Shield, "misc/powerup_shield"); +#endif + +REGISTER_ITEM(Shield, Powerup) { +#ifdef GAMEQC + this.m_model = MDL_Shield_ITEM; + this.m_sound = SND_Shield; +#endif + this.m_name = "Shield"; + this.m_icon = "shield"; + this.m_color = '1 0 1'; + this.m_waypoint = _("Shield"); + this.m_waypointblink = 2; + this.m_itemid = IT_INVINCIBLE; +} diff --git a/qcsrc/common/mapinfo.qc b/qcsrc/common/mapinfo.qc index 2bc84c4593..f3213527ba 100644 --- a/qcsrc/common/mapinfo.qc +++ b/qcsrc/common/mapinfo.qc @@ -1,13 +1,12 @@ +#include "mapinfo.qh" #if defined(CSQC) #include "../client/defs.qh" #include "util.qh" - #include - #include "mapinfo.qh" + #include #elif defined(MENUQC) #elif defined(SVQC) #include "util.qh" #include - #include "mapinfo.qh" #endif // generic string stuff @@ -1298,7 +1297,7 @@ int MapInfo_ForbiddenFlags() { int f = MAPINFO_FLAG_FORBIDDEN; -#ifndef MENUQC +#ifdef GAMEQC if (!cvar("g_maplist_allow_hidden")) #endif f |= MAPINFO_FLAG_HIDDEN; diff --git a/qcsrc/common/minigames/_mod.inc b/qcsrc/common/minigames/_mod.inc index 43ad69de59..4171f475cd 100644 --- a/qcsrc/common/minigames/_mod.inc +++ b/qcsrc/common/minigames/_mod.inc @@ -1,5 +1,7 @@ // generated file; do not modify -#include +#ifdef CSQC + #include +#endif #include #ifdef CSQC #include @@ -7,3 +9,5 @@ #ifdef SVQC #include #endif + +#include diff --git a/qcsrc/common/minigames/_mod.qh b/qcsrc/common/minigames/_mod.qh index 36f5de2f15..e0daf8f677 100644 --- a/qcsrc/common/minigames/_mod.qh +++ b/qcsrc/common/minigames/_mod.qh @@ -1,3 +1,13 @@ // generated file; do not modify -#include +#ifdef CSQC + #include +#endif #include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif + +#include diff --git a/qcsrc/common/minigames/minigame/bd.qc b/qcsrc/common/minigames/minigame/bd.qc index 041e56339c..a0a93b1910 100644 --- a/qcsrc/common/minigames/minigame/bd.qc +++ b/qcsrc/common/minigames/minigame/bd.qc @@ -1,3 +1,4 @@ +#include "bd.qh" REGISTER_MINIGAME(bd, "Bulldozer"); const int BD_TURN_MOVE = 0x0100; // player must move the bulldozer diff --git a/qcsrc/common/minigames/minigame/bd.qh b/qcsrc/common/minigames/minigame/bd.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/minigames/minigame/bd.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/minigames/minigame/c4.qc b/qcsrc/common/minigames/minigame/c4.qc index 497ef2afc3..86c9f1889f 100644 --- a/qcsrc/common/minigames/minigame/c4.qc +++ b/qcsrc/common/minigames/minigame/c4.qc @@ -1,3 +1,4 @@ +#include "c4.qh" REGISTER_MINIGAME(c4, "Connect Four"); const float C4_TURN_PLACE = 0x0100; // player has to place a piece on the board diff --git a/qcsrc/common/minigames/minigame/c4.qh b/qcsrc/common/minigames/minigame/c4.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/minigames/minigame/c4.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/minigames/minigame/nmm.qc b/qcsrc/common/minigames/minigame/nmm.qc index 78fb30f1df..bdb02ab962 100644 --- a/qcsrc/common/minigames/minigame/nmm.qc +++ b/qcsrc/common/minigames/minigame/nmm.qc @@ -1,3 +1,4 @@ +#include "nmm.qh" REGISTER_MINIGAME(nmm, "Nine Men's Morris"); const int NMM_TURN_PLACE = 0x0100; // player has to place a piece on the board diff --git a/qcsrc/common/minigames/minigame/nmm.qh b/qcsrc/common/minigames/minigame/nmm.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/minigames/minigame/nmm.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/minigames/minigame/pong.qc b/qcsrc/common/minigames/minigame/pong.qc index d535b8c85d..3574b9f74b 100644 --- a/qcsrc/common/minigames/minigame/pong.qc +++ b/qcsrc/common/minigames/minigame/pong.qc @@ -1,3 +1,4 @@ +#include "pong.qh" REGISTER_MINIGAME(pong, "Pong"); // minigame flags diff --git a/qcsrc/common/minigames/minigame/pong.qh b/qcsrc/common/minigames/minigame/pong.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/minigames/minigame/pong.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/minigames/minigame/pp.qc b/qcsrc/common/minigames/minigame/pp.qc index f9e0f79b9a..6689883875 100644 --- a/qcsrc/common/minigames/minigame/pp.qc +++ b/qcsrc/common/minigames/minigame/pp.qc @@ -1,3 +1,4 @@ +#include "pp.qh" REGISTER_MINIGAME(pp, "Push-Pull"); const int PP_TURN_PLACE = 0x0100; // player has to place a piece on the board diff --git a/qcsrc/common/minigames/minigame/pp.qh b/qcsrc/common/minigames/minigame/pp.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/minigames/minigame/pp.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/minigames/minigame/ps.qc b/qcsrc/common/minigames/minigame/ps.qc index c9ef8bd67a..b2831744de 100644 --- a/qcsrc/common/minigames/minigame/ps.qc +++ b/qcsrc/common/minigames/minigame/ps.qc @@ -1,3 +1,4 @@ +#include "ps.qh" REGISTER_MINIGAME(ps, "Peg Solitaire"); const float PS_TURN_MOVE = 0x0100; // player has to click on a piece on the board diff --git a/qcsrc/common/minigames/minigame/ps.qh b/qcsrc/common/minigames/minigame/ps.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/minigames/minigame/ps.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/minigames/minigame/snake.qc b/qcsrc/common/minigames/minigame/snake.qc index 4248d589a1..d5f0ea0787 100644 --- a/qcsrc/common/minigames/minigame/snake.qc +++ b/qcsrc/common/minigames/minigame/snake.qc @@ -1,3 +1,4 @@ +#include "snake.qh" REGISTER_MINIGAME(snake, "Snake"); // SNAAAAKE const float SNAKE_TURN_MOVE = 0x0100; // the snake is moving, player must control it diff --git a/qcsrc/common/minigames/minigame/snake.qh b/qcsrc/common/minigames/minigame/snake.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/minigames/minigame/snake.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/minigames/minigame/ttt.qc b/qcsrc/common/minigames/minigame/ttt.qc index 9c0ece1633..9db2201a3f 100644 --- a/qcsrc/common/minigames/minigame/ttt.qc +++ b/qcsrc/common/minigames/minigame/ttt.qc @@ -1,3 +1,4 @@ +#include "ttt.qh" REGISTER_MINIGAME(ttt, "Tic Tac Toe"); const int TTT_TURN_PLACE = 0x0100; // player has to place a piece on the board diff --git a/qcsrc/common/minigames/minigame/ttt.qh b/qcsrc/common/minigames/minigame/ttt.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/minigames/minigame/ttt.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/minigames/sv_minigames.qc b/qcsrc/common/minigames/sv_minigames.qc index da04340a63..5f1d1280b9 100644 --- a/qcsrc/common/minigames/sv_minigames.qc +++ b/qcsrc/common/minigames/sv_minigames.qc @@ -1,3 +1,4 @@ +#include "sv_minigames.qh" #include "minigames.qh" void player_clear_minigame(entity player) diff --git a/qcsrc/common/monsters/_all.inc b/qcsrc/common/monsters/_all.inc new file mode 100644 index 0000000000..8bc63f720a --- /dev/null +++ b/qcsrc/common/monsters/_all.inc @@ -0,0 +1,2 @@ +#include "_all.qh" +#include "_mod.inc" diff --git a/qcsrc/common/monsters/_all.qh b/qcsrc/common/monsters/_all.qh new file mode 100644 index 0000000000..947026dd59 --- /dev/null +++ b/qcsrc/common/monsters/_all.qh @@ -0,0 +1,2 @@ +#pragma once +#include "_mod.qh" diff --git a/qcsrc/common/monsters/_mod.inc b/qcsrc/common/monsters/_mod.inc index f9d80b3f4e..0478f09eec 100644 --- a/qcsrc/common/monsters/_mod.inc +++ b/qcsrc/common/monsters/_mod.inc @@ -1,4 +1,6 @@ // generated file; do not modify #include #include -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/monsters/_mod.qh b/qcsrc/common/monsters/_mod.qh index 48427f94f7..39d594e4c5 100644 --- a/qcsrc/common/monsters/_mod.qh +++ b/qcsrc/common/monsters/_mod.qh @@ -1,4 +1,6 @@ // generated file; do not modify #include #include -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/monsters/all.qc b/qcsrc/common/monsters/all.qc index fa3f65193a..a262a23450 100644 --- a/qcsrc/common/monsters/all.qc +++ b/qcsrc/common/monsters/all.qc @@ -1,3 +1,4 @@ +#include "all.qh" #ifndef MONSTERS_ALL_C #define MONSTERS_ALL_C diff --git a/qcsrc/common/monsters/monster.qh b/qcsrc/common/monsters/monster.qh index 5187c7f56b..341b92e7b5 100644 --- a/qcsrc/common/monsters/monster.qh +++ b/qcsrc/common/monsters/monster.qh @@ -11,7 +11,7 @@ #include #endif -#ifndef MENUQC +#ifdef GAMEQC #include "../animdecide.qh" #include "../anim.qh" vector animfixfps(entity e, vector a, vector b); diff --git a/qcsrc/common/monsters/monster/mage.qc b/qcsrc/common/monsters/monster/mage.qc index 509fa0aca1..650999de1e 100644 --- a/qcsrc/common/monsters/monster/mage.qc +++ b/qcsrc/common/monsters/monster/mage.qc @@ -1,39 +1,4 @@ -#ifndef MAGE_H -#define MAGE_H - -#ifndef MENUQC -MODEL(MON_MAGE, M_Model("mage.dpm")); -#endif - -CLASS(Mage, Monster) - ATTRIB(Mage, spawnflags, int, MON_FLAG_MELEE | MON_FLAG_RANGED); - ATTRIB(Mage, mins, vector, '-36 -36 -24'); - ATTRIB(Mage, maxs, vector, '36 36 50'); -#ifndef MENUQC - ATTRIB(Mage, m_model, Model, MDL_MON_MAGE); -#endif - ATTRIB(Mage, netname, string, "mage"); - ATTRIB(Mage, monster_name, string, _("Mage")); -ENDCLASS(Mage) - -REGISTER_MONSTER(MAGE, NEW(Mage)) { -#ifndef MENUQC - this.mr_precache(this); -#endif -} - -#include -#include - -CLASS(MageSpike, PortoLaunch) -/* flags */ ATTRIB(MageSpike, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); -/* impulse */ ATTRIB(MageSpike, impulse, int, 9); -/* refname */ ATTRIB(MageSpike, netname, string, "magespike"); -/* wepname */ ATTRIB(MageSpike, m_name, string, _("Mage spike")); -ENDCLASS(MageSpike) -REGISTER_WEAPON(MAGE_SPIKE, NEW(MageSpike)); - -#endif +#include "mage.qh" #ifdef IMPLEMENTATION @@ -459,7 +424,7 @@ METHOD(Mage, mr_death, bool(Mage this, entity actor)) } #endif -#ifndef MENUQC +#ifdef GAMEQC METHOD(Mage, mr_anim, bool(Mage this, entity actor)) { TC(Mage, this); diff --git a/qcsrc/common/monsters/monster/mage.qh b/qcsrc/common/monsters/monster/mage.qh new file mode 100644 index 0000000000..04826b39b5 --- /dev/null +++ b/qcsrc/common/monsters/monster/mage.qh @@ -0,0 +1,33 @@ +#pragma once + +#ifdef GAMEQC +MODEL(MON_MAGE, M_Model("mage.dpm")); +#endif + +CLASS(Mage, Monster) + ATTRIB(Mage, spawnflags, int, MON_FLAG_MELEE | MON_FLAG_RANGED); + ATTRIB(Mage, mins, vector, '-36 -36 -24'); + ATTRIB(Mage, maxs, vector, '36 36 50'); +#ifdef GAMEQC + ATTRIB(Mage, m_model, Model, MDL_MON_MAGE); +#endif + ATTRIB(Mage, netname, string, "mage"); + ATTRIB(Mage, monster_name, string, _("Mage")); +ENDCLASS(Mage) + +REGISTER_MONSTER(MAGE, NEW(Mage)) { +#ifdef GAMEQC + this.mr_precache(this); +#endif +} + +#include +#include + +CLASS(MageSpike, PortoLaunch) +/* flags */ ATTRIB(MageSpike, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); +/* impulse */ ATTRIB(MageSpike, impulse, int, 9); +/* refname */ ATTRIB(MageSpike, netname, string, "magespike"); +/* wepname */ ATTRIB(MageSpike, m_name, string, _("Mage spike")); +ENDCLASS(MageSpike) +REGISTER_WEAPON(MAGE_SPIKE, NEW(MageSpike)); diff --git a/qcsrc/common/monsters/monster/shambler.qc b/qcsrc/common/monsters/monster/shambler.qc index eb615fb554..3ba01fe21f 100644 --- a/qcsrc/common/monsters/monster/shambler.qc +++ b/qcsrc/common/monsters/monster/shambler.qc @@ -1,28 +1,4 @@ -#ifndef SHAMBLER_H -#define SHAMBLER_H - -#ifndef MENUQC -MODEL(MON_SHAMBLER, M_Model("shambler.mdl")); -#endif - -CLASS(Shambler, Monster) - ATTRIB(Shambler, spawnflags, int, MONSTER_SIZE_BROKEN | MON_FLAG_SUPERMONSTER | MON_FLAG_MELEE | MON_FLAG_RANGED); - ATTRIB(Shambler, mins, vector, '-41 -41 -31'); - ATTRIB(Shambler, maxs, vector, '41 41 65'); -#ifndef MENUQC - ATTRIB(Shambler, m_model, Model, MDL_MON_SHAMBLER); -#endif - ATTRIB(Shambler, netname, string, "shambler"); - ATTRIB(Shambler, monster_name, string, _("Shambler")); -ENDCLASS(Shambler) - -REGISTER_MONSTER(SHAMBLER, NEW(Shambler)) { -#ifndef MENUQC - this.mr_precache(this); -#endif -} - -#endif +#include "shambler.qh" #ifdef IMPLEMENTATION @@ -250,7 +226,7 @@ METHOD(Shambler, mr_death, bool(Shambler this, entity actor)) return true; } #endif -#ifndef MENUQC +#ifdef GAMEQC METHOD(Shambler, mr_anim, bool(Shambler this, entity actor)) { TC(Shambler, this); diff --git a/qcsrc/common/monsters/monster/shambler.qh b/qcsrc/common/monsters/monster/shambler.qh new file mode 100644 index 0000000000..0824b55879 --- /dev/null +++ b/qcsrc/common/monsters/monster/shambler.qh @@ -0,0 +1,22 @@ +#pragma once + +#ifdef GAMEQC +MODEL(MON_SHAMBLER, M_Model("shambler.mdl")); +#endif + +CLASS(Shambler, Monster) + ATTRIB(Shambler, spawnflags, int, MONSTER_SIZE_BROKEN | MON_FLAG_SUPERMONSTER | MON_FLAG_MELEE | MON_FLAG_RANGED); + ATTRIB(Shambler, mins, vector, '-41 -41 -31'); + ATTRIB(Shambler, maxs, vector, '41 41 65'); +#ifdef GAMEQC + ATTRIB(Shambler, m_model, Model, MDL_MON_SHAMBLER); +#endif + ATTRIB(Shambler, netname, string, "shambler"); + ATTRIB(Shambler, monster_name, string, _("Shambler")); +ENDCLASS(Shambler) + +REGISTER_MONSTER(SHAMBLER, NEW(Shambler)) { +#ifdef GAMEQC + this.mr_precache(this); +#endif +} diff --git a/qcsrc/common/monsters/monster/spider.qc b/qcsrc/common/monsters/monster/spider.qc index 9fa73508c1..43a07e650c 100644 --- a/qcsrc/common/monsters/monster/spider.qc +++ b/qcsrc/common/monsters/monster/spider.qc @@ -1,38 +1,4 @@ -#ifndef SPIDER_H -#define SPIDER_H - -#ifndef MENUQC -MODEL(MON_SPIDER, M_Model("spider.dpm")); -#endif - -CLASS(Spider, Monster) - ATTRIB(Spider, spawnflags, int, MON_FLAG_MELEE | MON_FLAG_RANGED | MON_FLAG_RIDE); - ATTRIB(Spider, mins, vector, '-18 -18 -25'); - ATTRIB(Spider, maxs, vector, '18 18 30'); -#ifndef MENUQC - ATTRIB(Spider, m_model, Model, MDL_MON_SPIDER); -#endif - ATTRIB(Spider, netname, string, "spider"); - ATTRIB(Spider, monster_name, string, _("Spider")); -ENDCLASS(Spider) - -REGISTER_MONSTER(SPIDER, NEW(Spider)) { -#ifndef MENUQC - this.mr_precache(this); -#endif -} - -#include - -CLASS(SpiderAttack, PortoLaunch) -/* flags */ ATTRIB(SpiderAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); -/* impulse */ ATTRIB(SpiderAttack, impulse, int, 9); -/* refname */ ATTRIB(SpiderAttack, netname, string, "spider"); -/* wepname */ ATTRIB(SpiderAttack, m_name, string, _("Spider attack")); -ENDCLASS(SpiderAttack) -REGISTER_WEAPON(SPIDER_ATTACK, NEW(SpiderAttack)); - -#endif +#include "spider.qh" #ifdef IMPLEMENTATION @@ -249,7 +215,7 @@ METHOD(Spider, mr_death, bool(Spider this, entity actor)) return true; } #endif -#ifndef MENUQC +#ifdef GAMEQC METHOD(Spider, mr_anim, bool(Spider this, entity actor)) { TC(Spider, this); diff --git a/qcsrc/common/monsters/monster/spider.qh b/qcsrc/common/monsters/monster/spider.qh new file mode 100644 index 0000000000..1da750d4ce --- /dev/null +++ b/qcsrc/common/monsters/monster/spider.qh @@ -0,0 +1,32 @@ +#pragma once + +#ifdef GAMEQC +MODEL(MON_SPIDER, M_Model("spider.dpm")); +#endif + +CLASS(Spider, Monster) + ATTRIB(Spider, spawnflags, int, MON_FLAG_MELEE | MON_FLAG_RANGED | MON_FLAG_RIDE); + ATTRIB(Spider, mins, vector, '-18 -18 -25'); + ATTRIB(Spider, maxs, vector, '18 18 30'); +#ifdef GAMEQC + ATTRIB(Spider, m_model, Model, MDL_MON_SPIDER); +#endif + ATTRIB(Spider, netname, string, "spider"); + ATTRIB(Spider, monster_name, string, _("Spider")); +ENDCLASS(Spider) + +REGISTER_MONSTER(SPIDER, NEW(Spider)) { +#ifdef GAMEQC + this.mr_precache(this); +#endif +} + +#include + +CLASS(SpiderAttack, PortoLaunch) +/* flags */ ATTRIB(SpiderAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); +/* impulse */ ATTRIB(SpiderAttack, impulse, int, 9); +/* refname */ ATTRIB(SpiderAttack, netname, string, "spider"); +/* wepname */ ATTRIB(SpiderAttack, m_name, string, _("Spider attack")); +ENDCLASS(SpiderAttack) +REGISTER_WEAPON(SPIDER_ATTACK, NEW(SpiderAttack)); diff --git a/qcsrc/common/monsters/monster/wyvern.qc b/qcsrc/common/monsters/monster/wyvern.qc index 33a3c44599..48e7ad61dc 100644 --- a/qcsrc/common/monsters/monster/wyvern.qc +++ b/qcsrc/common/monsters/monster/wyvern.qc @@ -1,38 +1,4 @@ -#ifndef WYVERN_H -#define WYVERN_H - -#ifndef MENUQC -MODEL(MON_WYVERN, M_Model("wizard.mdl")); -#endif - -CLASS(Wyvern, Monster) - ATTRIB(Wyvern, spawnflags, int, MONSTER_TYPE_FLY | MONSTER_SIZE_BROKEN | MON_FLAG_RANGED | MON_FLAG_RIDE); - ATTRIB(Wyvern, mins, vector, '-20 -20 -58'); - ATTRIB(Wyvern, maxs, vector, '20 20 20'); -#ifndef MENUQC - ATTRIB(Wyvern, m_model, Model, MDL_MON_WYVERN); -#endif - ATTRIB(Wyvern, netname, string, "wyvern"); - ATTRIB(Wyvern, monster_name, string, _("Wyvern")); -ENDCLASS(Wyvern) - -REGISTER_MONSTER(WYVERN, NEW(Wyvern)) { -#ifndef MENUQC - this.mr_precache(this); -#endif -} - -#include - -CLASS(WyvernAttack, PortoLaunch) -/* flags */ ATTRIB(WyvernAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); -/* impulse */ ATTRIB(WyvernAttack, impulse, int, 9); -/* refname */ ATTRIB(WyvernAttack, netname, string, "wyvern"); -/* wepname */ ATTRIB(WyvernAttack, m_name, string, _("Wyvern attack")); -ENDCLASS(WyvernAttack) -REGISTER_WEAPON(WYVERN_ATTACK, NEW(WyvernAttack)); - -#endif +#include "wyvern.qh" #ifdef IMPLEMENTATION @@ -170,7 +136,7 @@ METHOD(Wyvern, mr_death, bool(Wyvern this, entity actor)) return true; } #endif -#ifndef MENUQC +#ifdef GAMEQC METHOD(Wyvern, mr_anim, bool(Wyvern this, entity actor)) { TC(Wyvern, this); diff --git a/qcsrc/common/monsters/monster/wyvern.qh b/qcsrc/common/monsters/monster/wyvern.qh new file mode 100644 index 0000000000..0982eb176b --- /dev/null +++ b/qcsrc/common/monsters/monster/wyvern.qh @@ -0,0 +1,32 @@ +#pragma once + +#ifdef GAMEQC +MODEL(MON_WYVERN, M_Model("wizard.mdl")); +#endif + +CLASS(Wyvern, Monster) + ATTRIB(Wyvern, spawnflags, int, MONSTER_TYPE_FLY | MONSTER_SIZE_BROKEN | MON_FLAG_RANGED | MON_FLAG_RIDE); + ATTRIB(Wyvern, mins, vector, '-20 -20 -58'); + ATTRIB(Wyvern, maxs, vector, '20 20 20'); +#ifdef GAMEQC + ATTRIB(Wyvern, m_model, Model, MDL_MON_WYVERN); +#endif + ATTRIB(Wyvern, netname, string, "wyvern"); + ATTRIB(Wyvern, monster_name, string, _("Wyvern")); +ENDCLASS(Wyvern) + +REGISTER_MONSTER(WYVERN, NEW(Wyvern)) { +#ifdef GAMEQC + this.mr_precache(this); +#endif +} + +#include + +CLASS(WyvernAttack, PortoLaunch) +/* flags */ ATTRIB(WyvernAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); +/* impulse */ ATTRIB(WyvernAttack, impulse, int, 9); +/* refname */ ATTRIB(WyvernAttack, netname, string, "wyvern"); +/* wepname */ ATTRIB(WyvernAttack, m_name, string, _("Wyvern attack")); +ENDCLASS(WyvernAttack) +REGISTER_WEAPON(WYVERN_ATTACK, NEW(WyvernAttack)); diff --git a/qcsrc/common/monsters/monster/zombie.qc b/qcsrc/common/monsters/monster/zombie.qc index fd270a1e1e..3f43ea0f09 100644 --- a/qcsrc/common/monsters/monster/zombie.qc +++ b/qcsrc/common/monsters/monster/zombie.qc @@ -1,28 +1,4 @@ -#ifndef ZOMBIE_H -#define ZOMBIE_H - -#ifndef MENUQC -MODEL(MON_ZOMBIE, M_Model("zombie.dpm")); -#endif - -CLASS(Zombie, Monster) - ATTRIB(Zombie, spawnflags, int, MON_FLAG_MELEE | MON_FLAG_RIDE); - ATTRIB(Zombie, mins, vector, '-18 -18 -25'); - ATTRIB(Zombie, maxs, vector, '18 18 47'); -#ifndef MENUQC - ATTRIB(Zombie, m_model, Model, MDL_MON_ZOMBIE); -#endif - ATTRIB(Zombie, netname, string, "zombie"); - ATTRIB(Zombie, monster_name, string, _("Zombie")); -ENDCLASS(Zombie) - -REGISTER_MONSTER(ZOMBIE, NEW(Zombie)) { -#ifndef MENUQC - this.mr_precache(this); -#endif -} - -#endif +#include "zombie.qh" #ifdef IMPLEMENTATION @@ -180,7 +156,7 @@ METHOD(Zombie, mr_death, bool(Zombie this, entity actor)) return true; } #endif -#ifndef MENUQC +#ifdef GAMEQC METHOD(Zombie, mr_anim, bool(Zombie this, entity actor)) { TC(Zombie, this); diff --git a/qcsrc/common/monsters/monster/zombie.qh b/qcsrc/common/monsters/monster/zombie.qh new file mode 100644 index 0000000000..c5e243c754 --- /dev/null +++ b/qcsrc/common/monsters/monster/zombie.qh @@ -0,0 +1,22 @@ +#pragma once + +#ifdef GAMEQC +MODEL(MON_ZOMBIE, M_Model("zombie.dpm")); +#endif + +CLASS(Zombie, Monster) + ATTRIB(Zombie, spawnflags, int, MON_FLAG_MELEE | MON_FLAG_RIDE); + ATTRIB(Zombie, mins, vector, '-18 -18 -25'); + ATTRIB(Zombie, maxs, vector, '18 18 47'); +#ifdef GAMEQC + ATTRIB(Zombie, m_model, Model, MDL_MON_ZOMBIE); +#endif + ATTRIB(Zombie, netname, string, "zombie"); + ATTRIB(Zombie, monster_name, string, _("Zombie")); +ENDCLASS(Zombie) + +REGISTER_MONSTER(ZOMBIE, NEW(Zombie)) { +#ifdef GAMEQC + this.mr_precache(this); +#endif +} diff --git a/qcsrc/common/monsters/spawn.qc b/qcsrc/common/monsters/spawn.qc index 23fc845ac7..6c72ada302 100644 --- a/qcsrc/common/monsters/spawn.qc +++ b/qcsrc/common/monsters/spawn.qc @@ -1,3 +1,4 @@ +#include "spawn.qh" #if defined(CSQC) #elif defined(MENUQC) #elif defined(SVQC) diff --git a/qcsrc/common/monsters/sv_monsters.qc b/qcsrc/common/monsters/sv_monsters.qc index ea46be5f12..a4f7e6acaa 100644 --- a/qcsrc/common/monsters/sv_monsters.qc +++ b/qcsrc/common/monsters/sv_monsters.qc @@ -1,29 +1,27 @@ -#if defined(CSQC) -#elif defined(MENUQC) -#elif defined(SVQC) - #include - #include "../constants.qh" - #include "../teams.qh" - #include "../util.qh" - #include "all.qh" - #include "sv_monsters.qh" - #include "../physics/movelib.qh" - #include "../weapons/all.qh" - #include - #include - #include "../deathtypes/all.qh" - #include - #include - #include "../turrets/sv_turrets.qh" - #include "../turrets/util.qh" - #include "../vehicles/all.qh" - #include - #include - #include - #include "../triggers/triggers.qh" - #include - #include -#endif +#include "sv_monsters.qh" + +#include +#include +#include "../constants.qh" +#include "../teams.qh" +#include "../util.qh" +#include "all.qh" +#include "../physics/movelib.qh" +#include "../weapons/_mod.qh" +#include +#include +#include "../deathtypes/all.qh" +#include +#include +#include "../turrets/sv_turrets.qh" +#include "../turrets/util.qh" +#include "../vehicles/all.qh" +#include +#include +#include "../triggers/triggers.qh" +#include +#include +#include void monsters_setstatus(entity this) { @@ -94,6 +92,7 @@ bool Monster_ValidTarget(entity this, entity targ) || (SAME_TEAM(targ, this)) || (STAT(FROZEN, targ)) || (targ.alpha != 0 && targ.alpha < 0.5) + || (MUTATOR_CALLHOOK(MonsterValidTarget, this, targ)) ) { // if any of the above checks fail, target is not valid @@ -508,6 +507,8 @@ bool Monster_Respawn_Check(entity this) void Monster_Respawn(entity this) { Monster_Spawn(this, this.monsterid); } +.vector pos1, pos2; + void Monster_Dead_Fade(entity this) { if(Monster_Respawn_Check(this)) @@ -543,6 +544,7 @@ void Monster_Use(entity this, entity actor, entity trigger) if(Monster_ValidTarget(this, actor)) { this.enemy = actor; } } +.float pass_distance; vector Monster_Move_Target(entity this, entity targ) { // enemy is always preferred target @@ -686,6 +688,7 @@ void Monster_CalculateVelocity(entity this, vector to, vector from, float turnra } .entity draggedby; +.entity target2; void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed) { diff --git a/qcsrc/common/mutators/_mod.inc b/qcsrc/common/mutators/_mod.inc index 8220b2d195..29d6deabb9 100644 --- a/qcsrc/common/mutators/_mod.inc +++ b/qcsrc/common/mutators/_mod.inc @@ -1,2 +1,3 @@ // generated file; do not modify -#include + +#include diff --git a/qcsrc/common/mutators/_mod.qh b/qcsrc/common/mutators/_mod.qh index 5d6ac5628b..b19e9c4d70 100644 --- a/qcsrc/common/mutators/_mod.qh +++ b/qcsrc/common/mutators/_mod.qh @@ -1,2 +1,3 @@ // generated file; do not modify -#include + +#include diff --git a/qcsrc/common/mutators/all.inc b/qcsrc/common/mutators/all.inc deleted file mode 100644 index 6225872332..0000000000 --- a/qcsrc/common/mutators/all.inc +++ /dev/null @@ -1,39 +0,0 @@ -#include "mutator/waypoints/module.inc" - -#include "mutator/itemstime.qc" -#include "mutator/multijump/module.inc" -#include "mutator/nades/module.inc" -#include "mutator/superspec/module.inc" - -// completely self contained - -#include "mutator/bloodloss/module.inc" -#include "mutator/breakablehook/module.inc" -#include "mutator/buffs/module.inc" -#include "mutator/bugrigs/module.inc" -#include "mutator/campcheck/module.inc" -#include "mutator/cloaked/module.inc" -#include "mutator/damagetext/module.inc" -#include "mutator/dodging/module.inc" -#include "mutator/doublejump/module.inc" -#include "mutator/globalforces/module.inc" -#include "mutator/hook/module.inc" -#include "mutator/instagib/module.inc" -#include "mutator/invincibleproj/module.inc" -#include "mutator/melee_only/module.inc" -#include "mutator/midair/module.inc" -#include "mutator/new_toys/module.inc" -#include "mutator/nix/module.inc" -#include "mutator/overkill/module.inc" -#include "mutator/physical_items/module.inc" -#include "mutator/pinata/module.inc" -#include "mutator/random_gravity/module.inc" -#include "mutator/rocketflying/module.inc" -#include "mutator/rocketminsta/module.inc" -#include "mutator/running_guns/module.inc" -#include "mutator/sandbox/module.inc" -#include "mutator/spawn_near_teammate/module.inc" -#include "mutator/touchexplode/module.inc" -#include "mutator/vampirehook/module.inc" -#include "mutator/vampire/module.inc" -#include "mutator/weaponarena_random/module.inc" diff --git a/qcsrc/common/mutators/all.qc b/qcsrc/common/mutators/all.qc deleted file mode 100644 index f0fc7195a8..0000000000 --- a/qcsrc/common/mutators/all.qc +++ /dev/null @@ -1,5 +0,0 @@ -#include "all.qh" - -#define IMPLEMENTATION -#include "all.inc" -#undef IMPLEMENTATION diff --git a/qcsrc/common/mutators/all.qh b/qcsrc/common/mutators/all.qh deleted file mode 100644 index ceb4b51dd5..0000000000 --- a/qcsrc/common/mutators/all.qh +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef MUTATORS_ALL_H -#define MUTATORS_ALL_H - -#include "all.inc" - -#endif diff --git a/qcsrc/common/mutators/base.qh b/qcsrc/common/mutators/base.qh index 5187f7fe1f..ee5dc4ab12 100644 --- a/qcsrc/common/mutators/base.qh +++ b/qcsrc/common/mutators/base.qh @@ -1,5 +1,4 @@ -#ifndef MUTATORS_BASE_H -#define MUTATORS_BASE_H +#pragma once const int CBC_ORDER_FIRST = 1; const int CBC_ORDER_LAST = 2; @@ -168,7 +167,7 @@ bool Mutator_Add(Mutator mut); void Mutator_Remove(Mutator mut); bool mutator_log = false; -#ifndef MENUQC +#ifdef GAMEQC /** server mutators activate corresponding client mutators for all clients */ REGISTER_NET_LINKED(Mutator) @@ -322,5 +321,3 @@ STATIC_INIT_LATE(Mutators) { } MACRO_END #include "events.qh" - -#endif diff --git a/qcsrc/common/mutators/events.qh b/qcsrc/common/mutators/events.qh index 0dbc9ea218..6b16371a2c 100644 --- a/qcsrc/common/mutators/events.qh +++ b/qcsrc/common/mutators/events.qh @@ -1,5 +1,4 @@ -#ifndef COMMON_MUTATORS_EVENTS_H -#define COMMON_MUTATORS_EVENTS_H +#pragma once #define EV_NO_ARGS(i, o) @@ -104,5 +103,3 @@ MUTATOR_HOOKABLE(PM_Physics, EV_PM_Physics); /**/ o(string, MUTATOR_ARGV_1_string) \ /**/ MUTATOR_HOOKABLE(WeaponModel, EV_WeaponModel); - -#endif diff --git a/qcsrc/common/mutators/mutator/_mod.inc b/qcsrc/common/mutators/mutator/_mod.inc index 30d67e34bc..294047d500 100644 --- a/qcsrc/common/mutators/mutator/_mod.inc +++ b/qcsrc/common/mutators/mutator/_mod.inc @@ -1,2 +1,37 @@ // generated file; do not modify -#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/qcsrc/common/mutators/mutator/_mod.qh b/qcsrc/common/mutators/mutator/_mod.qh index ac056ac68b..de43630bed 100644 --- a/qcsrc/common/mutators/mutator/_mod.qh +++ b/qcsrc/common/mutators/mutator/_mod.qh @@ -1,2 +1,37 @@ // generated file; do not modify -#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/qcsrc/common/mutators/mutator/bloodloss/_mod.inc b/qcsrc/common/mutators/mutator/bloodloss/_mod.inc index 16e6308acf..768808db71 100644 --- a/qcsrc/common/mutators/mutator/bloodloss/_mod.inc +++ b/qcsrc/common/mutators/mutator/bloodloss/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/bloodloss/_mod.qh b/qcsrc/common/mutators/mutator/bloodloss/_mod.qh index b1d45e2791..e6ad248c62 100644 --- a/qcsrc/common/mutators/mutator/bloodloss/_mod.qh +++ b/qcsrc/common/mutators/mutator/bloodloss/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/bloodloss/bloodloss.qc b/qcsrc/common/mutators/mutator/bloodloss/bloodloss.qc deleted file mode 100644 index a335bf1cc4..0000000000 --- a/qcsrc/common/mutators/mutator/bloodloss/bloodloss.qc +++ /dev/null @@ -1,43 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(bloodloss, cvar("g_bloodloss")); - -.float bloodloss_timer; - -MUTATOR_HOOKFUNCTION(bloodloss, PlayerPreThink) -{ - entity player = M_ARGV(0, entity); - - if(IS_PLAYER(player)) - if(player.health <= autocvar_g_bloodloss && !IS_DEAD(player)) - { - PHYS_INPUT_BUTTON_CROUCH(player) = true; - - if(time >= player.bloodloss_timer) - { - if(player.vehicle) - vehicles_exit(player.vehicle, VHEF_RELEASE); - if(player.event_damage) - player.event_damage(player, player, player, 1, DEATH_ROT.m_id, player.origin, '0 0 0'); - player.bloodloss_timer = time + 0.5 + random() * 0.5; - } - } -} - -MUTATOR_HOOKFUNCTION(bloodloss, PlayerJump) -{ - entity player = M_ARGV(0, entity); - - if(player.health <= autocvar_g_bloodloss) - return true; -} - -MUTATOR_HOOKFUNCTION(bloodloss, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":bloodloss"); -} - -MUTATOR_HOOKFUNCTION(bloodloss, BuildMutatorsPrettyString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Blood loss"); -} -#endif diff --git a/qcsrc/common/mutators/mutator/bloodloss/module.inc b/qcsrc/common/mutators/mutator/bloodloss/module.inc deleted file mode 100644 index d3f665a18b..0000000000 --- a/qcsrc/common/mutators/mutator/bloodloss/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "bloodloss.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/bloodloss/sv_bloodloss.qc b/qcsrc/common/mutators/mutator/bloodloss/sv_bloodloss.qc new file mode 100644 index 0000000000..61b0c06016 --- /dev/null +++ b/qcsrc/common/mutators/mutator/bloodloss/sv_bloodloss.qc @@ -0,0 +1,43 @@ +#include "sv_bloodloss.qh" + +REGISTER_MUTATOR(bloodloss, cvar("g_bloodloss")); + +.float bloodloss_timer; + +MUTATOR_HOOKFUNCTION(bloodloss, PlayerPreThink) +{ + entity player = M_ARGV(0, entity); + + if(IS_PLAYER(player)) + if(player.health <= autocvar_g_bloodloss && !IS_DEAD(player)) + { + PHYS_INPUT_BUTTON_CROUCH(player) = true; + + if(time >= player.bloodloss_timer) + { + if(player.vehicle) + vehicles_exit(player.vehicle, VHEF_RELEASE); + if(player.event_damage) + player.event_damage(player, player, player, 1, DEATH_ROT.m_id, player.origin, '0 0 0'); + player.bloodloss_timer = time + 0.5 + random() * 0.5; + } + } +} + +MUTATOR_HOOKFUNCTION(bloodloss, PlayerJump) +{ + entity player = M_ARGV(0, entity); + + if(player.health <= autocvar_g_bloodloss) + return true; +} + +MUTATOR_HOOKFUNCTION(bloodloss, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":bloodloss"); +} + +MUTATOR_HOOKFUNCTION(bloodloss, BuildMutatorsPrettyString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Blood loss"); +} diff --git a/qcsrc/common/mutators/mutator/bloodloss/sv_bloodloss.qh b/qcsrc/common/mutators/mutator/bloodloss/sv_bloodloss.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/bloodloss/sv_bloodloss.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/breakablehook/_mod.inc b/qcsrc/common/mutators/mutator/breakablehook/_mod.inc index bdbbae46cd..11a080ef4a 100644 --- a/qcsrc/common/mutators/mutator/breakablehook/_mod.inc +++ b/qcsrc/common/mutators/mutator/breakablehook/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/breakablehook/_mod.qh b/qcsrc/common/mutators/mutator/breakablehook/_mod.qh index 8a41908af3..f8b2d1bc65 100644 --- a/qcsrc/common/mutators/mutator/breakablehook/_mod.qh +++ b/qcsrc/common/mutators/mutator/breakablehook/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/breakablehook/breakablehook.qc b/qcsrc/common/mutators/mutator/breakablehook/breakablehook.qc deleted file mode 100644 index ca266eb5af..0000000000 --- a/qcsrc/common/mutators/mutator/breakablehook/breakablehook.qc +++ /dev/null @@ -1,30 +0,0 @@ -#ifdef IMPLEMENTATION -#include -#include - -REGISTER_MUTATOR(breakablehook, cvar("g_breakablehook")); - -bool autocvar_g_breakablehook; // allow toggling mid match? -bool autocvar_g_breakablehook_owner; - -MUTATOR_HOOKFUNCTION(breakablehook, PlayerDamage_Calculate) -{ - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - - if(frag_target.classname == "grapplinghook") - { - if((!autocvar_g_breakablehook) - || (!autocvar_g_breakablehook_owner && frag_attacker == frag_target.realowner) - ) { M_ARGV(4, float) = 0; } - - // hurt the owner of the hook - if(DIFF_TEAM(frag_attacker, frag_target.realowner)) - { - Damage (frag_target.realowner, frag_attacker, frag_attacker, 5, WEP_HOOK.m_id | HITTYPE_SPLASH, frag_target.realowner.origin, '0 0 0'); - RemoveGrapplingHook(frag_target.realowner); - return; // dead - } - } -} -#endif diff --git a/qcsrc/common/mutators/mutator/breakablehook/module.inc b/qcsrc/common/mutators/mutator/breakablehook/module.inc deleted file mode 100644 index 484eb4c563..0000000000 --- a/qcsrc/common/mutators/mutator/breakablehook/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "breakablehook.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/breakablehook/sv_breakablehook.qc b/qcsrc/common/mutators/mutator/breakablehook/sv_breakablehook.qc new file mode 100644 index 0000000000..fdb0dc38d1 --- /dev/null +++ b/qcsrc/common/mutators/mutator/breakablehook/sv_breakablehook.qc @@ -0,0 +1,30 @@ +#include "sv_breakablehook.qh" + +#include +#include + +REGISTER_MUTATOR(breakablehook, cvar("g_breakablehook")); + +bool autocvar_g_breakablehook; // allow toggling mid match? +bool autocvar_g_breakablehook_owner; + +MUTATOR_HOOKFUNCTION(breakablehook, PlayerDamage_Calculate) +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + + if(frag_target.classname == "grapplinghook") + { + if((!autocvar_g_breakablehook) + || (!autocvar_g_breakablehook_owner && frag_attacker == frag_target.realowner) + ) { M_ARGV(4, float) = 0; } + + // hurt the owner of the hook + if(DIFF_TEAM(frag_attacker, frag_target.realowner)) + { + Damage (frag_target.realowner, frag_attacker, frag_attacker, 5, WEP_HOOK.m_id | HITTYPE_SPLASH, frag_target.realowner.origin, '0 0 0'); + RemoveGrapplingHook(frag_target.realowner); + return; // dead + } + } +} diff --git a/qcsrc/common/mutators/mutator/breakablehook/sv_breakablehook.qh b/qcsrc/common/mutators/mutator/breakablehook/sv_breakablehook.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/breakablehook/sv_breakablehook.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/buffs/_mod.inc b/qcsrc/common/mutators/mutator/buffs/_mod.inc index c06263a92f..715a3acfcc 100644 --- a/qcsrc/common/mutators/mutator/buffs/_mod.inc +++ b/qcsrc/common/mutators/mutator/buffs/_mod.inc @@ -1,3 +1,8 @@ // generated file; do not modify -#include #include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/buffs/_mod.qh b/qcsrc/common/mutators/mutator/buffs/_mod.qh index 2133c7250e..78217bb46e 100644 --- a/qcsrc/common/mutators/mutator/buffs/_mod.qh +++ b/qcsrc/common/mutators/mutator/buffs/_mod.qh @@ -1,3 +1,8 @@ // generated file; do not modify -#include #include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/buffs/all.qc b/qcsrc/common/mutators/mutator/buffs/all.qc deleted file mode 100644 index b056751624..0000000000 --- a/qcsrc/common/mutators/mutator/buffs/all.qc +++ /dev/null @@ -1 +0,0 @@ -#include "all.qh" diff --git a/qcsrc/common/mutators/mutator/buffs/all.qh b/qcsrc/common/mutators/mutator/buffs/all.qh deleted file mode 100644 index 79117c95a8..0000000000 --- a/qcsrc/common/mutators/mutator/buffs/all.qh +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef BUFFS_ALL_H -#define BUFFS_ALL_H - -#include -#include - -REGISTER_WAYPOINT(Buff, _("Buff"), '1 0.5 0', 1); -REGISTER_RADARICON(Buff, 1); - -REGISTRY(Buffs, BITS(5)) -#define Buffs_from(i) _Buffs_from(i, BUFF_Null) -REGISTER_REGISTRY(Buffs) -REGISTRY_CHECK(Buffs) - -#define REGISTER_BUFF(id) \ - REGISTER(Buffs, BUFF_##id, m_id, NEW(Buff)) - -#include -CLASS(Buff, Pickup) - /** bit index */ - ATTRIB(Buff, m_itemid, int, 0); - ATTRIB(Buff, m_name, string, "buff"); - ATTRIB(Buff, m_color, vector, '1 1 1'); - ATTRIB(Buff, m_prettyName, string, "Buff"); - ATTRIB(Buff, m_skin, int, 0); - ATTRIB(Buff, m_sprite, string, ""); - METHOD(Buff, display, void(entity this, void(string name, string icon) returns)) { - returns(this.m_prettyName, sprintf("/gfx/hud/%s/buff_%s", cvar_string("menu_skin"), this.m_name)); - } -#ifdef SVQC - METHOD(Buff, m_time, float(Buff this)) - { return cvar(strcat("g_buffs_", this.netname, "_time")); } -#endif -ENDCLASS(Buff) - -STATIC_INIT(REGISTER_BUFFS) { - FOREACH(Buffs, true, { - it.netname = it.m_name; \ - it.m_itemid = BIT(it.m_id - 1); \ - it.m_sprite = strzone(strcat("buff-", it.m_name)); \ - }); -} - -#ifdef SVQC - // .int buffs = _STAT(BUFFS); - void buff_Init(entity ent); - void buff_Init_Compat(entity ent, entity replacement); - #define BUFF_SPAWNFUNC(e, b, t) spawnfunc(item_buff_##e) { \ - this.buffs = b.m_itemid; \ - this.team = t; \ - buff_Init(this); \ - } - #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) - #define BUFF_SPAWNFUNC_Q3TA_COMPAT(o, r) spawnfunc(item_##o) { buff_Init_Compat(this, r); } -#else - #define BUFF_SPAWNFUNC(e, b, t) - #define BUFF_SPAWNFUNCS(e, b) - #define BUFF_SPAWNFUNC_Q3TA_COMPAT(o, r) -#endif - -REGISTER_BUFF(Null); -BUFF_SPAWNFUNCS(random, BUFF_Null) - -#include "all.inc" - -#endif diff --git a/qcsrc/common/mutators/mutator/buffs/buffs.qc b/qcsrc/common/mutators/mutator/buffs/buffs.qc index 29a0a39256..f38d39d618 100644 --- a/qcsrc/common/mutators/mutator/buffs/buffs.qc +++ b/qcsrc/common/mutators/mutator/buffs/buffs.qc @@ -1,1081 +1,16 @@ -#ifndef MUTATOR_BUFFS_H -#define MUTATOR_BUFFS_H +#include "buffs.qh" -#include "../instagib/module.inc" - -bool autocvar_g_buffs_effects; -float autocvar_g_buffs_waypoint_distance; -bool autocvar_g_buffs_randomize; -float autocvar_g_buffs_random_lifetime; -bool autocvar_g_buffs_random_location; -int autocvar_g_buffs_random_location_attempts; -int autocvar_g_buffs_spawn_count; -bool 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_medic_heal_amount = 15; -float autocvar_g_buffs_medic_heal_delay = 1; -float autocvar_g_buffs_medic_heal_range = 400; -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_slowtime; -float autocvar_g_buffs_disability_speed; -float autocvar_g_buffs_disability_rate; -float autocvar_g_buffs_disability_weaponspeed; -float autocvar_g_buffs_speed_speed; -float autocvar_g_buffs_speed_rate; -float autocvar_g_buffs_speed_weaponspeed; -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_jump_height; -float autocvar_g_buffs_inferno_burntime_factor; -float autocvar_g_buffs_inferno_burntime_min_time; -float autocvar_g_buffs_inferno_burntime_target_damage; -float autocvar_g_buffs_inferno_burntime_target_time; -float autocvar_g_buffs_inferno_damagemultiplier; -float autocvar_g_buffs_swapper_range; -float autocvar_g_buffs_magnet_range_item; -float autocvar_g_buffs_magnet_range_buff = 200; -float autocvar_g_buffs_luck_chance = 0.15; -float autocvar_g_buffs_luck_damagemultiplier = 3; - -// ammo -.float buff_ammo_prev_infitems; -.int buff_ammo_prev_clipload; -// invisible -.float buff_invisible_prev_alpha; -// medic -.float buff_medic_healtime; -// disability -.float buff_disability_time; -.float buff_disability_effect_time; -// common buff variables -.float buff_effect_delay; - -// buff definitions -.float buff_active; -.float buff_activetime; -.float buff_activetime_updated; -.entity buff_waypoint; -.int oldbuffs; // for updating effects -.entity buff_model; // controls effects (TODO: make csqc) - -const vector BUFF_MIN = ('-16 -16 -20'); -const vector BUFF_MAX = ('16 16 20'); - -// client side options -.float cvar_cl_buffs_autoreplace; -#endif - -#ifdef IMPLEMENTATION - -#include -#include - -.float buff_time = _STAT(BUFF_TIME); -void buffs_DelayedInit(entity this); - -REGISTER_MUTATOR(buffs, cvar("g_buffs")) -{ - MUTATOR_ONADD - { - InitializeEntity(NULL, buffs_DelayedInit, INITPRIO_FINDTARGET); - } -} - -bool buffs_BuffModel_Customize(entity this, entity client) -{ - entity player, myowner; - bool same_team; - - player = WaypointSprite_getviewentity(client); - myowner = this.owner; - same_team = (SAME_TEAM(player, myowner) || SAME_TEAM(player, myowner)); - - if(myowner.alpha <= 0.5 && !same_team && myowner.alpha != 0) - return false; - - if(MUTATOR_CALLHOOK(BuffModel_Customize, this, player)) - return false; - - if(player == myowner || (IS_SPEC(client) && client.enemy == myowner)) - { - // somewhat hide the model, but keep the glow - this.effects = 0; - this.alpha = -1; - } - else - { - this.effects = EF_FULLBRIGHT | EF_LOWPRECISION; - this.alpha = 1; - } - return true; -} - -void buffs_BuffModel_Spawn(entity player) -{ - player.buff_model = spawn(); - setmodel(player.buff_model, MDL_BUFF); - setsize(player.buff_model, '0 0 -40', '0 0 40'); - setattachment(player.buff_model, player, ""); - setorigin(player.buff_model, '0 0 1' * (player.buff_model.maxs.z * 1)); - player.buff_model.owner = player; - player.buff_model.scale = 0.7; - player.buff_model.pflags = PFLAGS_FULLDYNAMIC; - player.buff_model.light_lev = 200; - setcefc(player.buff_model, buffs_BuffModel_Customize); -} - -vector buff_GlowColor(entity buff) -{ - //if(buff.team) { return Team_ColorRGB(buff.team); } - return buff.m_color; -} - -void buff_Effect(entity player, string eff) -{ - if(!autocvar_g_buffs_effects) { return; } - - if(time >= player.buff_effect_delay) - { - Send_Effect_(eff, player.origin + ((player.mins + player.maxs) * 0.5), '0 0 0', 1); - player.buff_effect_delay = time + 0.05; // prevent spam - } -} - -// buff item -bool buff_Waypoint_visible_for_player(entity this, entity player, entity view) -{ - if(!this.owner.buff_active && !this.owner.buff_activetime) - return false; - - if (view.buffs) - { - return view.cvar_cl_buffs_autoreplace == false || view.buffs != this.owner.buffs; - } - - return WaypointSprite_visible_for_player(this, player, view); -} - -void buff_Waypoint_Spawn(entity e) -{ - entity buff = buff_FirstFromFlags(e.buffs); - entity wp = WaypointSprite_Spawn(WP_Buff, 0, autocvar_g_buffs_waypoint_distance, e, '0 0 1' * e.maxs.z, NULL, e.team, e, buff_waypoint, true, RADARICON_Buff); - wp.wp_extra = buff.m_id; - WaypointSprite_UpdateTeamRadar(e.buff_waypoint, RADARICON_Buff, e.glowmod); - e.buff_waypoint.waypointsprite_visible_for_player = buff_Waypoint_visible_for_player; -} - -void buff_SetCooldown(entity this, float cd) -{ - cd = max(0, cd); - - if(!this.buff_waypoint) - buff_Waypoint_Spawn(this); - - WaypointSprite_UpdateBuildFinished(this.buff_waypoint, time + cd); - this.buff_activetime = cd; - this.buff_active = !cd; -} - -void buff_Respawn(entity this) -{ - if(gameover) { return; } - - vector oldbufforigin = this.origin; - this.velocity = '0 0 200'; - - if(!MoveToRandomMapLocation(this, 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(this, true); - setorigin(this, spot.origin); - this.velocity = ((randomvec() * 100) + '0 0 200'); - this.angles = spot.angles; - } - - tracebox(this.origin, this.mins * 1.5, this.maxs * 1.5, this.origin, MOVE_NOMONSTERS, this); - - setorigin(this, trace_endpos); // attempt to unstick - - set_movetype(this, MOVETYPE_TOSS); - - makevectors(this.angles); - this.angles = '0 0 0'; - if(autocvar_g_buffs_random_lifetime > 0) - this.lifetime = time + autocvar_g_buffs_random_lifetime; - - Send_Effect(EFFECT_ELECTRO_COMBO, oldbufforigin + ((this.mins + this.maxs) * 0.5), '0 0 0', 1); - Send_Effect(EFFECT_ELECTRO_COMBO, CENTER_OR_VIEWOFS(this), '0 0 0', 1); - - WaypointSprite_Ping(this.buff_waypoint); - - sound(this, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere) -} - -void buff_Touch(entity this, entity toucher) -{ - if(gameover) { return; } - - if(ITEM_TOUCH_NEEDKILL()) - { - buff_Respawn(this); - return; - } - - if((this.team && DIFF_TEAM(toucher, this)) - || (STAT(FROZEN, toucher)) - || (toucher.vehicle) - || (!this.buff_active) - ) - { - // can't touch this - return; - } - - if(MUTATOR_CALLHOOK(BuffTouch, this, toucher)) - return; - toucher = M_ARGV(1, entity); - - if(!IS_PLAYER(toucher)) - return; // incase mutator changed toucher - - if (toucher.buffs) - { - if (toucher.cvar_cl_buffs_autoreplace && toucher.buffs != this.buffs) - { - int buffid = buff_FirstFromFlags(toucher.buffs).m_id; - //Send_Notification(NOTIF_ONE, toucher, MSG_MULTI, ITEM_BUFF_DROP, toucher.buffs); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ITEM_BUFF_LOST, toucher.netname, buffid); - - toucher.buffs = 0; - //sound(toucher, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); - } - else { return; } // do nothing - } - - this.owner = toucher; - this.buff_active = false; - this.lifetime = 0; - int buffid = buff_FirstFromFlags(this.buffs).m_id; - Send_Notification(NOTIF_ONE, toucher, MSG_MULTI, ITEM_BUFF_GOT, buffid); - Send_Notification(NOTIF_ALL_EXCEPT, toucher, MSG_INFO, INFO_ITEM_BUFF, toucher.netname, buffid); - - Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(this), '0 0 0', 1); - sound(toucher, CH_TRIGGER, SND_SHIELD_RESPAWN, VOL_BASE, ATTN_NORM); - toucher.buffs |= (this.buffs); -} - -float buff_Available(entity buff) -{ - if (buff == BUFF_Null) - return false; - if (buff == BUFF_AMMO && ((start_items & IT_UNLIMITED_WEAPON_AMMO) || (start_items & IT_UNLIMITED_AMMO) || (cvar("g_melee_only")))) - return false; - if (buff == BUFF_VAMPIRE && cvar("g_vampire")) - return false; - return cvar(strcat("g_buffs_", buff.m_name)); -} - -.int buff_seencount; - -void buff_NewType(entity ent, float cb) -{ - RandomSelection_Init(); - FOREACH(Buffs, buff_Available(it), LAMBDA( - it.buff_seencount += 1; - // if it's already been chosen, give it a lower priority - RandomSelection_Add(NULL, it.m_itemid, string_null, 1, max(0.2, 1 / it.buff_seencount)); - )); - ent.buffs = RandomSelection_chosen_float; -} - -void buff_Think(entity this) -{ - if(this.buffs != this.oldbuffs) - { - entity buff = buff_FirstFromFlags(this.buffs); - this.color = buff.m_color; - this.glowmod = buff_GlowColor(buff); - this.skin = buff.m_skin; - - setmodel(this, MDL_BUFF); - - if(this.buff_waypoint) - { - //WaypointSprite_Disown(this.buff_waypoint, 1); - WaypointSprite_Kill(this.buff_waypoint); - buff_Waypoint_Spawn(this); - if(this.buff_activetime) - WaypointSprite_UpdateBuildFinished(this.buff_waypoint, time + this.buff_activetime - frametime); - } - - this.oldbuffs = this.buffs; - } - - if(!gameover) - if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime) - if(!this.buff_activetime_updated) - { - buff_SetCooldown(this, this.buff_activetime); - this.buff_activetime_updated = true; - } - - if(!this.buff_active && !this.buff_activetime) - if(!this.owner || STAT(FROZEN, this.owner) || IS_DEAD(this.owner) || !this.owner.iscreature || !(this.owner.buffs & this.buffs)) - { - buff_SetCooldown(this, autocvar_g_buffs_cooldown_respawn + frametime); - this.owner = NULL; - if(autocvar_g_buffs_randomize) - buff_NewType(this, this.buffs); - - if(autocvar_g_buffs_random_location || (this.spawnflags & 64)) - buff_Respawn(this); - } - - if(this.buff_activetime) - if(!gameover) - if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime) - { - this.buff_activetime = max(0, this.buff_activetime - frametime); - - if(!this.buff_activetime) - { - this.buff_active = true; - sound(this, CH_TRIGGER, SND_STRENGTH_RESPAWN, VOL_BASE, ATTN_NORM); - Send_Effect(EFFECT_ITEM_RESPAWN, CENTER_OR_VIEWOFS(this), '0 0 0', 1); - } - } - - if(this.buff_active) - { - if(this.team && !this.buff_waypoint) - buff_Waypoint_Spawn(this); - - if(this.lifetime) - if(time >= this.lifetime) - buff_Respawn(this); - } - - this.nextthink = time; - //this.angles_y = time * 110.1; -} - -void buff_Waypoint_Reset(entity this) -{ - WaypointSprite_Kill(this.buff_waypoint); - - if(this.buff_activetime) { buff_Waypoint_Spawn(this); } -} - -void buff_Reset(entity this) -{ - if(autocvar_g_buffs_randomize) - buff_NewType(this, this.buffs); - this.owner = NULL; - buff_SetCooldown(this, autocvar_g_buffs_cooldown_activate); - buff_Waypoint_Reset(this); - this.buff_activetime_updated = false; - - if(autocvar_g_buffs_random_location || (this.spawnflags & 64)) - buff_Respawn(this); -} - -bool buff_Customize(entity this, entity client) -{ - entity player = WaypointSprite_getviewentity(client); - if(!this.buff_active || (this.team && DIFF_TEAM(player, this))) - { - this.alpha = 0.3; - if(this.effects & EF_FULLBRIGHT) { this.effects &= ~(EF_FULLBRIGHT); } - this.pflags = 0; - } - else - { - this.alpha = 1; - if(!(this.effects & EF_FULLBRIGHT)) { this.effects |= EF_FULLBRIGHT; } - this.light_lev = 220 + 36 * sin(time); - this.pflags = PFLAGS_FULLDYNAMIC; - } - return true; -} - -void buff_Init(entity this) -{ - if(!cvar("g_buffs")) { delete(this); return; } - - if(!teamplay && this.team) { this.team = 0; } - - entity buff = buff_FirstFromFlags(this.buffs); - - if(!this.buffs || buff_Available(buff)) - buff_NewType(this, 0); - - this.classname = "item_buff"; - this.solid = SOLID_TRIGGER; - this.flags = FL_ITEM; - setthink(this, buff_Think); - settouch(this, buff_Touch); - this.reset = buff_Reset; - this.nextthink = time + 0.1; - this.gravity = 1; - set_movetype(this, MOVETYPE_TOSS); - this.scale = 1; - this.skin = buff.m_skin; - this.effects = EF_FULLBRIGHT | EF_STARDUST | EF_NOSHADOW; - this.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY; - setcefc(this, buff_Customize); - //this.gravity = 100; - this.color = buff.m_color; - this.glowmod = buff_GlowColor(this); - buff_SetCooldown(this, autocvar_g_buffs_cooldown_activate + game_starttime); - this.buff_active = !this.buff_activetime; - this.pflags = PFLAGS_FULLDYNAMIC; - - if(this.spawnflags & 1) - this.noalign = true; - - if(this.noalign) - set_movetype(this, MOVETYPE_NONE); // reset by random location - - setmodel(this, MDL_BUFF); - setsize(this, BUFF_MIN, BUFF_MAX); - - if(cvar("g_buffs_random_location") || (this.spawnflags & 64)) - buff_Respawn(this); -} - -void buff_Init_Compat(entity ent, entity replacement) -{ - if (ent.spawnflags & 2) - ent.team = NUM_TEAM_1; - else if (ent.spawnflags & 4) - ent.team = NUM_TEAM_2; - - ent.buffs = replacement.m_itemid; - - buff_Init(ent); -} - -void buff_SpawnReplacement(entity ent, entity old) -{ - setorigin(ent, old.origin); - ent.angles = old.angles; - ent.noalign = (old.noalign || (old.spawnflags & 1)); - - buff_Init(ent); -} - -void buff_Vengeance_DelayedDamage(entity this) -{ - if(this.enemy) - Damage(this.enemy, this.owner, this.owner, this.dmg, DEATH_BUFF.m_id, this.enemy.origin, '0 0 0'); - - delete(this); - return; -} - -// note: only really useful in teamplay -void buff_Medic_Heal(entity this) -{ - FOREACH_CLIENT(IS_PLAYER(it) && it != this && vdist(it.origin - this.origin, <=, autocvar_g_buffs_medic_heal_range), - { - if(SAME_TEAM(it, this)) - if(it.health < autocvar_g_balance_health_regenstable) - { - Send_Effect(EFFECT_HEALING, it.origin, '0 0 0', 1); - it.health = bound(0, it.health + autocvar_g_buffs_medic_heal_amount, autocvar_g_balance_health_regenstable); - } - }); -} - -float buff_Inferno_CalculateTime(float x, float offset_x, float offset_y, float intersect_x, float intersect_y, float base) -{ - return offset_y + (intersect_y - offset_y) * logn(((x - offset_x) * ((base - 1) / intersect_x)) + 1, base); -} - -// mutator hooks -MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_SplitHealthArmor) -{ - entity frag_target = M_ARGV(2, entity); - float frag_deathtype = M_ARGV(6, float); - float frag_damage = M_ARGV(7, float); - - if(frag_deathtype == DEATH_BUFF.m_id) { return; } - - if(frag_target.buffs & BUFF_RESISTANCE.m_itemid) - { - vector v = healtharmor_applydamage(50, autocvar_g_buffs_resistance_blockpercent, frag_deathtype, frag_damage); - M_ARGV(4, float) = v.x; // take - M_ARGV(5, float) = v.y; // save - } -} - -MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_Calculate) -{ - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - float frag_deathtype = M_ARGV(3, float); - float frag_damage = M_ARGV(4, float); - vector frag_force = M_ARGV(6, vector); - - if(frag_deathtype == DEATH_BUFF.m_id) { return; } - - if(frag_target.buffs & BUFF_SPEED.m_itemid) - if(frag_target != frag_attacker) - frag_damage *= autocvar_g_buffs_speed_damage_take; - - if(frag_target.buffs & BUFF_MEDIC.m_itemid) - 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 = max(5, frag_target.health - autocvar_g_buffs_medic_survive_health); - - if(frag_target.buffs & BUFF_JUMP.m_itemid) - if(frag_deathtype == DEATH_FALL.m_id) - frag_damage = 0; - - if(frag_target.buffs & BUFF_VENGEANCE.m_itemid) - 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; - setthink(dmgent, buff_Vengeance_DelayedDamage); - dmgent.nextthink = time + 0.1; - } - - if(frag_target.buffs & BUFF_BASH.m_itemid) - if(frag_attacker != frag_target) - frag_force = '0 0 0'; - - if(frag_attacker.buffs & BUFF_BASH.m_itemid) - if(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.m_itemid) - if(frag_target != frag_attacker) - frag_target.buff_disability_time = time + autocvar_g_buffs_disability_slowtime; - - if(frag_target.buffs & BUFF_INFERNO.m_itemid) - { - if(frag_deathtype == DEATH_FIRE.m_id) - frag_damage = 0; - if(frag_deathtype == DEATH_LAVA.m_id) - frag_damage *= 0.5; // TODO: cvarize? - } - - if(frag_attacker.buffs & BUFF_LUCK.m_itemid) - if(frag_attacker != frag_target) - if(autocvar_g_buffs_luck_damagemultiplier > 0) - if(random() <= autocvar_g_buffs_luck_chance) - frag_damage *= autocvar_g_buffs_luck_damagemultiplier; - - if(frag_attacker.buffs & BUFF_INFERNO.m_itemid) - if(frag_target != frag_attacker) { - float btime = buff_Inferno_CalculateTime( - frag_damage, - 0, - autocvar_g_buffs_inferno_burntime_min_time, - autocvar_g_buffs_inferno_burntime_target_damage, - autocvar_g_buffs_inferno_burntime_target_time, - autocvar_g_buffs_inferno_burntime_factor - ); - Fire_AddDamage(frag_target, frag_attacker, (frag_damage * autocvar_g_buffs_inferno_damagemultiplier), btime, DEATH_BUFF.m_id); - } - - // this... is ridiculous (TODO: fix!) - if(frag_attacker.buffs & BUFF_VAMPIRE.m_itemid) - if(!frag_target.vehicle) - if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype)) - if(!IS_DEAD(frag_target)) - if(IS_PLAYER(frag_target) || IS_MONSTER(frag_target)) - if(frag_attacker != frag_target) - if(!STAT(FROZEN, frag_target)) - 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); - if(frag_target.armorvalue) - frag_attacker.armorvalue = bound(0, frag_attacker.armorvalue + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.armorvalue), g_pickup_armorsmall_max); - } - - M_ARGV(4, float) = frag_damage; - M_ARGV(6, vector) = frag_force; -} - -MUTATOR_HOOKFUNCTION(buffs, PlayerSpawn) -{ - entity player = M_ARGV(0, entity); - - player.buffs = 0; - player.buff_time = 0; - // reset timers here to prevent them continuing after re-spawn - player.buff_disability_time = 0; - player.buff_disability_effect_time = 0; -} - -.float stat_sv_maxspeed; -.float stat_sv_airspeedlimit_nonqw; -.float stat_sv_jumpvelocity; - -MUTATOR_HOOKFUNCTION(buffs, PlayerPhysics) -{ - entity player = M_ARGV(0, entity); - - if(player.buffs & BUFF_SPEED.m_itemid) - { - player.stat_sv_maxspeed *= autocvar_g_buffs_speed_speed; - player.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_speed_speed; - } - - if(time < player.buff_disability_time) - { - player.stat_sv_maxspeed *= autocvar_g_buffs_disability_speed; - player.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_disability_speed; - } - - if(player.buffs & BUFF_JUMP.m_itemid) - { - // automatically reset, no need to worry - player.stat_sv_jumpvelocity = autocvar_g_buffs_jump_height; - } -} - -MUTATOR_HOOKFUNCTION(buffs, PlayerJump) -{ - entity player = M_ARGV(0, entity); - - if(player.buffs & BUFF_JUMP.m_itemid) - M_ARGV(1, float) = autocvar_g_buffs_jump_height; -} - -MUTATOR_HOOKFUNCTION(buffs, MonsterMove) -{ - entity mon = M_ARGV(0, entity); - - if(time < mon.buff_disability_time) - { - M_ARGV(1, float) *= autocvar_g_buffs_disability_speed; // run speed - M_ARGV(2, float) *= autocvar_g_buffs_disability_speed; // walk speed - } -} - -MUTATOR_HOOKFUNCTION(buffs, PlayerDies) -{ - entity frag_target = M_ARGV(2, entity); - - if(frag_target.buffs) - { - int buffid = buff_FirstFromFlags(frag_target.buffs).m_id; - Send_Notification(NOTIF_ALL_EXCEPT, frag_target, MSG_INFO, INFO_ITEM_BUFF_LOST, frag_target.netname, buffid); - frag_target.buffs = 0; - - if(frag_target.buff_model) - { - delete(frag_target.buff_model); - frag_target.buff_model = NULL; - } - } -} - -MUTATOR_HOOKFUNCTION(buffs, PlayerUseKey, CBC_ORDER_FIRST) -{ - if(MUTATOR_RETURNVALUE || gameover) { return; } - - entity player = M_ARGV(0, entity); - - if(player.buffs) - { - int buffid = buff_FirstFromFlags(player.buffs).m_id; - Send_Notification(NOTIF_ONE, player, MSG_MULTI, ITEM_BUFF_DROP, buffid); - Send_Notification(NOTIF_ALL_EXCEPT, player, MSG_INFO, INFO_ITEM_BUFF_LOST, player.netname, buffid); - - player.buffs = 0; - player.buff_time = 0; // already notified - sound(player, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); - return true; - } -} - -MUTATOR_HOOKFUNCTION(buffs, ForbidThrowCurrentWeapon) -{ - if(MUTATOR_RETURNVALUE || gameover) { return; } - entity player = M_ARGV(0, entity); - - if(player.buffs & BUFF_SWAPPER.m_itemid) - { - float best_distance = autocvar_g_buffs_swapper_range; - entity closest = NULL; - FOREACH_CLIENT(IS_PLAYER(it), LAMBDA( - if(!IS_DEAD(it) && !STAT(FROZEN, it) && !it.vehicle) - if(DIFF_TEAM(it, player)) - { - float test = vlen2(player.origin - it.origin); - if(test <= best_distance * best_distance) - { - best_distance = sqrt(test); - closest = it; - } - } - )); - - if(closest) - { - vector my_org, my_vel, my_ang, their_org, their_vel, their_ang; - - my_org = player.origin; - my_vel = player.velocity; - my_ang = player.angles; - their_org = closest.origin; - their_vel = closest.velocity; - their_ang = closest.angles; - - Drop_Special_Items(closest); - - MUTATOR_CALLHOOK(PortalTeleport, player); // initiate flag dropper - - setorigin(player, their_org); - setorigin(closest, my_org); - - closest.velocity = my_vel; - closest.angles = my_ang; - closest.fixangle = true; - closest.oldorigin = my_org; - closest.oldvelocity = my_vel; - player.velocity = their_vel; - player.angles = their_ang; - player.fixangle = true; - player.oldorigin = their_org; - player.oldvelocity = their_vel; - - // set pusher so player gets the kill if they fall into void - closest.pusher = player; - closest.pushltime = time + autocvar_g_maxpushtime; - closest.istypefrag = PHYS_INPUT_BUTTON_CHAT(closest); - - Send_Effect(EFFECT_ELECTRO_COMBO, their_org, '0 0 0', 1); - Send_Effect(EFFECT_ELECTRO_COMBO, my_org, '0 0 0', 1); - - sound(player, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NORM); - sound(closest, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NORM); - - // TODO: add a counter to handle how many times one can teleport, and a delay to prevent spam - player.buffs = 0; - return true; - } - } -} - -bool buffs_RemovePlayer(entity player) -{ - if(player.buff_model) - { - delete(player.buff_model); - player.buff_model = NULL; - } - - // also reset timers here to prevent them continuing after spectating - player.buff_disability_time = 0; - player.buff_disability_effect_time = 0; - - return false; -} -MUTATOR_HOOKFUNCTION(buffs, MakePlayerObserver) { entity player = M_ARGV(0, entity); return buffs_RemovePlayer(player); } -MUTATOR_HOOKFUNCTION(buffs, ClientDisconnect) { entity player = M_ARGV(0, entity); return buffs_RemovePlayer(player); } - -MUTATOR_HOOKFUNCTION(buffs, CustomizeWaypoint) -{ - entity wp = M_ARGV(0, entity); - entity player = M_ARGV(1, entity); - - entity e = WaypointSprite_getviewentity(player); - - // if you have the invisibility powerup, sprites ALWAYS are restricted to your team - // but only apply this to real players, not to spectators - if((wp.owner.flags & FL_CLIENT) && (wp.owner.buffs & BUFF_INVISIBLE.m_itemid) && (e == player)) - if(DIFF_TEAM(wp.owner, e)) - return true; -} - -MUTATOR_HOOKFUNCTION(buffs, OnEntityPreSpawn, CBC_ORDER_LAST) -{ - entity ent = M_ARGV(0, entity); - - if(autocvar_g_buffs_replace_powerups) - switch(ent.classname) - { - case "item_strength": - case "item_invincible": - { - entity e = spawn(); - buff_SpawnReplacement(e, ent); - return true; - } - } -} - -MUTATOR_HOOKFUNCTION(buffs, WeaponRateFactor) -{ - entity player = M_ARGV(1, entity); - - if(player.buffs & BUFF_SPEED.m_itemid) - M_ARGV(0, float) *= autocvar_g_buffs_speed_rate; - - if(time < player.buff_disability_time) - M_ARGV(0, float) *= autocvar_g_buffs_disability_rate; -} - -MUTATOR_HOOKFUNCTION(buffs, WeaponSpeedFactor) -{ - entity player = M_ARGV(1, entity); - - if(player.buffs & BUFF_SPEED.m_itemid) - M_ARGV(0, float) *= autocvar_g_buffs_speed_weaponspeed; - - if(time < player.buff_disability_time) - M_ARGV(0, float) *= autocvar_g_buffs_disability_weaponspeed; -} - -MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink) -{ - entity player = M_ARGV(0, entity); - - if(gameover || IS_DEAD(player)) { return; } - - if(time < player.buff_disability_time) - if(time >= player.buff_disability_effect_time) - { - Send_Effect(EFFECT_SMOKING, player.origin + ((player.mins + player.maxs) * 0.5), '0 0 0', 1); - player.buff_disability_effect_time = time + 0.5; - } - - // handle buff lost status - // 1: notify everyone else - // 2: notify carrier as well - int buff_lost = 0; - - if(player.buff_time && player.buffs) - if(time >= player.buff_time) - { - player.buff_time = 0; - buff_lost = 2; - } - - if(STAT(FROZEN, player)) { buff_lost = 1; } - - if(buff_lost) - { - if(player.buffs) - { - int buffid = buff_FirstFromFlags(player.buffs).m_id; - if(buff_lost == 2) - { - Send_Notification(NOTIF_ONE, player, MSG_MULTI, ITEM_BUFF_DROP, buffid); // TODO: special timeout message? - sound(player, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); - } - else - Send_Notification(NOTIF_ALL_EXCEPT, player, MSG_INFO, INFO_ITEM_BUFF_LOST, player.netname, buffid); - player.buffs = 0; - } - } - - if(player.buffs & BUFF_MAGNET.m_itemid) - { - vector pickup_size; - FOREACH_ENTITY_FLAGS(flags, FL_ITEM, - { - if(it.buffs) - pickup_size = '1 1 1' * autocvar_g_buffs_magnet_range_buff; - else - pickup_size = '1 1 1' * autocvar_g_buffs_magnet_range_item; - - if(boxesoverlap(player.absmin - pickup_size, player.absmax + pickup_size, it.absmin, it.absmax)) - { - if(gettouch(it)) - gettouch(it)(it, player); - } - }); - } - - if(player.buffs & BUFF_AMMO.m_itemid) - if(player.clip_size) - player.clip_load = player.(weapon_load[PS(player).m_switchweapon.m_id]) = player.clip_size; - - if((player.buffs & BUFF_INVISIBLE.m_itemid) && (player.oldbuffs & BUFF_INVISIBLE.m_itemid)) - if(player.alpha != autocvar_g_buffs_invisible_alpha) - player.alpha = autocvar_g_buffs_invisible_alpha; // powerups reset alpha, so we must enforce this (TODO) - - if(player.buffs & BUFF_MEDIC.m_itemid) - if(time >= player.buff_medic_healtime) - { - buff_Medic_Heal(player); - player.buff_medic_healtime = time + autocvar_g_buffs_medic_heal_delay; - } - -#define BUFF_ONADD(b) if ( (player.buffs & (b).m_itemid) && !(player.oldbuffs & (b).m_itemid)) -#define BUFF_ONREM(b) if (!(player.buffs & (b).m_itemid) && (player.oldbuffs & (b).m_itemid)) - - if(player.buffs != player.oldbuffs) - { - entity buff = buff_FirstFromFlags(player.buffs); - float bufftime = buff != BUFF_Null ? buff.m_time(buff) : 0; - player.buff_time = (bufftime) ? time + bufftime : 0; - - BUFF_ONADD(BUFF_AMMO) - { - player.buff_ammo_prev_infitems = (player.items & IT_UNLIMITED_WEAPON_AMMO); - player.items |= IT_UNLIMITED_WEAPON_AMMO; - - if(player.clip_load) - player.buff_ammo_prev_clipload = player.clip_load; - player.clip_load = player.(weapon_load[PS(player).m_switchweapon.m_id]) = player.clip_size; - } - - BUFF_ONREM(BUFF_AMMO) - { - if(player.buff_ammo_prev_infitems) - player.items |= IT_UNLIMITED_WEAPON_AMMO; - else - player.items &= ~IT_UNLIMITED_WEAPON_AMMO; - - if(player.buff_ammo_prev_clipload) - player.clip_load = player.buff_ammo_prev_clipload; - } - - BUFF_ONADD(BUFF_INVISIBLE) - { - if(time < player.strength_finished && g_instagib) - player.alpha = autocvar_g_instagib_invis_alpha; - else - player.alpha = player.buff_invisible_prev_alpha; - player.alpha = autocvar_g_buffs_invisible_alpha; - } - - BUFF_ONREM(BUFF_INVISIBLE) - player.alpha = player.buff_invisible_prev_alpha; - - player.oldbuffs = player.buffs; - if(player.buffs) - { - if(!player.buff_model) - buffs_BuffModel_Spawn(player); - - player.buff_model.color = buff.m_color; - player.buff_model.glowmod = buff_GlowColor(player.buff_model); - player.buff_model.skin = buff.m_skin; - - player.effects |= EF_NOSHADOW; - } - else - { - delete(player.buff_model); - player.buff_model = NULL; - - player.effects &= ~(EF_NOSHADOW); - } - } - - if(player.buff_model) - { - player.buff_model.effects = player.effects; - player.buff_model.effects |= EF_LOWPRECISION; - player.buff_model.effects = player.buff_model.effects & EFMASK_CHEAP; // eat performance - - player.buff_model.alpha = player.alpha; - } - -#undef BUFF_ONADD -#undef BUFF_ONREM -} - -MUTATOR_HOOKFUNCTION(buffs, SpectateCopy) -{ - entity spectatee = M_ARGV(0, entity); - entity client = M_ARGV(1, entity); - - client.buffs = spectatee.buffs; -} - -MUTATOR_HOOKFUNCTION(buffs, VehicleEnter) -{ - entity player = M_ARGV(0, entity); - entity veh = M_ARGV(1, entity); - - veh.buffs = player.buffs; - player.buffs = 0; - veh.buff_time = max(0, player.buff_time - time); - player.buff_time = 0; -} - -MUTATOR_HOOKFUNCTION(buffs, VehicleExit) -{ - entity player = M_ARGV(0, entity); - entity veh = M_ARGV(1, entity); - - player.buffs = player.oldbuffs = veh.buffs; - veh.buffs = 0; - player.buff_time = time + veh.buff_time; - veh.buff_time = 0; -} - -MUTATOR_HOOKFUNCTION(buffs, PlayerRegen) -{ - entity player = M_ARGV(0, entity); - - if(player.buffs & BUFF_MEDIC.m_itemid) - { - M_ARGV(2, float) = autocvar_g_buffs_medic_rot; // rot_mod - M_ARGV(4, float) = M_ARGV(1, float) = autocvar_g_buffs_medic_max; // limit_mod = max_mod - M_ARGV(2, float) = autocvar_g_buffs_medic_regen; // regen_mod - } - - if(player.buffs & BUFF_SPEED.m_itemid) - M_ARGV(2, float) = autocvar_g_buffs_speed_regen; // regen_mod -} - -REPLICATE(cvar_cl_buffs_autoreplace, bool, "cl_buffs_autoreplace"); - -MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Buffs"); -} - -MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsPrettyString) +string BUFF_NAME(int i) { - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Buffs"); + Buff b = Buffs_from(i); + return sprintf("%s%s", rgb_to_hexcolor(b.m_color), b.m_prettyName); } -void buffs_DelayedInit(entity this) +entity buff_FirstFromFlags(int _buffs) { - if(autocvar_g_buffs_spawn_count > 0) - if(find(NULL, classname, "item_buff") == NULL) - { - float i; - for(i = 0; i < autocvar_g_buffs_spawn_count; ++i) - { - entity e = spawn(); - e.spawnflags |= 64; // always randomize - e.velocity = randomvec() * 250; // this gets reset anyway if random location works - buff_Init(e); - } - } + if (flags) + { + FOREACH(Buffs, it.m_itemid & _buffs, LAMBDA(return it)); + } + return BUFF_Null; } -#endif diff --git a/qcsrc/common/mutators/mutator/buffs/buffs.qh b/qcsrc/common/mutators/mutator/buffs/buffs.qh new file mode 100644 index 0000000000..ae76d9f59e --- /dev/null +++ b/qcsrc/common/mutators/mutator/buffs/buffs.qh @@ -0,0 +1,70 @@ +#pragma once + +#include +#include + +#ifdef GAMEQC +REGISTER_WAYPOINT(Buff, _("Buff"), '1 0.5 0', 1); +REGISTER_RADARICON(Buff, 1); +#endif + +REGISTRY(Buffs, BITS(5)) +#define Buffs_from(i) _Buffs_from(i, BUFF_Null) +REGISTER_REGISTRY(Buffs) +REGISTRY_CHECK(Buffs) + +#define REGISTER_BUFF(id) \ + REGISTER(Buffs, BUFF_##id, m_id, NEW(Buff)) + +#include +CLASS(Buff, Pickup) + /** bit index */ + ATTRIB(Buff, m_itemid, int, 0); + ATTRIB(Buff, m_name, string, "buff"); + ATTRIB(Buff, m_color, vector, '1 1 1'); + ATTRIB(Buff, m_prettyName, string, "Buff"); + ATTRIB(Buff, m_skin, int, 0); + ATTRIB(Buff, m_sprite, string, ""); + METHOD(Buff, display, void(entity this, void(string name, string icon) returns)) { + returns(this.m_prettyName, sprintf("/gfx/hud/%s/buff_%s", cvar_string("menu_skin"), this.m_name)); + } +#ifdef SVQC + METHOD(Buff, m_time, float(Buff this)) + { return cvar(strcat("g_buffs_", this.netname, "_time")); } +#endif +ENDCLASS(Buff) + +STATIC_INIT(REGISTER_BUFFS) { + FOREACH(Buffs, true, { + it.netname = it.m_name; \ + it.m_itemid = BIT(it.m_id - 1); \ + it.m_sprite = strzone(strcat("buff-", it.m_name)); \ + }); +} + +#ifdef SVQC + // .int buffs = _STAT(BUFFS); + void buff_Init(entity ent); + void buff_Init_Compat(entity ent, entity replacement); + #define BUFF_SPAWNFUNC(e, b, t) spawnfunc(item_buff_##e) { \ + this.buffs = b.m_itemid; \ + this.team = t; \ + buff_Init(this); \ + } + #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) + #define BUFF_SPAWNFUNC_Q3TA_COMPAT(o, r) spawnfunc(item_##o) { buff_Init_Compat(this, r); } +#else + #define BUFF_SPAWNFUNC(e, b, t) + #define BUFF_SPAWNFUNCS(e, b) + #define BUFF_SPAWNFUNC_Q3TA_COMPAT(o, r) +#endif + +REGISTER_BUFF(Null); +BUFF_SPAWNFUNCS(random, BUFF_Null) + +#include "all.inc" diff --git a/qcsrc/common/mutators/mutator/buffs/cl_buffs.qc b/qcsrc/common/mutators/mutator/buffs/cl_buffs.qc new file mode 100644 index 0000000000..ca14753727 --- /dev/null +++ b/qcsrc/common/mutators/mutator/buffs/cl_buffs.qc @@ -0,0 +1,22 @@ +#include "cl_buffs.qh" + +REGISTER_MUTATOR(cl_buffs, true); +MUTATOR_HOOKFUNCTION(cl_buffs, HUD_Powerups_add) +{ + int allBuffs = STAT(BUFFS); + FOREACH(Buffs, it.m_itemid & allBuffs, LAMBDA( + addPowerupItem(it.m_prettyName, strcat("buff_", it.m_name), it.m_color, bound(0, STAT(BUFF_TIME) - time, 99), 60); + )); +} +MUTATOR_HOOKFUNCTION(cl_buffs, WP_Format) +{ + entity this = M_ARGV(0, entity); + string s = M_ARGV(1, string); + if (s == WP_Buff.netname || s == RADARICON_Buff.netname) + { + Buff b = Buffs_from(this.wp_extra); + M_ARGV(2, vector) = b.m_color; + M_ARGV(3, string) = b.m_prettyName; + return true; + } +} diff --git a/qcsrc/common/mutators/mutator/buffs/cl_buffs.qh b/qcsrc/common/mutators/mutator/buffs/cl_buffs.qh new file mode 100644 index 0000000000..c93902291d --- /dev/null +++ b/qcsrc/common/mutators/mutator/buffs/cl_buffs.qh @@ -0,0 +1,3 @@ +#pragma once + +#include "buffs.qh" diff --git a/qcsrc/common/mutators/mutator/buffs/module.inc b/qcsrc/common/mutators/mutator/buffs/module.inc deleted file mode 100644 index c24892836a..0000000000 --- a/qcsrc/common/mutators/mutator/buffs/module.inc +++ /dev/null @@ -1,46 +0,0 @@ -#include "all.qc" -#ifdef SVQC -#include "buffs.qc" -#endif - -#ifdef IMPLEMENTATION - -string BUFF_NAME(int i) -{ - Buff b = Buffs_from(i); - return sprintf("%s%s", rgb_to_hexcolor(b.m_color), b.m_prettyName); -} - -entity buff_FirstFromFlags(int _buffs) -{ - if (flags) - { - FOREACH(Buffs, it.m_itemid & _buffs, LAMBDA(return it)); - } - return BUFF_Null; -} - -#ifdef CSQC -REGISTER_MUTATOR(cl_buffs, true); -MUTATOR_HOOKFUNCTION(cl_buffs, HUD_Powerups_add) -{ - int allBuffs = STAT(BUFFS); - FOREACH(Buffs, it.m_itemid & allBuffs, LAMBDA( - addPowerupItem(it.m_prettyName, strcat("buff_", it.m_name), it.m_color, bound(0, STAT(BUFF_TIME) - time, 99), 60); - )); -} -MUTATOR_HOOKFUNCTION(cl_buffs, WP_Format) -{ - entity this = M_ARGV(0, entity); - string s = M_ARGV(1, string); - if (s == WP_Buff.netname || s == RADARICON_Buff.netname) - { - Buff b = Buffs_from(this.wp_extra); - M_ARGV(2, vector) = b.m_color; - M_ARGV(3, string) = b.m_prettyName; - return true; - } -} - -#endif -#endif diff --git a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc new file mode 100644 index 0000000000..d9223b302a --- /dev/null +++ b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc @@ -0,0 +1,1002 @@ +#include "sv_buffs.qh" + +#include +#include + +.float buff_time = _STAT(BUFF_TIME); +void buffs_DelayedInit(entity this); + +REGISTER_MUTATOR(buffs, cvar("g_buffs")) +{ + MUTATOR_ONADD + { + InitializeEntity(NULL, buffs_DelayedInit, INITPRIO_FINDTARGET); + } +} + +bool buffs_BuffModel_Customize(entity this, entity client) +{ + entity player, myowner; + bool same_team; + + player = WaypointSprite_getviewentity(client); + myowner = this.owner; + same_team = (SAME_TEAM(player, myowner) || SAME_TEAM(player, myowner)); + + if(myowner.alpha <= 0.5 && !same_team && myowner.alpha != 0) + return false; + + if(MUTATOR_CALLHOOK(BuffModel_Customize, this, player)) + return false; + + if(player == myowner || (IS_SPEC(client) && client.enemy == myowner)) + { + // somewhat hide the model, but keep the glow + this.effects = 0; + this.alpha = -1; + } + else + { + this.effects = EF_FULLBRIGHT | EF_LOWPRECISION; + this.alpha = 1; + } + return true; +} + +void buffs_BuffModel_Spawn(entity player) +{ + player.buff_model = spawn(); + setmodel(player.buff_model, MDL_BUFF); + setsize(player.buff_model, '0 0 -40', '0 0 40'); + setattachment(player.buff_model, player, ""); + setorigin(player.buff_model, '0 0 1' * (player.buff_model.maxs.z * 1)); + player.buff_model.owner = player; + player.buff_model.scale = 0.7; + player.buff_model.pflags = PFLAGS_FULLDYNAMIC; + player.buff_model.light_lev = 200; + setcefc(player.buff_model, buffs_BuffModel_Customize); +} + +vector buff_GlowColor(entity buff) +{ + //if(buff.team) { return Team_ColorRGB(buff.team); } + return buff.m_color; +} + +void buff_Effect(entity player, string eff) +{ + if(!autocvar_g_buffs_effects) { return; } + + if(time >= player.buff_effect_delay) + { + Send_Effect_(eff, player.origin + ((player.mins + player.maxs) * 0.5), '0 0 0', 1); + player.buff_effect_delay = time + 0.05; // prevent spam + } +} + +// buff item +bool buff_Waypoint_visible_for_player(entity this, entity player, entity view) +{ + if(!this.owner.buff_active && !this.owner.buff_activetime) + return false; + + if (view.buffs) + { + return view.cvar_cl_buffs_autoreplace == false || view.buffs != this.owner.buffs; + } + + return WaypointSprite_visible_for_player(this, player, view); +} + +void buff_Waypoint_Spawn(entity e) +{ + entity buff = buff_FirstFromFlags(e.buffs); + entity wp = WaypointSprite_Spawn(WP_Buff, 0, autocvar_g_buffs_waypoint_distance, e, '0 0 1' * e.maxs.z, NULL, e.team, e, buff_waypoint, true, RADARICON_Buff); + wp.wp_extra = buff.m_id; + WaypointSprite_UpdateTeamRadar(e.buff_waypoint, RADARICON_Buff, e.glowmod); + e.buff_waypoint.waypointsprite_visible_for_player = buff_Waypoint_visible_for_player; +} + +void buff_SetCooldown(entity this, float cd) +{ + cd = max(0, cd); + + if(!this.buff_waypoint) + buff_Waypoint_Spawn(this); + + WaypointSprite_UpdateBuildFinished(this.buff_waypoint, time + cd); + this.buff_activetime = cd; + this.buff_active = !cd; +} + +void buff_Respawn(entity this) +{ + if(gameover) { return; } + + vector oldbufforigin = this.origin; + this.velocity = '0 0 200'; + + if(!MoveToRandomMapLocation(this, 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(this, true); + setorigin(this, spot.origin); + this.velocity = ((randomvec() * 100) + '0 0 200'); + this.angles = spot.angles; + } + + tracebox(this.origin, this.mins * 1.5, this.maxs * 1.5, this.origin, MOVE_NOMONSTERS, this); + + setorigin(this, trace_endpos); // attempt to unstick + + set_movetype(this, MOVETYPE_TOSS); + + makevectors(this.angles); + this.angles = '0 0 0'; + if(autocvar_g_buffs_random_lifetime > 0) + this.lifetime = time + autocvar_g_buffs_random_lifetime; + + Send_Effect(EFFECT_ELECTRO_COMBO, oldbufforigin + ((this.mins + this.maxs) * 0.5), '0 0 0', 1); + Send_Effect(EFFECT_ELECTRO_COMBO, CENTER_OR_VIEWOFS(this), '0 0 0', 1); + + WaypointSprite_Ping(this.buff_waypoint); + + sound(this, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere) +} + +void buff_Touch(entity this, entity toucher) +{ + if(gameover) { return; } + + if(ITEM_TOUCH_NEEDKILL()) + { + buff_Respawn(this); + return; + } + + if((this.team && DIFF_TEAM(toucher, this)) + || (STAT(FROZEN, toucher)) + || (toucher.vehicle) + || (!this.buff_active) + ) + { + // can't touch this + return; + } + + if(MUTATOR_CALLHOOK(BuffTouch, this, toucher)) + return; + toucher = M_ARGV(1, entity); + + if(!IS_PLAYER(toucher)) + return; // incase mutator changed toucher + + if (toucher.buffs) + { + if (toucher.cvar_cl_buffs_autoreplace && toucher.buffs != this.buffs) + { + int buffid = buff_FirstFromFlags(toucher.buffs).m_id; + //Send_Notification(NOTIF_ONE, toucher, MSG_MULTI, ITEM_BUFF_DROP, toucher.buffs); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ITEM_BUFF_LOST, toucher.netname, buffid); + + toucher.buffs = 0; + //sound(toucher, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); + } + else { return; } // do nothing + } + + this.owner = toucher; + this.buff_active = false; + this.lifetime = 0; + int buffid = buff_FirstFromFlags(this.buffs).m_id; + Send_Notification(NOTIF_ONE, toucher, MSG_MULTI, ITEM_BUFF_GOT, buffid); + Send_Notification(NOTIF_ALL_EXCEPT, toucher, MSG_INFO, INFO_ITEM_BUFF, toucher.netname, buffid); + + Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(this), '0 0 0', 1); + sound(toucher, CH_TRIGGER, SND_SHIELD_RESPAWN, VOL_BASE, ATTN_NORM); + toucher.buffs |= (this.buffs); +} + +float buff_Available(entity buff) +{ + if (buff == BUFF_Null) + return false; + if (buff == BUFF_AMMO && ((start_items & IT_UNLIMITED_WEAPON_AMMO) || (start_items & IT_UNLIMITED_AMMO) || (cvar("g_melee_only")))) + return false; + if (buff == BUFF_VAMPIRE && cvar("g_vampire")) + return false; + return cvar(strcat("g_buffs_", buff.m_name)); +} + +.int buff_seencount; + +void buff_NewType(entity ent, float cb) +{ + RandomSelection_Init(); + FOREACH(Buffs, buff_Available(it), LAMBDA( + it.buff_seencount += 1; + // if it's already been chosen, give it a lower priority + RandomSelection_Add(NULL, it.m_itemid, string_null, 1, max(0.2, 1 / it.buff_seencount)); + )); + ent.buffs = RandomSelection_chosen_float; +} + +void buff_Think(entity this) +{ + if(this.buffs != this.oldbuffs) + { + entity buff = buff_FirstFromFlags(this.buffs); + this.color = buff.m_color; + this.glowmod = buff_GlowColor(buff); + this.skin = buff.m_skin; + + setmodel(this, MDL_BUFF); + + if(this.buff_waypoint) + { + //WaypointSprite_Disown(this.buff_waypoint, 1); + WaypointSprite_Kill(this.buff_waypoint); + buff_Waypoint_Spawn(this); + if(this.buff_activetime) + WaypointSprite_UpdateBuildFinished(this.buff_waypoint, time + this.buff_activetime - frametime); + } + + this.oldbuffs = this.buffs; + } + + if(!gameover) + if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime) + if(!this.buff_activetime_updated) + { + buff_SetCooldown(this, this.buff_activetime); + this.buff_activetime_updated = true; + } + + if(!this.buff_active && !this.buff_activetime) + if(!this.owner || STAT(FROZEN, this.owner) || IS_DEAD(this.owner) || !this.owner.iscreature || !(this.owner.buffs & this.buffs)) + { + buff_SetCooldown(this, autocvar_g_buffs_cooldown_respawn + frametime); + this.owner = NULL; + if(autocvar_g_buffs_randomize) + buff_NewType(this, this.buffs); + + if(autocvar_g_buffs_random_location || (this.spawnflags & 64)) + buff_Respawn(this); + } + + if(this.buff_activetime) + if(!gameover) + if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime) + { + this.buff_activetime = max(0, this.buff_activetime - frametime); + + if(!this.buff_activetime) + { + this.buff_active = true; + sound(this, CH_TRIGGER, SND_STRENGTH_RESPAWN, VOL_BASE, ATTN_NORM); + Send_Effect(EFFECT_ITEM_RESPAWN, CENTER_OR_VIEWOFS(this), '0 0 0', 1); + } + } + + if(this.buff_active) + { + if(this.team && !this.buff_waypoint) + buff_Waypoint_Spawn(this); + + if(this.lifetime) + if(time >= this.lifetime) + buff_Respawn(this); + } + + this.nextthink = time; + //this.angles_y = time * 110.1; +} + +void buff_Waypoint_Reset(entity this) +{ + WaypointSprite_Kill(this.buff_waypoint); + + if(this.buff_activetime) { buff_Waypoint_Spawn(this); } +} + +void buff_Reset(entity this) +{ + if(autocvar_g_buffs_randomize) + buff_NewType(this, this.buffs); + this.owner = NULL; + buff_SetCooldown(this, autocvar_g_buffs_cooldown_activate); + buff_Waypoint_Reset(this); + this.buff_activetime_updated = false; + + if(autocvar_g_buffs_random_location || (this.spawnflags & 64)) + buff_Respawn(this); +} + +bool buff_Customize(entity this, entity client) +{ + entity player = WaypointSprite_getviewentity(client); + if(!this.buff_active || (this.team && DIFF_TEAM(player, this))) + { + this.alpha = 0.3; + if(this.effects & EF_FULLBRIGHT) { this.effects &= ~(EF_FULLBRIGHT); } + this.pflags = 0; + } + else + { + this.alpha = 1; + if(!(this.effects & EF_FULLBRIGHT)) { this.effects |= EF_FULLBRIGHT; } + this.light_lev = 220 + 36 * sin(time); + this.pflags = PFLAGS_FULLDYNAMIC; + } + return true; +} + +void buff_Init(entity this) +{ + if(!cvar("g_buffs")) { delete(this); return; } + + if(!teamplay && this.team) { this.team = 0; } + + entity buff = buff_FirstFromFlags(this.buffs); + + if(!this.buffs || buff_Available(buff)) + buff_NewType(this, 0); + + this.classname = "item_buff"; + this.solid = SOLID_TRIGGER; + this.flags = FL_ITEM; + setthink(this, buff_Think); + settouch(this, buff_Touch); + this.reset = buff_Reset; + this.nextthink = time + 0.1; + this.gravity = 1; + set_movetype(this, MOVETYPE_TOSS); + this.scale = 1; + this.skin = buff.m_skin; + this.effects = EF_FULLBRIGHT | EF_STARDUST | EF_NOSHADOW; + this.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY; + setcefc(this, buff_Customize); + //this.gravity = 100; + this.color = buff.m_color; + this.glowmod = buff_GlowColor(this); + buff_SetCooldown(this, autocvar_g_buffs_cooldown_activate + game_starttime); + this.buff_active = !this.buff_activetime; + this.pflags = PFLAGS_FULLDYNAMIC; + + if(this.spawnflags & 1) + this.noalign = true; + + if(this.noalign) + set_movetype(this, MOVETYPE_NONE); // reset by random location + + setmodel(this, MDL_BUFF); + setsize(this, BUFF_MIN, BUFF_MAX); + + if(cvar("g_buffs_random_location") || (this.spawnflags & 64)) + buff_Respawn(this); +} + +void buff_Init_Compat(entity ent, entity replacement) +{ + if (ent.spawnflags & 2) + ent.team = NUM_TEAM_1; + else if (ent.spawnflags & 4) + ent.team = NUM_TEAM_2; + + ent.buffs = replacement.m_itemid; + + buff_Init(ent); +} + +void buff_SpawnReplacement(entity ent, entity old) +{ + setorigin(ent, old.origin); + ent.angles = old.angles; + ent.noalign = (old.noalign || (old.spawnflags & 1)); + + buff_Init(ent); +} + +void buff_Vengeance_DelayedDamage(entity this) +{ + if(this.enemy) + Damage(this.enemy, this.owner, this.owner, this.dmg, DEATH_BUFF.m_id, this.enemy.origin, '0 0 0'); + + delete(this); + return; +} + +// note: only really useful in teamplay +void buff_Medic_Heal(entity this) +{ + FOREACH_CLIENT(IS_PLAYER(it) && it != this && vdist(it.origin - this.origin, <=, autocvar_g_buffs_medic_heal_range), + { + if(SAME_TEAM(it, this)) + if(it.health < autocvar_g_balance_health_regenstable) + { + Send_Effect(EFFECT_HEALING, it.origin, '0 0 0', 1); + it.health = bound(0, it.health + autocvar_g_buffs_medic_heal_amount, autocvar_g_balance_health_regenstable); + } + }); +} + +float buff_Inferno_CalculateTime(float x, float offset_x, float offset_y, float intersect_x, float intersect_y, float base) +{ + return offset_y + (intersect_y - offset_y) * logn(((x - offset_x) * ((base - 1) / intersect_x)) + 1, base); +} + +// mutator hooks +MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_SplitHealthArmor) +{ + entity frag_target = M_ARGV(2, entity); + float frag_deathtype = M_ARGV(6, float); + float frag_damage = M_ARGV(7, float); + + if(frag_deathtype == DEATH_BUFF.m_id) { return; } + + if(frag_target.buffs & BUFF_RESISTANCE.m_itemid) + { + vector v = healtharmor_applydamage(50, autocvar_g_buffs_resistance_blockpercent, frag_deathtype, frag_damage); + M_ARGV(4, float) = v.x; // take + M_ARGV(5, float) = v.y; // save + } +} + +MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_Calculate) +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + float frag_deathtype = M_ARGV(3, float); + float frag_damage = M_ARGV(4, float); + vector frag_force = M_ARGV(6, vector); + + if(frag_deathtype == DEATH_BUFF.m_id) { return; } + + if(frag_target.buffs & BUFF_SPEED.m_itemid) + if(frag_target != frag_attacker) + frag_damage *= autocvar_g_buffs_speed_damage_take; + + if(frag_target.buffs & BUFF_MEDIC.m_itemid) + 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 = max(5, frag_target.health - autocvar_g_buffs_medic_survive_health); + + if(frag_target.buffs & BUFF_JUMP.m_itemid) + if(frag_deathtype == DEATH_FALL.m_id) + frag_damage = 0; + + if(frag_target.buffs & BUFF_VENGEANCE.m_itemid) + 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; + setthink(dmgent, buff_Vengeance_DelayedDamage); + dmgent.nextthink = time + 0.1; + } + + if(frag_target.buffs & BUFF_BASH.m_itemid) + if(frag_attacker != frag_target) + frag_force = '0 0 0'; + + if(frag_attacker.buffs & BUFF_BASH.m_itemid) + if(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.m_itemid) + if(frag_target != frag_attacker) + frag_target.buff_disability_time = time + autocvar_g_buffs_disability_slowtime; + + if(frag_target.buffs & BUFF_INFERNO.m_itemid) + { + if(frag_deathtype == DEATH_FIRE.m_id) + frag_damage = 0; + if(frag_deathtype == DEATH_LAVA.m_id) + frag_damage *= 0.5; // TODO: cvarize? + } + + if(frag_attacker.buffs & BUFF_LUCK.m_itemid) + if(frag_attacker != frag_target) + if(autocvar_g_buffs_luck_damagemultiplier > 0) + if(random() <= autocvar_g_buffs_luck_chance) + frag_damage *= autocvar_g_buffs_luck_damagemultiplier; + + if(frag_attacker.buffs & BUFF_INFERNO.m_itemid) + if(frag_target != frag_attacker) { + float btime = buff_Inferno_CalculateTime( + frag_damage, + 0, + autocvar_g_buffs_inferno_burntime_min_time, + autocvar_g_buffs_inferno_burntime_target_damage, + autocvar_g_buffs_inferno_burntime_target_time, + autocvar_g_buffs_inferno_burntime_factor + ); + Fire_AddDamage(frag_target, frag_attacker, (frag_damage * autocvar_g_buffs_inferno_damagemultiplier), btime, DEATH_BUFF.m_id); + } + + // this... is ridiculous (TODO: fix!) + if(frag_attacker.buffs & BUFF_VAMPIRE.m_itemid) + if(!frag_target.vehicle) + if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype)) + if(!IS_DEAD(frag_target)) + if(IS_PLAYER(frag_target) || IS_MONSTER(frag_target)) + if(frag_attacker != frag_target) + if(!STAT(FROZEN, frag_target)) + 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); + if(frag_target.armorvalue) + frag_attacker.armorvalue = bound(0, frag_attacker.armorvalue + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.armorvalue), g_pickup_armorsmall_max); + } + + M_ARGV(4, float) = frag_damage; + M_ARGV(6, vector) = frag_force; +} + +MUTATOR_HOOKFUNCTION(buffs, PlayerSpawn) +{ + entity player = M_ARGV(0, entity); + + player.buffs = 0; + player.buff_time = 0; + // reset timers here to prevent them continuing after re-spawn + player.buff_disability_time = 0; + player.buff_disability_effect_time = 0; +} + +.float stat_sv_maxspeed; +.float stat_sv_airspeedlimit_nonqw; +.float stat_sv_jumpvelocity; + +MUTATOR_HOOKFUNCTION(buffs, PlayerPhysics) +{ + entity player = M_ARGV(0, entity); + + if(player.buffs & BUFF_SPEED.m_itemid) + { + player.stat_sv_maxspeed *= autocvar_g_buffs_speed_speed; + player.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_speed_speed; + } + + if(time < player.buff_disability_time) + { + player.stat_sv_maxspeed *= autocvar_g_buffs_disability_speed; + player.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_disability_speed; + } + + if(player.buffs & BUFF_JUMP.m_itemid) + { + // automatically reset, no need to worry + player.stat_sv_jumpvelocity = autocvar_g_buffs_jump_height; + } +} + +MUTATOR_HOOKFUNCTION(buffs, PlayerJump) +{ + entity player = M_ARGV(0, entity); + + if(player.buffs & BUFF_JUMP.m_itemid) + M_ARGV(1, float) = autocvar_g_buffs_jump_height; +} + +MUTATOR_HOOKFUNCTION(buffs, MonsterMove) +{ + entity mon = M_ARGV(0, entity); + + if(time < mon.buff_disability_time) + { + M_ARGV(1, float) *= autocvar_g_buffs_disability_speed; // run speed + M_ARGV(2, float) *= autocvar_g_buffs_disability_speed; // walk speed + } +} + +MUTATOR_HOOKFUNCTION(buffs, PlayerDies) +{ + entity frag_target = M_ARGV(2, entity); + + if(frag_target.buffs) + { + int buffid = buff_FirstFromFlags(frag_target.buffs).m_id; + Send_Notification(NOTIF_ALL_EXCEPT, frag_target, MSG_INFO, INFO_ITEM_BUFF_LOST, frag_target.netname, buffid); + frag_target.buffs = 0; + + if(frag_target.buff_model) + { + delete(frag_target.buff_model); + frag_target.buff_model = NULL; + } + } +} + +MUTATOR_HOOKFUNCTION(buffs, PlayerUseKey, CBC_ORDER_FIRST) +{ + if(MUTATOR_RETURNVALUE || gameover) { return; } + + entity player = M_ARGV(0, entity); + + if(player.buffs) + { + int buffid = buff_FirstFromFlags(player.buffs).m_id; + Send_Notification(NOTIF_ONE, player, MSG_MULTI, ITEM_BUFF_DROP, buffid); + Send_Notification(NOTIF_ALL_EXCEPT, player, MSG_INFO, INFO_ITEM_BUFF_LOST, player.netname, buffid); + + player.buffs = 0; + player.buff_time = 0; // already notified + sound(player, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); + return true; + } +} + +MUTATOR_HOOKFUNCTION(buffs, ForbidThrowCurrentWeapon) +{ + if(MUTATOR_RETURNVALUE || gameover) { return; } + entity player = M_ARGV(0, entity); + + if(player.buffs & BUFF_SWAPPER.m_itemid) + { + float best_distance = autocvar_g_buffs_swapper_range; + entity closest = NULL; + FOREACH_CLIENT(IS_PLAYER(it), LAMBDA( + if(!IS_DEAD(it) && !STAT(FROZEN, it) && !it.vehicle) + if(DIFF_TEAM(it, player)) + { + float test = vlen2(player.origin - it.origin); + if(test <= best_distance * best_distance) + { + best_distance = sqrt(test); + closest = it; + } + } + )); + + if(closest) + { + vector my_org, my_vel, my_ang, their_org, their_vel, their_ang; + + my_org = player.origin; + my_vel = player.velocity; + my_ang = player.angles; + their_org = closest.origin; + their_vel = closest.velocity; + their_ang = closest.angles; + + Drop_Special_Items(closest); + + MUTATOR_CALLHOOK(PortalTeleport, player); // initiate flag dropper + + setorigin(player, their_org); + setorigin(closest, my_org); + + closest.velocity = my_vel; + closest.angles = my_ang; + closest.fixangle = true; + closest.oldorigin = my_org; + closest.oldvelocity = my_vel; + player.velocity = their_vel; + player.angles = their_ang; + player.fixangle = true; + player.oldorigin = their_org; + player.oldvelocity = their_vel; + + // set pusher so player gets the kill if they fall into void + closest.pusher = player; + closest.pushltime = time + autocvar_g_maxpushtime; + closest.istypefrag = PHYS_INPUT_BUTTON_CHAT(closest); + + Send_Effect(EFFECT_ELECTRO_COMBO, their_org, '0 0 0', 1); + Send_Effect(EFFECT_ELECTRO_COMBO, my_org, '0 0 0', 1); + + sound(player, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NORM); + sound(closest, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NORM); + + // TODO: add a counter to handle how many times one can teleport, and a delay to prevent spam + player.buffs = 0; + return true; + } + } +} + +bool buffs_RemovePlayer(entity player) +{ + if(player.buff_model) + { + delete(player.buff_model); + player.buff_model = NULL; + } + + // also reset timers here to prevent them continuing after spectating + player.buff_disability_time = 0; + player.buff_disability_effect_time = 0; + + return false; +} +MUTATOR_HOOKFUNCTION(buffs, MakePlayerObserver) { entity player = M_ARGV(0, entity); return buffs_RemovePlayer(player); } +MUTATOR_HOOKFUNCTION(buffs, ClientDisconnect) { entity player = M_ARGV(0, entity); return buffs_RemovePlayer(player); } + +MUTATOR_HOOKFUNCTION(buffs, CustomizeWaypoint) +{ + entity wp = M_ARGV(0, entity); + entity player = M_ARGV(1, entity); + + entity e = WaypointSprite_getviewentity(player); + + // if you have the invisibility powerup, sprites ALWAYS are restricted to your team + // but only apply this to real players, not to spectators + if((wp.owner.flags & FL_CLIENT) && (wp.owner.buffs & BUFF_INVISIBLE.m_itemid) && (e == player)) + if(DIFF_TEAM(wp.owner, e)) + return true; +} + +MUTATOR_HOOKFUNCTION(buffs, OnEntityPreSpawn, CBC_ORDER_LAST) +{ + entity ent = M_ARGV(0, entity); + + if(autocvar_g_buffs_replace_powerups) + switch(ent.classname) + { + case "item_strength": + case "item_invincible": + { + entity e = spawn(); + buff_SpawnReplacement(e, ent); + return true; + } + } +} + +MUTATOR_HOOKFUNCTION(buffs, WeaponRateFactor) +{ + entity player = M_ARGV(1, entity); + + if(player.buffs & BUFF_SPEED.m_itemid) + M_ARGV(0, float) *= autocvar_g_buffs_speed_rate; + + if(time < player.buff_disability_time) + M_ARGV(0, float) *= autocvar_g_buffs_disability_rate; +} + +MUTATOR_HOOKFUNCTION(buffs, WeaponSpeedFactor) +{ + entity player = M_ARGV(1, entity); + + if(player.buffs & BUFF_SPEED.m_itemid) + M_ARGV(0, float) *= autocvar_g_buffs_speed_weaponspeed; + + if(time < player.buff_disability_time) + M_ARGV(0, float) *= autocvar_g_buffs_disability_weaponspeed; +} + +MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink) +{ + entity player = M_ARGV(0, entity); + + if(gameover || IS_DEAD(player)) { return; } + + if(time < player.buff_disability_time) + if(time >= player.buff_disability_effect_time) + { + Send_Effect(EFFECT_SMOKING, player.origin + ((player.mins + player.maxs) * 0.5), '0 0 0', 1); + player.buff_disability_effect_time = time + 0.5; + } + + // handle buff lost status + // 1: notify everyone else + // 2: notify carrier as well + int buff_lost = 0; + + if(player.buff_time && player.buffs) + if(time >= player.buff_time) + { + player.buff_time = 0; + buff_lost = 2; + } + + if(STAT(FROZEN, player)) { buff_lost = 1; } + + if(buff_lost) + { + if(player.buffs) + { + int buffid = buff_FirstFromFlags(player.buffs).m_id; + if(buff_lost == 2) + { + Send_Notification(NOTIF_ONE, player, MSG_MULTI, ITEM_BUFF_DROP, buffid); // TODO: special timeout message? + sound(player, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); + } + else + Send_Notification(NOTIF_ALL_EXCEPT, player, MSG_INFO, INFO_ITEM_BUFF_LOST, player.netname, buffid); + player.buffs = 0; + } + } + + if(player.buffs & BUFF_MAGNET.m_itemid) + { + vector pickup_size; + FOREACH_ENTITY_FLAGS(flags, FL_ITEM, + { + if(it.buffs) + pickup_size = '1 1 1' * autocvar_g_buffs_magnet_range_buff; + else + pickup_size = '1 1 1' * autocvar_g_buffs_magnet_range_item; + + if(boxesoverlap(player.absmin - pickup_size, player.absmax + pickup_size, it.absmin, it.absmax)) + { + if(gettouch(it)) + gettouch(it)(it, player); + } + }); + } + + if(player.buffs & BUFF_AMMO.m_itemid) + if(player.clip_size) + player.clip_load = player.(weapon_load[PS(player).m_switchweapon.m_id]) = player.clip_size; + + if((player.buffs & BUFF_INVISIBLE.m_itemid) && (player.oldbuffs & BUFF_INVISIBLE.m_itemid)) + if(player.alpha != autocvar_g_buffs_invisible_alpha) + player.alpha = autocvar_g_buffs_invisible_alpha; // powerups reset alpha, so we must enforce this (TODO) + + if(player.buffs & BUFF_MEDIC.m_itemid) + if(time >= player.buff_medic_healtime) + { + buff_Medic_Heal(player); + player.buff_medic_healtime = time + autocvar_g_buffs_medic_heal_delay; + } + +#define BUFF_ONADD(b) if ( (player.buffs & (b).m_itemid) && !(player.oldbuffs & (b).m_itemid)) +#define BUFF_ONREM(b) if (!(player.buffs & (b).m_itemid) && (player.oldbuffs & (b).m_itemid)) + + if(player.buffs != player.oldbuffs) + { + entity buff = buff_FirstFromFlags(player.buffs); + float bufftime = buff != BUFF_Null ? buff.m_time(buff) : 0; + player.buff_time = (bufftime) ? time + bufftime : 0; + + BUFF_ONADD(BUFF_AMMO) + { + player.buff_ammo_prev_infitems = (player.items & IT_UNLIMITED_WEAPON_AMMO); + player.items |= IT_UNLIMITED_WEAPON_AMMO; + + if(player.clip_load) + player.buff_ammo_prev_clipload = player.clip_load; + player.clip_load = player.(weapon_load[PS(player).m_switchweapon.m_id]) = player.clip_size; + } + + BUFF_ONREM(BUFF_AMMO) + { + if(player.buff_ammo_prev_infitems) + player.items |= IT_UNLIMITED_WEAPON_AMMO; + else + player.items &= ~IT_UNLIMITED_WEAPON_AMMO; + + if(player.buff_ammo_prev_clipload) + player.clip_load = player.buff_ammo_prev_clipload; + } + + BUFF_ONADD(BUFF_INVISIBLE) + { + if(time < player.strength_finished && g_instagib) + player.alpha = autocvar_g_instagib_invis_alpha; + else + player.alpha = player.buff_invisible_prev_alpha; + player.alpha = autocvar_g_buffs_invisible_alpha; + } + + BUFF_ONREM(BUFF_INVISIBLE) + player.alpha = player.buff_invisible_prev_alpha; + + player.oldbuffs = player.buffs; + if(player.buffs) + { + if(!player.buff_model) + buffs_BuffModel_Spawn(player); + + player.buff_model.color = buff.m_color; + player.buff_model.glowmod = buff_GlowColor(player.buff_model); + player.buff_model.skin = buff.m_skin; + + player.effects |= EF_NOSHADOW; + } + else + { + delete(player.buff_model); + player.buff_model = NULL; + + player.effects &= ~(EF_NOSHADOW); + } + } + + if(player.buff_model) + { + player.buff_model.effects = player.effects; + player.buff_model.effects |= EF_LOWPRECISION; + player.buff_model.effects = player.buff_model.effects & EFMASK_CHEAP; // eat performance + + player.buff_model.alpha = player.alpha; + } + +#undef BUFF_ONADD +#undef BUFF_ONREM +} + +MUTATOR_HOOKFUNCTION(buffs, SpectateCopy) +{ + entity spectatee = M_ARGV(0, entity); + entity client = M_ARGV(1, entity); + + client.buffs = spectatee.buffs; +} + +MUTATOR_HOOKFUNCTION(buffs, VehicleEnter) +{ + entity player = M_ARGV(0, entity); + entity veh = M_ARGV(1, entity); + + veh.buffs = player.buffs; + player.buffs = 0; + veh.buff_time = max(0, player.buff_time - time); + player.buff_time = 0; +} + +MUTATOR_HOOKFUNCTION(buffs, VehicleExit) +{ + entity player = M_ARGV(0, entity); + entity veh = M_ARGV(1, entity); + + player.buffs = player.oldbuffs = veh.buffs; + veh.buffs = 0; + player.buff_time = time + veh.buff_time; + veh.buff_time = 0; +} + +MUTATOR_HOOKFUNCTION(buffs, PlayerRegen) +{ + entity player = M_ARGV(0, entity); + + if(player.buffs & BUFF_MEDIC.m_itemid) + { + M_ARGV(2, float) = autocvar_g_buffs_medic_rot; // rot_mod + M_ARGV(4, float) = M_ARGV(1, float) = autocvar_g_buffs_medic_max; // limit_mod = max_mod + M_ARGV(2, float) = autocvar_g_buffs_medic_regen; // regen_mod + } + + if(player.buffs & BUFF_SPEED.m_itemid) + M_ARGV(2, float) = autocvar_g_buffs_speed_regen; // regen_mod +} + +REPLICATE(cvar_cl_buffs_autoreplace, bool, "cl_buffs_autoreplace"); + +MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Buffs"); +} + +MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsPrettyString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Buffs"); +} + +void buffs_DelayedInit(entity this) +{ + if(autocvar_g_buffs_spawn_count > 0) + if(find(NULL, classname, "item_buff") == NULL) + { + float i; + for(i = 0; i < autocvar_g_buffs_spawn_count; ++i) + { + entity e = spawn(); + e.spawnflags |= 64; // always randomize + e.velocity = randomvec() * 250; // this gets reset anyway if random location works + buff_Init(e); + } + } +} diff --git a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qh b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qh new file mode 100644 index 0000000000..b9fc1e42d2 --- /dev/null +++ b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qh @@ -0,0 +1,77 @@ +#pragma once + +#include "buffs.qh" + +#include "../instagib/_mod.qh" + +bool autocvar_g_buffs_effects; +float autocvar_g_buffs_waypoint_distance; +bool autocvar_g_buffs_randomize; +float autocvar_g_buffs_random_lifetime; +bool autocvar_g_buffs_random_location; +int autocvar_g_buffs_random_location_attempts; +int autocvar_g_buffs_spawn_count; +bool 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_medic_heal_amount = 15; +float autocvar_g_buffs_medic_heal_delay = 1; +float autocvar_g_buffs_medic_heal_range = 400; +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_slowtime; +float autocvar_g_buffs_disability_speed; +float autocvar_g_buffs_disability_rate; +float autocvar_g_buffs_disability_weaponspeed; +float autocvar_g_buffs_speed_speed; +float autocvar_g_buffs_speed_rate; +float autocvar_g_buffs_speed_weaponspeed; +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_jump_height; +float autocvar_g_buffs_inferno_burntime_factor; +float autocvar_g_buffs_inferno_burntime_min_time; +float autocvar_g_buffs_inferno_burntime_target_damage; +float autocvar_g_buffs_inferno_burntime_target_time; +float autocvar_g_buffs_inferno_damagemultiplier; +float autocvar_g_buffs_swapper_range; +float autocvar_g_buffs_magnet_range_item; +float autocvar_g_buffs_magnet_range_buff = 200; +float autocvar_g_buffs_luck_chance = 0.15; +float autocvar_g_buffs_luck_damagemultiplier = 3; + +// ammo +.float buff_ammo_prev_infitems; +.int buff_ammo_prev_clipload; +// invisible +.float buff_invisible_prev_alpha; +// medic +.float buff_medic_healtime; +// disability +.float buff_disability_time; +.float buff_disability_effect_time; +// common buff variables +.float buff_effect_delay; + +// buff definitions +.float buff_active; +.float buff_activetime; +.float buff_activetime_updated; +.entity buff_waypoint; +.int oldbuffs; // for updating effects +.entity buff_model; // controls effects (TODO: make csqc) + +const vector BUFF_MIN = ('-16 -16 -20'); +const vector BUFF_MAX = ('16 16 20'); + +// client side options +.float cvar_cl_buffs_autoreplace; diff --git a/qcsrc/common/mutators/mutator/bugrigs/bugrigs.qc b/qcsrc/common/mutators/mutator/bugrigs/bugrigs.qc index f636696e64..a67e9455d9 100644 --- a/qcsrc/common/mutators/mutator/bugrigs/bugrigs.qc +++ b/qcsrc/common/mutators/mutator/bugrigs/bugrigs.qc @@ -1,4 +1,7 @@ -#ifdef IMPLEMENTATION +#include "bugrigs.qh" + +#ifdef GAMEQC + #ifdef SVQC #include #endif @@ -311,4 +314,5 @@ MUTATOR_HOOKFUNCTION(bugrigs, BuildMutatorsPrettyString) } #endif + #endif diff --git a/qcsrc/common/mutators/mutator/bugrigs/bugrigs.qh b/qcsrc/common/mutators/mutator/bugrigs/bugrigs.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/bugrigs/bugrigs.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/bugrigs/module.inc b/qcsrc/common/mutators/mutator/bugrigs/module.inc deleted file mode 100644 index cef744fdcb..0000000000 --- a/qcsrc/common/mutators/mutator/bugrigs/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifndef MENUQC -#include "bugrigs.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/campcheck/_mod.inc b/qcsrc/common/mutators/mutator/campcheck/_mod.inc index 3ddb376fc9..07c2ae2722 100644 --- a/qcsrc/common/mutators/mutator/campcheck/_mod.inc +++ b/qcsrc/common/mutators/mutator/campcheck/_mod.inc @@ -1,2 +1,5 @@ // generated file; do not modify #include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/campcheck/_mod.qh b/qcsrc/common/mutators/mutator/campcheck/_mod.qh index 81345f1cff..c52811129b 100644 --- a/qcsrc/common/mutators/mutator/campcheck/_mod.qh +++ b/qcsrc/common/mutators/mutator/campcheck/_mod.qh @@ -1,2 +1,5 @@ // generated file; do not modify #include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/campcheck/campcheck.qc b/qcsrc/common/mutators/mutator/campcheck/campcheck.qc index 1ab6fb4624..822d4af780 100644 --- a/qcsrc/common/mutators/mutator/campcheck/campcheck.qc +++ b/qcsrc/common/mutators/mutator/campcheck/campcheck.qc @@ -1,91 +1 @@ -#ifdef IMPLEMENTATION -float autocvar_g_campcheck_damage; -float autocvar_g_campcheck_distance; -float autocvar_g_campcheck_interval; - -REGISTER_MUTATOR(campcheck, cvar("g_campcheck")); - -.float campcheck_nextcheck; -.float campcheck_traveled_distance; - -MUTATOR_HOOKFUNCTION(campcheck, PlayerDies) -{ - entity frag_target = M_ARGV(2, entity); - - Kill_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CPID_CAMPCHECK); -} - -MUTATOR_HOOKFUNCTION(campcheck, PlayerDamage_Calculate) -{ - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - - if(IS_PLAYER(frag_target)) - if(IS_PLAYER(frag_attacker)) - if(frag_attacker != frag_target) - { - frag_target.campcheck_traveled_distance = autocvar_g_campcheck_distance; - frag_attacker.campcheck_traveled_distance = autocvar_g_campcheck_distance; - } -} - -MUTATOR_HOOKFUNCTION(campcheck, PlayerPreThink) -{ - entity player = M_ARGV(0, entity); - - if(!gameover) - if(!warmup_stage) // don't consider it camping during warmup? - if(time >= game_starttime) - if(IS_PLAYER(player)) - if(IS_REAL_CLIENT(player)) // bots may camp, but that's no reason to constantly kill them - if(!IS_DEAD(player)) - if(!STAT(FROZEN, player)) - if(!PHYS_INPUT_BUTTON_CHAT(player)) - if(autocvar_g_campcheck_interval) - { - vector dist; - - // calculate player movement (in 2 dimensions only, so jumping on one spot doesn't count as movement) - dist = player.prevorigin - player.origin; - dist.z = 0; - player.campcheck_traveled_distance += fabs(vlen(dist)); - - if((autocvar_g_campaign && !campaign_bots_may_start) || (time < game_starttime) || (round_handler_IsActive() && !round_handler_IsRoundStarted())) - { - player.campcheck_nextcheck = time + autocvar_g_campcheck_interval * 2; - player.campcheck_traveled_distance = 0; - } - - if(time > player.campcheck_nextcheck) - { - if(player.campcheck_traveled_distance < autocvar_g_campcheck_distance) - { - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CAMPCHECK); - if(player.vehicle) - Damage(player.vehicle, NULL, NULL, autocvar_g_campcheck_damage * 2, DEATH_CAMP.m_id, player.vehicle.origin, '0 0 0'); - else - Damage(player, NULL, NULL, bound(0, autocvar_g_campcheck_damage, player.health + player.armorvalue * autocvar_g_balance_armor_blockpercent + 5), DEATH_CAMP.m_id, player.origin, '0 0 0'); - } - player.campcheck_nextcheck = time + autocvar_g_campcheck_interval; - player.campcheck_traveled_distance = 0; - } - - return; - } - - player.campcheck_nextcheck = time + autocvar_g_campcheck_interval; // one of the above checks failed, so keep the timer up to date -} - -MUTATOR_HOOKFUNCTION(campcheck, PlayerSpawn) -{ - entity player = M_ARGV(0, entity); - - player.campcheck_nextcheck = time + autocvar_g_campcheck_interval * 2; - player.campcheck_traveled_distance = 0; -} - -MUTATOR_HOOKFUNCTION(campcheck, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":CampCheck"); -} -#endif +#include "campcheck.qh" diff --git a/qcsrc/common/mutators/mutator/campcheck/campcheck.qh b/qcsrc/common/mutators/mutator/campcheck/campcheck.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/campcheck/campcheck.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/campcheck/module.inc b/qcsrc/common/mutators/mutator/campcheck/module.inc deleted file mode 100644 index 9f4181f163..0000000000 --- a/qcsrc/common/mutators/mutator/campcheck/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "campcheck.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qc b/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qc new file mode 100644 index 0000000000..c4d2e03026 --- /dev/null +++ b/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qc @@ -0,0 +1,92 @@ +#include "sv_campcheck.qh" + +float autocvar_g_campcheck_damage; +float autocvar_g_campcheck_distance; +float autocvar_g_campcheck_interval; + +REGISTER_MUTATOR(campcheck, cvar("g_campcheck")); + +.float campcheck_nextcheck; +.float campcheck_traveled_distance; + +MUTATOR_HOOKFUNCTION(campcheck, PlayerDies) +{ + entity frag_target = M_ARGV(2, entity); + + Kill_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CPID_CAMPCHECK); +} + +MUTATOR_HOOKFUNCTION(campcheck, PlayerDamage_Calculate) +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + + if(IS_PLAYER(frag_target)) + if(IS_PLAYER(frag_attacker)) + if(frag_attacker != frag_target) + { + frag_target.campcheck_traveled_distance = autocvar_g_campcheck_distance; + frag_attacker.campcheck_traveled_distance = autocvar_g_campcheck_distance; + } +} + +MUTATOR_HOOKFUNCTION(campcheck, PlayerPreThink) +{ + entity player = M_ARGV(0, entity); + + if(!gameover) + if(!warmup_stage) // don't consider it camping during warmup? + if(time >= game_starttime) + if(IS_PLAYER(player)) + if(IS_REAL_CLIENT(player)) // bots may camp, but that's no reason to constantly kill them + if(!IS_DEAD(player)) + if(!forbidWeaponUse(player)) + if(!STAT(FROZEN, player)) + if(!PHYS_INPUT_BUTTON_CHAT(player)) + if(autocvar_g_campcheck_interval) + { + vector dist; + + // calculate player movement (in 2 dimensions only, so jumping on one spot doesn't count as movement) + dist = player.prevorigin - player.origin; + dist.z = 0; + player.campcheck_traveled_distance += fabs(vlen(dist)); + + if((autocvar_g_campaign && !campaign_bots_may_start) || (time < game_starttime) || (round_handler_IsActive() && !round_handler_IsRoundStarted())) + { + player.campcheck_nextcheck = time + autocvar_g_campcheck_interval * 2; + player.campcheck_traveled_distance = 0; + } + + if(time > player.campcheck_nextcheck) + { + if(player.campcheck_traveled_distance < autocvar_g_campcheck_distance) + { + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CAMPCHECK); + if(player.vehicle) + Damage(player.vehicle, NULL, NULL, autocvar_g_campcheck_damage * 2, DEATH_CAMP.m_id, player.vehicle.origin, '0 0 0'); + else + Damage(player, NULL, NULL, bound(0, autocvar_g_campcheck_damage, player.health + player.armorvalue * autocvar_g_balance_armor_blockpercent + 5), DEATH_CAMP.m_id, player.origin, '0 0 0'); + } + player.campcheck_nextcheck = time + autocvar_g_campcheck_interval; + player.campcheck_traveled_distance = 0; + } + + return; + } + + player.campcheck_nextcheck = time + autocvar_g_campcheck_interval; // one of the above checks failed, so keep the timer up to date +} + +MUTATOR_HOOKFUNCTION(campcheck, PlayerSpawn) +{ + entity player = M_ARGV(0, entity); + + player.campcheck_nextcheck = time + autocvar_g_campcheck_interval * 2; + player.campcheck_traveled_distance = 0; +} + +MUTATOR_HOOKFUNCTION(campcheck, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":CampCheck"); +} diff --git a/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qh b/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/cloaked/_mod.inc b/qcsrc/common/mutators/mutator/cloaked/_mod.inc index 7213695225..34fdea7479 100644 --- a/qcsrc/common/mutators/mutator/cloaked/_mod.inc +++ b/qcsrc/common/mutators/mutator/cloaked/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/cloaked/_mod.qh b/qcsrc/common/mutators/mutator/cloaked/_mod.qh index 5606b9aafd..b225a2be5b 100644 --- a/qcsrc/common/mutators/mutator/cloaked/_mod.qh +++ b/qcsrc/common/mutators/mutator/cloaked/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/cloaked/cloaked.qc b/qcsrc/common/mutators/mutator/cloaked/cloaked.qc deleted file mode 100644 index 4fca532768..0000000000 --- a/qcsrc/common/mutators/mutator/cloaked/cloaked.qc +++ /dev/null @@ -1,19 +0,0 @@ -#ifdef IMPLEMENTATION - -REGISTER_MUTATOR(cloaked, cvar("g_cloaked")); - -float autocvar_g_balance_cloaked_alpha; - -MUTATOR_HOOKFUNCTION(cloaked, SetDefaultAlpha) -{ - default_player_alpha = autocvar_g_balance_cloaked_alpha; - default_weapon_alpha = default_player_alpha; - return true; -} - -MUTATOR_HOOKFUNCTION(cloaked, BuildMutatorsPrettyString) -{ - if (!g_cts) M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Cloaked"); -} - -#endif diff --git a/qcsrc/common/mutators/mutator/cloaked/module.inc b/qcsrc/common/mutators/mutator/cloaked/module.inc deleted file mode 100644 index 3d3a042936..0000000000 --- a/qcsrc/common/mutators/mutator/cloaked/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "cloaked.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/cloaked/sv_cloaked.qc b/qcsrc/common/mutators/mutator/cloaked/sv_cloaked.qc new file mode 100644 index 0000000000..eb45d4baf4 --- /dev/null +++ b/qcsrc/common/mutators/mutator/cloaked/sv_cloaked.qc @@ -0,0 +1,17 @@ +#include "sv_cloaked.qh" + +REGISTER_MUTATOR(cloaked, cvar("g_cloaked")); + +float autocvar_g_balance_cloaked_alpha; + +MUTATOR_HOOKFUNCTION(cloaked, SetDefaultAlpha) +{ + default_player_alpha = autocvar_g_balance_cloaked_alpha; + default_weapon_alpha = default_player_alpha; + return true; +} + +MUTATOR_HOOKFUNCTION(cloaked, BuildMutatorsPrettyString) +{ + if (!g_cts) M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Cloaked"); +} diff --git a/qcsrc/common/mutators/mutator/cloaked/sv_cloaked.qh b/qcsrc/common/mutators/mutator/cloaked/sv_cloaked.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/cloaked/sv_cloaked.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/damagetext/damagetext.qc b/qcsrc/common/mutators/mutator/damagetext/damagetext.qc index 878a854fb7..88af5496b3 100644 --- a/qcsrc/common/mutators/mutator/damagetext/damagetext.qc +++ b/qcsrc/common/mutators/mutator/damagetext/damagetext.qc @@ -1,13 +1,5 @@ -#ifndef MUTATOR_DAMAGETEXT_H -#define MUTATOR_DAMAGETEXT_H +#include "damagetext.qh" -#ifdef MENUQC -#include -#endif - -#endif - -#ifdef IMPLEMENTATION REGISTER_MUTATOR(damagetext, true); #if defined(CSQC) || defined(MENUQC) @@ -157,8 +149,10 @@ NET_HANDLE(damagetext, bool isNew) #endif #ifdef MENUQC + +#include + CLASS(XonoticDamageTextSettings, XonoticTab) - #include REGISTER_SETTINGS(damagetext, NEW(XonoticDamageTextSettings)); ATTRIB(XonoticDamageTextSettings, title, string, _("Damage text")); ATTRIB(XonoticDamageTextSettings, intendedWidth, float, 0.9); @@ -193,7 +187,6 @@ CLASS(XonoticDamageTextSettings, XonoticTab) setDependent(e, "cl_damagetext", 1, 1); this.TR(this); this.TR(this); - // friendly fire this.TD(this, 1, 3, e = makeXonoticCheckBox(0, "cl_damagetext_friendlyfire", _("Draw damage numbers for friendly fire"))); setDependent(e, "cl_damagetext", 1, 1); this.TR(this); @@ -205,4 +198,3 @@ CLASS(XonoticDamageTextSettings, XonoticTab) } ENDCLASS(XonoticDamageTextSettings) #endif -#endif diff --git a/qcsrc/common/mutators/mutator/damagetext/damagetext.qh b/qcsrc/common/mutators/mutator/damagetext/damagetext.qh new file mode 100644 index 0000000000..7228f37ea5 --- /dev/null +++ b/qcsrc/common/mutators/mutator/damagetext/damagetext.qh @@ -0,0 +1,5 @@ +#pragma once + +#ifdef MENUQC +#include +#endif diff --git a/qcsrc/common/mutators/mutator/damagetext/module.inc b/qcsrc/common/mutators/mutator/damagetext/module.inc deleted file mode 100644 index 8e70be7c6d..0000000000 --- a/qcsrc/common/mutators/mutator/damagetext/module.inc +++ /dev/null @@ -1 +0,0 @@ -#include "damagetext.qc" diff --git a/qcsrc/common/mutators/mutator/dodging/_mod.inc b/qcsrc/common/mutators/mutator/dodging/_mod.inc index 4902d5fc1b..80a7828a70 100644 --- a/qcsrc/common/mutators/mutator/dodging/_mod.inc +++ b/qcsrc/common/mutators/mutator/dodging/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/dodging/_mod.qh b/qcsrc/common/mutators/mutator/dodging/_mod.qh index b2b65f02f7..fef7b8e6e4 100644 --- a/qcsrc/common/mutators/mutator/dodging/_mod.qh +++ b/qcsrc/common/mutators/mutator/dodging/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/dodging/dodging.qc b/qcsrc/common/mutators/mutator/dodging/dodging.qc deleted file mode 100644 index 7ddfcea2ba..0000000000 --- a/qcsrc/common/mutators/mutator/dodging/dodging.qc +++ /dev/null @@ -1,292 +0,0 @@ -#ifdef IMPLEMENTATION - -#define PHYS_DODGING STAT(DODGING, this) -#define PHYS_DODGING_DELAY STAT(DODGING_DELAY, this) -#define PHYS_DODGING_DISTANCE_THRESHOLD STAT(DODGING_DISTANCE_THRESHOLD, this) -#define PHYS_DODGING_FROZEN_NODOUBLETAP STAT(DODGING_FROZEN_NO_DOUBLETAP, this) -#define PHYS_DODGING_HEIGHT_THRESHOLD STAT(DODGING_HEIGHT_THRESHOLD, this) -#define PHYS_DODGING_HORIZ_SPEED STAT(DODGING_HORIZ_SPEED, this) -#define PHYS_DODGING_HORIZ_SPEED_FROZEN STAT(DODGING_HORIZ_SPEED_FROZEN, this) -#define PHYS_DODGING_RAMP_TIME STAT(DODGING_RAMP_TIME, this) -#define PHYS_DODGING_UP_SPEED STAT(DODGING_UP_SPEED, this) -#define PHYS_DODGING_WALL STAT(DODGING_WALL, this) -#define PHYS_DODGING_AIR STAT(DODGING_AIR, this) -#define PHYS_DODGING_PRESSED_KEYS(s) (s).pressedkeys - -#ifdef CSQC - #define PHYS_DODGING_FRAMETIME (1 / (frametime <= 0 ? 60 : frametime)) - #define PHYS_DODGING_TIMEOUT(s) STAT(DODGING_TIMEOUT) -#elif defined(SVQC) - #define PHYS_DODGING_FRAMETIME sys_frametime - #define PHYS_DODGING_TIMEOUT(s) s.cvar_cl_dodging_timeout -#endif - -#ifdef SVQC - -bool autocvar_sv_dodging_sound; - -// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done.. -.float dodging_action; - -// the jump part of the dodge cannot be ramped -.float dodging_single_action; - -#include -#include - -.float cvar_cl_dodging_timeout = _STAT(DODGING_TIMEOUT); - -REGISTER_MUTATOR(dodging, cvar("g_dodging")) -{ - // this just turns on the cvar. - MUTATOR_ONADD - { - g_dodging = cvar("g_dodging"); - } - - // this just turns off the cvar. - MUTATOR_ONROLLBACK_OR_REMOVE - { - g_dodging = 0; - } - - return false; -} - -#elif defined(CSQC) -REGISTER_MUTATOR(dodging, true); -#endif - -// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done.. -.float dodging_action; - -// the jump part of the dodge cannot be ramped -.float dodging_single_action; - - -// these are used to store the last key press time for each of the keys.. -.float last_FORWARD_KEY_time; -.float last_BACKWARD_KEY_time; -.float last_LEFT_KEY_time; -.float last_RIGHT_KEY_time; - -// these store the movement direction at the time of the dodge action happening. -.vector dodging_direction; - -// this indicates the last time a dodge was executed. used to check if another one is allowed -// and to ramp up the dodge acceleration in the physics hook. -.float last_dodging_time; - -// This is the velocity gain to be added over the ramp time. -// It will decrease from frame to frame during dodging_action = 1 -// until it's 0. -.float dodging_velocity_gain; - -#ifdef CSQC -.int pressedkeys; -#endif - -// returns 1 if the player is close to a wall -bool check_close_to_wall(entity this, float threshold) -{ - if (PHYS_DODGING_WALL == 0) { return false; } - -#define X(OFFSET) \ - tracebox(this.origin, this.mins, this.maxs, this.origin + OFFSET, true, this); \ - if(trace_fraction < 1 && vdist(this.origin - trace_endpos, <, threshold)) \ - return true; - X(1000*v_right); - X(-1000*v_right); - X(1000*v_forward); - X(-1000*v_forward); -#undef X - - return false; -} - -bool check_close_to_ground(entity this, float threshold) -{ - return IS_ONGROUND(this) ? true : false; -} - -float PM_dodging_checkpressedkeys(entity this) -{ - if(!PHYS_DODGING) - return false; - - float frozen_dodging = (PHYS_FROZEN(this) && PHYS_DODGING_FROZEN(this)); - float frozen_no_doubletap = (frozen_dodging && !PHYS_DODGING_FROZEN_NODOUBLETAP); - - // first check if the last dodge is far enough back in time so we can dodge again - if ((time - this.last_dodging_time) < PHYS_DODGING_DELAY) - return false; - - makevectors(this.angles); - - if(!PHYS_DODGING_AIR) - if (check_close_to_ground(this, PHYS_DODGING_HEIGHT_THRESHOLD) != 1 - && check_close_to_wall(this, PHYS_DODGING_DISTANCE_THRESHOLD) != 1) - return true; - - float tap_direction_x = 0; - float tap_direction_y = 0; - bool dodge_detected = false; - - #define X(COND,BTN,RESULT) \ - if (this.movement_##COND) \ - /* is this a state change? */ \ - if(!(PHYS_DODGING_PRESSED_KEYS(this) & KEY_##BTN) || frozen_no_doubletap) { \ - tap_direction_##RESULT; \ - if ((time - this.last_##BTN##_KEY_time) < PHYS_DODGING_TIMEOUT(this) || frozen_no_doubletap) \ - dodge_detected = true; \ - this.last_##BTN##_KEY_time = time; \ - } - X(x < 0, BACKWARD, x--); - X(x > 0, FORWARD, x++); - X(y < 0, LEFT, y--); - X(y > 0, RIGHT, y++); - #undef X - - if (dodge_detected) - { - this.last_dodging_time = time; - - this.dodging_action = 1; - this.dodging_single_action = 1; - - this.dodging_velocity_gain = PHYS_DODGING_HORIZ_SPEED; - - this.dodging_direction_x = tap_direction_x; - this.dodging_direction_y = tap_direction_y; - - // normalize the dodging_direction vector.. (unlike UT99) XD - float length = this.dodging_direction_x * this.dodging_direction_x - + this.dodging_direction_y * this.dodging_direction_y; - length = sqrt(length); - - this.dodging_direction_x = this.dodging_direction_x * 1.0 / length; - this.dodging_direction_y = this.dodging_direction_y * 1.0 / length; - return true; - } - return false; -} - -void PM_dodging(entity this) -{ - if (!PHYS_DODGING) - return; - - if (IS_DEAD(this)) - return; - - // when swimming, no dodging allowed.. - if (this.waterlevel >= WATERLEVEL_SWIMMING) - { - this.dodging_action = 0; - this.dodging_direction_x = 0; - this.dodging_direction_y = 0; - return; - } - - // make sure v_up, v_right and v_forward are sane - if(PHYS_DODGING_AIR) - makevectors(this.v_angle); - else - makevectors(this.angles); - - // if we have e.g. 0.5 sec ramptime and a frametime of 0.25, then the ramp code - // will be called ramp_time/frametime times = 2 times. so, we need to - // add 0.5 * the total speed each frame until the dodge action is done.. - float common_factor = PHYS_DODGING_FRAMETIME / PHYS_DODGING_RAMP_TIME; - - // if ramp time is smaller than frametime we get problems ;D - common_factor = min(common_factor, 1); - - float horiz_speed = PHYS_FROZEN(this) ? PHYS_DODGING_HORIZ_SPEED_FROZEN : PHYS_DODGING_HORIZ_SPEED; - float new_velocity_gain = this.dodging_velocity_gain - (common_factor * horiz_speed); - new_velocity_gain = max(0, new_velocity_gain); - - float velocity_difference = this.dodging_velocity_gain - new_velocity_gain; - - // ramp up dodging speed by adding some velocity each frame.. TODO: do it! :D - if (this.dodging_action == 1) - { - //disable jump key during dodge accel phase - if(this.movement_z > 0) { this.movement_z = 0; } - - this.velocity += ((this.dodging_direction_y * velocity_difference) * v_right) - + ((this.dodging_direction_x * velocity_difference) * v_forward); - - this.dodging_velocity_gain = this.dodging_velocity_gain - velocity_difference; - } - - // the up part of the dodge is a single shot action - if (this.dodging_single_action == 1) - { - UNSET_ONGROUND(this); - - this.velocity += PHYS_DODGING_UP_SPEED * v_up; - -#ifdef SVQC - if (autocvar_sv_dodging_sound) - PlayerSound(this, playersound_jump, CH_PLAYER, VOL_BASE, VOICETYPE_PLAYERSOUND); - - animdecide_setaction(this, ANIMACTION_JUMP, true); -#endif - - this.dodging_single_action = 0; - } - - // are we done with the dodging ramp yet? - if((this.dodging_action == 1) && ((time - this.last_dodging_time) > PHYS_DODGING_RAMP_TIME)) - { - // reset state so next dodge can be done correctly - this.dodging_action = 0; - this.dodging_direction_x = 0; - this.dodging_direction_y = 0; - } -} - -void PM_dodging_GetPressedKeys(entity this) -{ -#ifdef CSQC - if(!PHYS_DODGING) { return; } - - PM_dodging_checkpressedkeys(this); - - int keys = this.pressedkeys; - keys = BITSET(keys, KEY_FORWARD, this.movement.x > 0); - keys = BITSET(keys, KEY_BACKWARD, this.movement.x < 0); - keys = BITSET(keys, KEY_RIGHT, this.movement.y > 0); - keys = BITSET(keys, KEY_LEFT, this.movement.y < 0); - - keys = BITSET(keys, KEY_JUMP, PHYS_INPUT_BUTTON_JUMP(this)); - keys = BITSET(keys, KEY_CROUCH, PHYS_INPUT_BUTTON_CROUCH(this)); - keys = BITSET(keys, KEY_ATCK, PHYS_INPUT_BUTTON_ATCK(this)); - keys = BITSET(keys, KEY_ATCK2, PHYS_INPUT_BUTTON_ATCK2(this)); - this.pressedkeys = keys; -#endif -} - -MUTATOR_HOOKFUNCTION(dodging, PlayerPhysics) -{ - entity player = M_ARGV(0, entity); - - // print("dodging_PlayerPhysics\n"); - PM_dodging_GetPressedKeys(player); - PM_dodging(player); -} - -#ifdef SVQC - -REPLICATE(cvar_cl_dodging_timeout, float, "cl_dodging_timeout"); - -MUTATOR_HOOKFUNCTION(dodging, GetPressedKeys) -{ - entity player = M_ARGV(0, entity); - - PM_dodging_checkpressedkeys(player); -} - -#endif -#endif diff --git a/qcsrc/common/mutators/mutator/dodging/module.inc b/qcsrc/common/mutators/mutator/dodging/module.inc deleted file mode 100644 index 60b193fdf9..0000000000 --- a/qcsrc/common/mutators/mutator/dodging/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC - #include "dodging.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/dodging/sv_dodging.qc b/qcsrc/common/mutators/mutator/dodging/sv_dodging.qc new file mode 100644 index 0000000000..f9cee61f1e --- /dev/null +++ b/qcsrc/common/mutators/mutator/dodging/sv_dodging.qc @@ -0,0 +1,291 @@ +#include "sv_dodging.qh" + +#define PHYS_DODGING STAT(DODGING, this) +#define PHYS_DODGING_DELAY STAT(DODGING_DELAY, this) +#define PHYS_DODGING_DISTANCE_THRESHOLD STAT(DODGING_DISTANCE_THRESHOLD, this) +#define PHYS_DODGING_FROZEN_NODOUBLETAP STAT(DODGING_FROZEN_NO_DOUBLETAP, this) +#define PHYS_DODGING_HEIGHT_THRESHOLD STAT(DODGING_HEIGHT_THRESHOLD, this) +#define PHYS_DODGING_HORIZ_SPEED STAT(DODGING_HORIZ_SPEED, this) +#define PHYS_DODGING_HORIZ_SPEED_FROZEN STAT(DODGING_HORIZ_SPEED_FROZEN, this) +#define PHYS_DODGING_RAMP_TIME STAT(DODGING_RAMP_TIME, this) +#define PHYS_DODGING_UP_SPEED STAT(DODGING_UP_SPEED, this) +#define PHYS_DODGING_WALL STAT(DODGING_WALL, this) +#define PHYS_DODGING_AIR STAT(DODGING_AIR, this) +#define PHYS_DODGING_PRESSED_KEYS(s) (s).pressedkeys + +#ifdef CSQC + #define PHYS_DODGING_FRAMETIME (1 / (frametime <= 0 ? 60 : frametime)) + #define PHYS_DODGING_TIMEOUT(s) STAT(DODGING_TIMEOUT) +#elif defined(SVQC) + #define PHYS_DODGING_FRAMETIME sys_frametime + #define PHYS_DODGING_TIMEOUT(s) s.cvar_cl_dodging_timeout +#endif + +#ifdef SVQC + +bool autocvar_sv_dodging_sound; + +// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done.. +.float dodging_action; + +// the jump part of the dodge cannot be ramped +.float dodging_single_action; + +#include +#include + +.float cvar_cl_dodging_timeout = _STAT(DODGING_TIMEOUT); + +REGISTER_MUTATOR(dodging, cvar("g_dodging")) +{ + // this just turns on the cvar. + MUTATOR_ONADD + { + g_dodging = cvar("g_dodging"); + } + + // this just turns off the cvar. + MUTATOR_ONROLLBACK_OR_REMOVE + { + g_dodging = 0; + } + + return false; +} + +#elif defined(CSQC) +REGISTER_MUTATOR(dodging, true); +#endif + +// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done.. +.float dodging_action; + +// the jump part of the dodge cannot be ramped +.float dodging_single_action; + + +// these are used to store the last key press time for each of the keys.. +.float last_FORWARD_KEY_time; +.float last_BACKWARD_KEY_time; +.float last_LEFT_KEY_time; +.float last_RIGHT_KEY_time; + +// these store the movement direction at the time of the dodge action happening. +.vector dodging_direction; + +// this indicates the last time a dodge was executed. used to check if another one is allowed +// and to ramp up the dodge acceleration in the physics hook. +.float last_dodging_time; + +// This is the velocity gain to be added over the ramp time. +// It will decrease from frame to frame during dodging_action = 1 +// until it's 0. +.float dodging_velocity_gain; + +#ifdef CSQC +.int pressedkeys; +#endif + +// returns 1 if the player is close to a wall +bool check_close_to_wall(entity this, float threshold) +{ + if (PHYS_DODGING_WALL == 0) { return false; } + +#define X(OFFSET) \ + tracebox(this.origin, this.mins, this.maxs, this.origin + OFFSET, true, this); \ + if(trace_fraction < 1 && vdist(this.origin - trace_endpos, <, threshold)) \ + return true; + X(1000*v_right); + X(-1000*v_right); + X(1000*v_forward); + X(-1000*v_forward); +#undef X + + return false; +} + +bool check_close_to_ground(entity this, float threshold) +{ + return IS_ONGROUND(this) ? true : false; +} + +float PM_dodging_checkpressedkeys(entity this) +{ + if(!PHYS_DODGING) + return false; + + float frozen_dodging = (PHYS_FROZEN(this) && PHYS_DODGING_FROZEN(this)); + float frozen_no_doubletap = (frozen_dodging && !PHYS_DODGING_FROZEN_NODOUBLETAP); + + // first check if the last dodge is far enough back in time so we can dodge again + if ((time - this.last_dodging_time) < PHYS_DODGING_DELAY) + return false; + + makevectors(this.angles); + + if(!PHYS_DODGING_AIR) + if (check_close_to_ground(this, PHYS_DODGING_HEIGHT_THRESHOLD) != 1 + && check_close_to_wall(this, PHYS_DODGING_DISTANCE_THRESHOLD) != 1) + return true; + + float tap_direction_x = 0; + float tap_direction_y = 0; + bool dodge_detected = false; + + #define X(COND,BTN,RESULT) \ + if (this.movement_##COND) \ + /* is this a state change? */ \ + if(!(PHYS_DODGING_PRESSED_KEYS(this) & KEY_##BTN) || frozen_no_doubletap) { \ + tap_direction_##RESULT; \ + if ((time - this.last_##BTN##_KEY_time) < PHYS_DODGING_TIMEOUT(this) || frozen_no_doubletap) \ + dodge_detected = true; \ + this.last_##BTN##_KEY_time = time; \ + } + X(x < 0, BACKWARD, x--); + X(x > 0, FORWARD, x++); + X(y < 0, LEFT, y--); + X(y > 0, RIGHT, y++); + #undef X + + if (dodge_detected) + { + this.last_dodging_time = time; + + this.dodging_action = 1; + this.dodging_single_action = 1; + + this.dodging_velocity_gain = PHYS_DODGING_HORIZ_SPEED; + + this.dodging_direction_x = tap_direction_x; + this.dodging_direction_y = tap_direction_y; + + // normalize the dodging_direction vector.. (unlike UT99) XD + float length = this.dodging_direction_x * this.dodging_direction_x + + this.dodging_direction_y * this.dodging_direction_y; + length = sqrt(length); + + this.dodging_direction_x = this.dodging_direction_x * 1.0 / length; + this.dodging_direction_y = this.dodging_direction_y * 1.0 / length; + return true; + } + return false; +} + +void PM_dodging(entity this) +{ + if (!PHYS_DODGING) + return; + + if (IS_DEAD(this)) + return; + + // when swimming, no dodging allowed.. + if (this.waterlevel >= WATERLEVEL_SWIMMING) + { + this.dodging_action = 0; + this.dodging_direction_x = 0; + this.dodging_direction_y = 0; + return; + } + + // make sure v_up, v_right and v_forward are sane + if(PHYS_DODGING_AIR) + makevectors(this.v_angle); + else + makevectors(this.angles); + + // if we have e.g. 0.5 sec ramptime and a frametime of 0.25, then the ramp code + // will be called ramp_time/frametime times = 2 times. so, we need to + // add 0.5 * the total speed each frame until the dodge action is done.. + float common_factor = PHYS_DODGING_FRAMETIME / PHYS_DODGING_RAMP_TIME; + + // if ramp time is smaller than frametime we get problems ;D + common_factor = min(common_factor, 1); + + float horiz_speed = PHYS_FROZEN(this) ? PHYS_DODGING_HORIZ_SPEED_FROZEN : PHYS_DODGING_HORIZ_SPEED; + float new_velocity_gain = this.dodging_velocity_gain - (common_factor * horiz_speed); + new_velocity_gain = max(0, new_velocity_gain); + + float velocity_difference = this.dodging_velocity_gain - new_velocity_gain; + + // ramp up dodging speed by adding some velocity each frame.. TODO: do it! :D + if (this.dodging_action == 1) + { + //disable jump key during dodge accel phase + if(this.movement_z > 0) { this.movement_z = 0; } + + this.velocity += ((this.dodging_direction_y * velocity_difference) * v_right) + + ((this.dodging_direction_x * velocity_difference) * v_forward); + + this.dodging_velocity_gain = this.dodging_velocity_gain - velocity_difference; + } + + // the up part of the dodge is a single shot action + if (this.dodging_single_action == 1) + { + UNSET_ONGROUND(this); + + this.velocity += PHYS_DODGING_UP_SPEED * v_up; + +#ifdef SVQC + if (autocvar_sv_dodging_sound) + PlayerSound(this, playersound_jump, CH_PLAYER, VOL_BASE, VOICETYPE_PLAYERSOUND); + + animdecide_setaction(this, ANIMACTION_JUMP, true); +#endif + + this.dodging_single_action = 0; + } + + // are we done with the dodging ramp yet? + if((this.dodging_action == 1) && ((time - this.last_dodging_time) > PHYS_DODGING_RAMP_TIME)) + { + // reset state so next dodge can be done correctly + this.dodging_action = 0; + this.dodging_direction_x = 0; + this.dodging_direction_y = 0; + } +} + +void PM_dodging_GetPressedKeys(entity this) +{ +#ifdef CSQC + if(!PHYS_DODGING) { return; } + + PM_dodging_checkpressedkeys(this); + + int keys = this.pressedkeys; + keys = BITSET(keys, KEY_FORWARD, this.movement.x > 0); + keys = BITSET(keys, KEY_BACKWARD, this.movement.x < 0); + keys = BITSET(keys, KEY_RIGHT, this.movement.y > 0); + keys = BITSET(keys, KEY_LEFT, this.movement.y < 0); + + keys = BITSET(keys, KEY_JUMP, PHYS_INPUT_BUTTON_JUMP(this)); + keys = BITSET(keys, KEY_CROUCH, PHYS_INPUT_BUTTON_CROUCH(this)); + keys = BITSET(keys, KEY_ATCK, PHYS_INPUT_BUTTON_ATCK(this)); + keys = BITSET(keys, KEY_ATCK2, PHYS_INPUT_BUTTON_ATCK2(this)); + this.pressedkeys = keys; +#endif +} + +MUTATOR_HOOKFUNCTION(dodging, PlayerPhysics) +{ + entity player = M_ARGV(0, entity); + + // print("dodging_PlayerPhysics\n"); + PM_dodging_GetPressedKeys(player); + PM_dodging(player); +} + +#ifdef SVQC + +REPLICATE(cvar_cl_dodging_timeout, float, "cl_dodging_timeout"); + +MUTATOR_HOOKFUNCTION(dodging, GetPressedKeys) +{ + entity player = M_ARGV(0, entity); + + PM_dodging_checkpressedkeys(player); +} + +#endif diff --git a/qcsrc/common/mutators/mutator/dodging/sv_dodging.qh b/qcsrc/common/mutators/mutator/dodging/sv_dodging.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/dodging/sv_dodging.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/doublejump/doublejump.qc b/qcsrc/common/mutators/mutator/doublejump/doublejump.qc index d490ecaaff..cc60ccc1fd 100644 --- a/qcsrc/common/mutators/mutator/doublejump/doublejump.qc +++ b/qcsrc/common/mutators/mutator/doublejump/doublejump.qc @@ -1,4 +1,6 @@ -#ifdef IMPLEMENTATION +#include "doublejump.qh" + +#ifdef GAMEQC #ifdef SVQC #include #endif @@ -31,5 +33,4 @@ MUTATOR_HOOKFUNCTION(doublejump, PlayerJump) } } } - #endif diff --git a/qcsrc/common/mutators/mutator/doublejump/doublejump.qh b/qcsrc/common/mutators/mutator/doublejump/doublejump.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/doublejump/doublejump.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/doublejump/module.inc b/qcsrc/common/mutators/mutator/doublejump/module.inc deleted file mode 100644 index f4c695be0e..0000000000 --- a/qcsrc/common/mutators/mutator/doublejump/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifndef MENUQC -#include "doublejump.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/globalforces/_mod.inc b/qcsrc/common/mutators/mutator/globalforces/_mod.inc index 006a5d5c7e..2302d85fb1 100644 --- a/qcsrc/common/mutators/mutator/globalforces/_mod.inc +++ b/qcsrc/common/mutators/mutator/globalforces/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/globalforces/_mod.qh b/qcsrc/common/mutators/mutator/globalforces/_mod.qh index ac5cfd2442..c31ee5cfcb 100644 --- a/qcsrc/common/mutators/mutator/globalforces/_mod.qh +++ b/qcsrc/common/mutators/mutator/globalforces/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/globalforces/globalforces.qc b/qcsrc/common/mutators/mutator/globalforces/globalforces.qc deleted file mode 100644 index 49ac468ba5..0000000000 --- a/qcsrc/common/mutators/mutator/globalforces/globalforces.qc +++ /dev/null @@ -1,33 +0,0 @@ -#ifdef IMPLEMENTATION - -AUTOCVAR(g_globalforces, float, false, "Global forces: knockback affects everyone"); -AUTOCVAR(g_globalforces_noself, bool, true, "Global forces: ignore self damage"); -AUTOCVAR(g_globalforces_self, float, 1, "Global forces: knockback self scale"); -AUTOCVAR(g_globalforces_range, float, 1000, "Global forces: max range of effect"); -REGISTER_MUTATOR(mutator_globalforces, autocvar_g_globalforces); - -MUTATOR_HOOKFUNCTION(mutator_globalforces, BuildMutatorsString) { - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":GlobalForces"); -} - -MUTATOR_HOOKFUNCTION(mutator_globalforces, BuildMutatorsPrettyString) { - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Global forces"); -} - -MUTATOR_HOOKFUNCTION(mutator_globalforces, PlayerDamage_SplitHealthArmor) { - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - if (autocvar_g_globalforces_noself && frag_target == frag_attacker) return; - vector damage_force = M_ARGV(3, vector) * autocvar_g_globalforces; - FOREACH_CLIENT(IS_PLAYER(it) && it != frag_target, { - if (autocvar_g_globalforces_range) { - if (vdist(it.origin - frag_target.origin, >, autocvar_g_globalforces_range)) { - continue; - } - } - float f = (it == frag_attacker) ? autocvar_g_globalforces_self : 1; - it.velocity += damage_explosion_calcpush(f * it.damageforcescale * damage_force, it.velocity, autocvar_g_balance_damagepush_speedfactor); - }); -} - -#endif diff --git a/qcsrc/common/mutators/mutator/globalforces/module.inc b/qcsrc/common/mutators/mutator/globalforces/module.inc deleted file mode 100644 index 2a0eec8bff..0000000000 --- a/qcsrc/common/mutators/mutator/globalforces/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC - #include "globalforces.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/globalforces/sv_globalforces.qc b/qcsrc/common/mutators/mutator/globalforces/sv_globalforces.qc new file mode 100644 index 0000000000..3e9cd0d51c --- /dev/null +++ b/qcsrc/common/mutators/mutator/globalforces/sv_globalforces.qc @@ -0,0 +1,31 @@ +#include "sv_globalforces.qh" + +AUTOCVAR(g_globalforces, float, false, "Global forces: knockback affects everyone"); +AUTOCVAR(g_globalforces_noself, bool, true, "Global forces: ignore self damage"); +AUTOCVAR(g_globalforces_self, float, 1, "Global forces: knockback self scale"); +AUTOCVAR(g_globalforces_range, float, 1000, "Global forces: max range of effect"); +REGISTER_MUTATOR(mutator_globalforces, autocvar_g_globalforces); + +MUTATOR_HOOKFUNCTION(mutator_globalforces, BuildMutatorsString) { + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":GlobalForces"); +} + +MUTATOR_HOOKFUNCTION(mutator_globalforces, BuildMutatorsPrettyString) { + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Global forces"); +} + +MUTATOR_HOOKFUNCTION(mutator_globalforces, PlayerDamage_SplitHealthArmor) { + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + if (autocvar_g_globalforces_noself && frag_target == frag_attacker) return; + vector damage_force = M_ARGV(3, vector) * autocvar_g_globalforces; + FOREACH_CLIENT(IS_PLAYER(it) && it != frag_target, { + if (autocvar_g_globalforces_range) { + if (vdist(it.origin - frag_target.origin, >, autocvar_g_globalforces_range)) { + continue; + } + } + float f = (it == frag_attacker) ? autocvar_g_globalforces_self : 1; + it.velocity += damage_explosion_calcpush(f * it.damageforcescale * damage_force, it.velocity, autocvar_g_balance_damagepush_speedfactor); + }); +} diff --git a/qcsrc/common/mutators/mutator/globalforces/sv_globalforces.qh b/qcsrc/common/mutators/mutator/globalforces/sv_globalforces.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/globalforces/sv_globalforces.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/hook/_mod.inc b/qcsrc/common/mutators/mutator/hook/_mod.inc index ec6da662e7..e5e68b6106 100644 --- a/qcsrc/common/mutators/mutator/hook/_mod.inc +++ b/qcsrc/common/mutators/mutator/hook/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/hook/_mod.qh b/qcsrc/common/mutators/mutator/hook/_mod.qh index 50c0c13726..5a5d26e817 100644 --- a/qcsrc/common/mutators/mutator/hook/_mod.qh +++ b/qcsrc/common/mutators/mutator/hook/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/hook/hook.qc b/qcsrc/common/mutators/mutator/hook/hook.qc deleted file mode 100644 index e72134e0e7..0000000000 --- a/qcsrc/common/mutators/mutator/hook/hook.qc +++ /dev/null @@ -1,47 +0,0 @@ -#ifdef IMPLEMENTATION -AUTOCVAR(g_grappling_hook, bool, false, _("let players spawn with the grappling hook which allows them to pull themselves up")); -#ifdef SVQC -REGISTER_MUTATOR(hook, autocvar_g_grappling_hook) { - MUTATOR_ONADD { - g_grappling_hook = true; - WEP_HOOK.ammo_factor = 0; - } - MUTATOR_ONROLLBACK_OR_REMOVE { - g_grappling_hook = false; - WEP_HOOK.ammo_factor = 1; - } - - return false; -} - -MUTATOR_HOOKFUNCTION(hook, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":grappling_hook"); -} - -MUTATOR_HOOKFUNCTION(hook, BuildMutatorsPrettyString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Hook"); -} - -MUTATOR_HOOKFUNCTION(hook, BuildGameplayTipsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), "\n\n^3grappling hook^8 is enabled, press 'e' to use it\n"); -} - -MUTATOR_HOOKFUNCTION(hook, PlayerSpawn) -{ - entity player = M_ARGV(0, entity); - - player.offhand = OFFHAND_HOOK; -} - -MUTATOR_HOOKFUNCTION(hook, FilterItem) -{ - entity item = M_ARGV(0, entity); - - return item.weapon == WEP_HOOK.m_id; -} - -#endif -#endif diff --git a/qcsrc/common/mutators/mutator/hook/module.inc b/qcsrc/common/mutators/mutator/hook/module.inc deleted file mode 100644 index 61600c47b1..0000000000 --- a/qcsrc/common/mutators/mutator/hook/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "hook.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/hook/sv_hook.qc b/qcsrc/common/mutators/mutator/hook/sv_hook.qc new file mode 100644 index 0000000000..b5a196c511 --- /dev/null +++ b/qcsrc/common/mutators/mutator/hook/sv_hook.qc @@ -0,0 +1,47 @@ +#include "sv_hook.qh" + +AUTOCVAR(g_grappling_hook, bool, false, _("let players spawn with the grappling hook which allows them to pull themselves up")); +#ifdef SVQC +REGISTER_MUTATOR(hook, autocvar_g_grappling_hook) { + MUTATOR_ONADD { + g_grappling_hook = true; + WEP_HOOK.ammo_factor = 0; + } + MUTATOR_ONROLLBACK_OR_REMOVE { + g_grappling_hook = false; + WEP_HOOK.ammo_factor = 1; + } + + return false; +} + +MUTATOR_HOOKFUNCTION(hook, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":grappling_hook"); +} + +MUTATOR_HOOKFUNCTION(hook, BuildMutatorsPrettyString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Hook"); +} + +MUTATOR_HOOKFUNCTION(hook, BuildGameplayTipsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), "\n\n^3grappling hook^8 is enabled, press 'e' to use it\n"); +} + +MUTATOR_HOOKFUNCTION(hook, PlayerSpawn) +{ + entity player = M_ARGV(0, entity); + + player.offhand = OFFHAND_HOOK; +} + +MUTATOR_HOOKFUNCTION(hook, FilterItem) +{ + entity item = M_ARGV(0, entity); + + return item.weapon == WEP_HOOK.m_id; +} + +#endif diff --git a/qcsrc/common/mutators/mutator/hook/sv_hook.qh b/qcsrc/common/mutators/mutator/hook/sv_hook.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/hook/sv_hook.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/instagib/_mod.inc b/qcsrc/common/mutators/mutator/instagib/_mod.inc index dad005fe49..2195111f0f 100644 --- a/qcsrc/common/mutators/mutator/instagib/_mod.inc +++ b/qcsrc/common/mutators/mutator/instagib/_mod.inc @@ -1,3 +1,5 @@ // generated file; do not modify -#include #include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/instagib/_mod.qh b/qcsrc/common/mutators/mutator/instagib/_mod.qh index 2e88f427e9..7097eaf390 100644 --- a/qcsrc/common/mutators/mutator/instagib/_mod.qh +++ b/qcsrc/common/mutators/mutator/instagib/_mod.qh @@ -1,3 +1,5 @@ // generated file; do not modify -#include #include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/instagib/instagib.qc b/qcsrc/common/mutators/mutator/instagib/instagib.qc deleted file mode 100644 index 25edbb225c..0000000000 --- a/qcsrc/common/mutators/mutator/instagib/instagib.qc +++ /dev/null @@ -1,505 +0,0 @@ -#ifndef MUTATOR_INSTAGIB_H -#define MUTATOR_INSTAGIB_H - -#include "items.qc" - -#ifdef SVQC -float autocvar_g_instagib_invis_alpha; -#endif - -#endif - -#ifdef IMPLEMENTATION -#ifdef SVQC - -int autocvar_g_instagib_ammo_drop; -int autocvar_g_instagib_extralives; -float autocvar_g_instagib_speed_highspeed; - -#include - -#include - -REGISTER_MUTATOR(mutator_instagib, cvar("g_instagib") && !g_nexball); - -spawnfunc(item_minst_cells) -{ - if (!g_instagib) { delete(this); return; } - if (!this.ammo_cells) this.ammo_cells = autocvar_g_instagib_ammo_drop; - StartItem(this, ITEM_VaporizerCells); -} - -void instagib_invisibility(entity this) -{ - this.strength_finished = autocvar_g_balance_powerup_strength_time; - StartItem(this, ITEM_Invisibility); -} - -void instagib_extralife(entity this) -{ - StartItem(this, ITEM_ExtraLife); -} - -void instagib_speed(entity this) -{ - this.invincible_finished = autocvar_g_balance_powerup_invincible_time; - StartItem(this, ITEM_Speed); -} - -.float instagib_nextthink; -.float instagib_needammo; -void instagib_stop_countdown(entity e) -{ - if (!e.instagib_needammo) - return; - Kill_Notification(NOTIF_ONE_ONLY, e, MSG_CENTER, CPID_INSTAGIB_FINDAMMO); - e.instagib_needammo = false; -} -void instagib_ammocheck(entity this) -{ - if(time < this.instagib_nextthink) - return; - if(!IS_PLAYER(this)) - return; // not a player - - if(IS_DEAD(this) || gameover) - instagib_stop_countdown(this); - else if (this.ammo_cells > 0 || (this.items & IT_UNLIMITED_WEAPON_AMMO) || (this.flags & FL_GODMODE)) - instagib_stop_countdown(this); - else if(autocvar_g_rm && autocvar_g_rm_laser) - { - if(!this.instagib_needammo) - { - Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_INSTAGIB_DOWNGRADE); - this.instagib_needammo = true; - } - } - else - { - this.instagib_needammo = true; - if (this.health <= 5) - { - Damage(this, this, this, 5, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); - Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_INSTAGIB_TERMINATED); - } - else if (this.health <= 10) - { - Damage(this, this, this, 5, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); - Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_1); - } - else if (this.health <= 20) - { - Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); - Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_2); - } - else if (this.health <= 30) - { - Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); - Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_3); - } - else if (this.health <= 40) - { - Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); - Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_4); - } - else if (this.health <= 50) - { - Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); - Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_5); - } - else if (this.health <= 60) - { - Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); - Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_6); - } - else if (this.health <= 70) - { - Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); - Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_7); - } - else if (this.health <= 80) - { - Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); - Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_8); - } - else if (this.health <= 90) - { - Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_INSTAGIB_FINDAMMO); - Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); - Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_9); - } - else - { - Send_Notification(NOTIF_ONE_ONLY, this, MSG_MULTI, MULTI_INSTAGIB_FINDAMMO); - Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); - } - } - this.instagib_nextthink = time + 1; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, MatchEnd) -{ - FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(instagib_stop_countdown(it))); -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, MonsterDropItem) -{ - entity item = M_ARGV(1, entity); - - item.monster_loot = spawnfunc_item_minst_cells; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, MonsterSpawn) -{ - entity mon = M_ARGV(0, entity); - - // always refill ammo - if(mon.monsterid == MON_MAGE.monsterid) - mon.skin = 1; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, BotShouldAttack) -{ - entity targ = M_ARGV(1, entity); - - if (targ.items & ITEM_Invisibility.m_itemid) - return true; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, MakePlayerObserver) -{ - entity player = M_ARGV(0, entity); - - instagib_stop_countdown(player); -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerSpawn) -{ - entity player = M_ARGV(0, entity); - - player.effects |= EF_FULLBRIGHT; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerPreThink) -{ - entity player = M_ARGV(0, entity); - - instagib_ammocheck(player); -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerRegen) -{ - // no regeneration in instagib - return true; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerPowerups) -{ - entity player = M_ARGV(0, entity); - - if (!(player.effects & EF_FULLBRIGHT)) - player.effects |= EF_FULLBRIGHT; - - if (player.items & ITEM_Invisibility.m_itemid) - { - play_countdown(player, player.strength_finished, SND_POWEROFF); - if (time > player.strength_finished) - { - player.alpha = default_player_alpha; - player.exteriorweaponentity.alpha = default_weapon_alpha; - player.items &= ~ITEM_Invisibility.m_itemid; - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERDOWN_INVISIBILITY); - } - } - else - { - if (time < player.strength_finished) - { - player.alpha = autocvar_g_instagib_invis_alpha; - player.exteriorweaponentity.alpha = autocvar_g_instagib_invis_alpha; - player.items |= ITEM_Invisibility.m_itemid; - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_INVISIBILITY, player.netname); - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERUP_INVISIBILITY); - } - } - - if (player.items & ITEM_Speed.m_itemid) - { - play_countdown(player, player.invincible_finished, SND_POWEROFF); - if (time > player.invincible_finished) - { - player.items &= ~ITEM_Speed.m_itemid; - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERDOWN_SPEED); - } - } - else - { - if (time < player.invincible_finished) - { - player.items |= ITEM_Speed.m_itemid; - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_SPEED, player.netname); - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERUP_SPEED); - } - } -} - -.float stat_sv_maxspeed; - -MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerPhysics) -{ - entity player = M_ARGV(0, entity); - - if(player.items & ITEM_Speed.m_itemid) - player.stat_sv_maxspeed = player.stat_sv_maxspeed * autocvar_g_instagib_speed_highspeed; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDamage_SplitHealthArmor) -{ - M_ARGV(4, float) = M_ARGV(7, float); // take = damage - M_ARGV(5, float) = 0; // save -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, ForbidThrowCurrentWeapon) -{ - // weapon dropping on death handled by FilterItem - return true; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDamage_Calculate) -{ - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - float frag_deathtype = M_ARGV(3, float); - float frag_damage = M_ARGV(4, float); - float frag_mirrordamage = M_ARGV(5, float); - vector frag_force = M_ARGV(6, vector); - - if(autocvar_g_friendlyfire == 0 && SAME_TEAM(frag_target, frag_attacker) && IS_PLAYER(frag_target) && IS_PLAYER(frag_attacker)) - frag_damage = 0; - - if(IS_PLAYER(frag_target)) - { - if(frag_deathtype == DEATH_FALL.m_id) - frag_damage = 0; // never count fall damage - - if(!autocvar_g_instagib_damagedbycontents) - switch(DEATH_ENT(frag_deathtype)) - { - case DEATH_DROWN: - case DEATH_SLIME: - case DEATH_LAVA: - frag_damage = 0; - break; - } - - if(IS_PLAYER(frag_attacker)) - if(DEATH_ISWEAPON(frag_deathtype, WEP_VAPORIZER)) - { - if(!autocvar_g_instagib_friendlypush && SAME_TEAM(frag_target, frag_attacker)) - frag_force = '0 0 0'; - - if(frag_target.armorvalue) - { - frag_target.armorvalue -= 1; - frag_damage = 0; - frag_target.damage_dealt += 1; - frag_attacker.damage_dealt += 1; - Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, frag_target.armorvalue); - } - } - - if(IS_PLAYER(frag_attacker) && DEATH_ISWEAPON(frag_deathtype, WEP_BLASTER)) - { - if(frag_deathtype & HITTYPE_SECONDARY) - { - if(!autocvar_g_instagib_blaster_keepdamage || frag_attacker == frag_target) - { - frag_damage = 0; - if(!autocvar_g_instagib_mirrordamage) - frag_mirrordamage = 0; // never do mirror damage on enemies - } - - if(frag_target != frag_attacker) - { - if(frag_damage <= 0 && frag_target.health > 0) { Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_SECONDARY_NODAMAGE); } - if(!autocvar_g_instagib_blaster_keepforce) - frag_force = '0 0 0'; - } - } - } - } - - if(!autocvar_g_instagib_mirrordamage) // only apply the taking lives hack if we don't want to support real damage mirroring - if(IS_PLAYER(frag_attacker)) - if(frag_mirrordamage > 0) - { - // just lose extra LIVES, don't kill the player for mirror damage - if(frag_attacker.armorvalue > 0) - { - frag_attacker.armorvalue -= 1; - Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, frag_attacker.armorvalue); - frag_attacker.damage_dealt += frag_mirrordamage; - } - frag_mirrordamage = 0; - } - - if(frag_target.alpha && frag_target.alpha < 1) - if(IS_PLAYER(frag_target)) - yoda = 1; - - M_ARGV(4, float) = frag_damage; - M_ARGV(5, float) = frag_mirrordamage; - M_ARGV(6, vector) = frag_force; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, SetStartItems) -{ - start_health = warmup_start_health = 100; - start_armorvalue = warmup_start_armorvalue = 0; - - start_ammo_shells = warmup_start_ammo_shells = 0; - start_ammo_nails = warmup_start_ammo_nails = 0; - start_ammo_cells = warmup_start_ammo_cells = cvar("g_instagib_ammo_start"); - start_ammo_plasma = warmup_start_ammo_plasma = 0; - start_ammo_rockets = warmup_start_ammo_rockets = 0; - start_ammo_fuel = warmup_start_ammo_fuel = 0; - - start_weapons = warmup_start_weapons = WEPSET(VAPORIZER); - start_items |= IT_UNLIMITED_SUPERWEAPONS; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, FilterItem) -{ - entity item = M_ARGV(0, entity); - - if(item.classname == "item_cells") - return true; // no normal cells? - - if(item.weapon == WEP_VAPORIZER.m_id && item.classname == "droppedweapon") - { - item.ammo_cells = autocvar_g_instagib_ammo_drop; - return false; - } - - if(item.weapon == WEP_DEVASTATOR.m_id || item.weapon == WEP_VORTEX.m_id) - { - entity e = spawn(); - setorigin(e, item.origin); - e.noalign = item.noalign; - e.cnt = item.cnt; - e.team = item.team; - e.spawnfunc_checked = true; - spawnfunc_item_minst_cells(e); - return true; - } - - if(item.flags & FL_POWERUP) - return false; - - if(item.ammo_cells > autocvar_g_instagib_ammo_drop && item.classname != "item_minst_cells") - item.ammo_cells = autocvar_g_instagib_ammo_drop; - - if(item.ammo_cells && !item.weapon) - return false; - - return true; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, CustomizeWaypoint) -{ - entity wp = M_ARGV(0, entity); - entity player = M_ARGV(1, entity); - - entity e = WaypointSprite_getviewentity(player); - - // if you have the invisibility powerup, sprites ALWAYS are restricted to your team - // but only apply this to real players, not to spectators - if((wp.owner.flags & FL_CLIENT) && (wp.owner.items & ITEM_Invisibility.m_itemid) && (e == player)) - if(DIFF_TEAM(wp.owner, e)) - return true; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDies) -{ - float frag_deathtype = M_ARGV(3, float); - - if(DEATH_ISWEAPON(frag_deathtype, WEP_VAPORIZER)) - M_ARGV(4, float) = 1000; // always gib if it was a vaporizer death -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, ItemTouch) -{ - entity item = M_ARGV(0, entity); - entity toucher = M_ARGV(1, entity); - - if(item.ammo_cells) - { - // play some cool sounds ;) - if (IS_CLIENT(toucher)) - { - if(toucher.health <= 5) - Send_Notification(NOTIF_ONE, toucher, MSG_ANNCE, ANNCE_INSTAGIB_LASTSECOND); - else if(toucher.health < 50) - Send_Notification(NOTIF_ONE, toucher, MSG_ANNCE, ANNCE_INSTAGIB_NARROWLY); - } - - if(toucher.health < 100) - toucher.health = 100; - - return MUT_ITEMTOUCH_CONTINUE; - } - - if(item.itemdef == ITEM_ExtraLife) - { - toucher.armorvalue = bound(toucher.armorvalue, 999, toucher.armorvalue + autocvar_g_instagib_extralives); - Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_EXTRALIVES); - return MUT_ITEMTOUCH_PICKUP; - } - - return MUT_ITEMTOUCH_CONTINUE; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, OnEntityPreSpawn) -{ - if (!autocvar_g_powerups) { return; } - entity ent = M_ARGV(0, entity); - // Can't use .itemdef here - if (!(ent.classname == "item_strength" || ent.classname == "item_invincible" || ent.classname == "item_health_mega")) - return; - - entity e = spawn(); - - float r = random(); - if (r < 0.3) - setthink(e, instagib_invisibility); - else if (r < 0.6) - setthink(e, instagib_extralife); - else - setthink(e, instagib_speed); - - e.nextthink = time + 0.1; - e.spawnflags = ent.spawnflags; - e.noalign = ent.noalign; - setorigin(e, ent.origin); - - return true; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":instagib"); -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, BuildMutatorsPrettyString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", instagib"); -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, SetModname) -{ - M_ARGV(0, string) = "InstaGib"; - return true; -} - -#endif -#endif diff --git a/qcsrc/common/mutators/mutator/instagib/items.qc b/qcsrc/common/mutators/mutator/instagib/items.qc index 269ec2d245..b0205a5f7b 100644 --- a/qcsrc/common/mutators/mutator/instagib/items.qc +++ b/qcsrc/common/mutators/mutator/instagib/items.qc @@ -1,84 +1 @@ -#pragma once - -#include -#include -#include - -float instagib_respawntime_ammo = 45; -float instagib_respawntimejitter_ammo = 0; -GETTER(float, instagib_respawntime_ammo) -GETTER(float, instagib_respawntimejitter_ammo) - -#ifndef MENUQC -MODEL(VaporizerCells_ITEM, Item_Model("a_cells.md3")); -SOUND(VaporizerCells, "misc/itempickup"); -#endif - -REGISTER_ITEM(VaporizerCells, Ammo) { -#ifndef MENUQC - this.m_model = MDL_VaporizerCells_ITEM; - this.m_sound = SND_VaporizerCells; -#endif - this.m_name = "Vaporizer Ammo"; - this.m_icon = "ammo_supercells"; -#ifdef SVQC - this.m_botvalue = 100; - this.m_itemid = IT_CELLS; - this.m_respawntime = GET(instagib_respawntime_ammo); - this.m_respawntimejitter = GET(instagib_respawntimejitter_ammo); -#endif -} - -#ifndef MENUQC -MODEL(ExtraLife_ITEM, Item_Model("g_h100.md3")); -SOUND(ExtraLife, "misc/megahealth"); -#endif - -REGISTER_ITEM(ExtraLife, Powerup) { -#ifndef MENUQC - this.m_model = MDL_ExtraLife_ITEM; - this.m_sound = SND_ExtraLife; -#endif - this.m_name = "Extra life"; - this.m_icon = "item_mega_health"; - this.m_color = '1 0 0'; - this.m_waypoint = _("Extra life"); - this.m_waypointblink = 2; - this.m_itemid = IT_NAILS; -} - -#ifndef MENUQC -MODEL(Invisibility_ITEM, Item_Model("g_strength.md3")); -SOUND(Invisibility, "misc/powerup"); -#endif - -REGISTER_ITEM(Invisibility, Powerup) { -#ifndef MENUQC - this.m_model = MDL_Invisibility_ITEM; - this.m_sound = SND_Invisibility; -#endif - this.m_name = "Invisibility"; - this.m_icon = "strength"; - this.m_color = '0 0 1'; - this.m_waypoint = _("Invisibility"); - this.m_waypointblink = 2; - this.m_itemid = IT_STRENGTH; -} - -#ifndef MENUQC -MODEL(Speed_ITEM, Item_Model("g_invincible.md3")); -SOUND(Speed, "misc/powerup_shield"); -#endif - -REGISTER_ITEM(Speed, Powerup) { -#ifndef MENUQC - this.m_model = MDL_Speed_ITEM; - this.m_sound = SND_Speed; -#endif - this.m_name = "Speed"; - this.m_icon = "shield"; - this.m_color = '1 0 1'; - this.m_waypoint = _("Speed"); - this.m_waypointblink = 2; - this.m_itemid = IT_INVINCIBLE; -} +#include "items.qh" diff --git a/qcsrc/common/mutators/mutator/instagib/items.qh b/qcsrc/common/mutators/mutator/instagib/items.qh new file mode 100644 index 0000000000..7736fa7b30 --- /dev/null +++ b/qcsrc/common/mutators/mutator/instagib/items.qh @@ -0,0 +1,84 @@ +#pragma once + +#include +#include +#include + +float instagib_respawntime_ammo = 45; +float instagib_respawntimejitter_ammo = 0; +GETTER(float, instagib_respawntime_ammo) +GETTER(float, instagib_respawntimejitter_ammo) + +#ifdef GAMEQC +MODEL(VaporizerCells_ITEM, Item_Model("a_cells.md3")); +SOUND(VaporizerCells, "misc/itempickup"); +#endif + +REGISTER_ITEM(VaporizerCells, Ammo) { +#ifdef GAMEQC + this.m_model = MDL_VaporizerCells_ITEM; + this.m_sound = SND_VaporizerCells; +#endif + this.m_name = "Vaporizer Ammo"; + this.m_icon = "ammo_supercells"; +#ifdef SVQC + this.m_botvalue = 100; + this.m_itemid = IT_CELLS; + this.m_respawntime = GET(instagib_respawntime_ammo); + this.m_respawntimejitter = GET(instagib_respawntimejitter_ammo); +#endif +} + +#ifdef GAMEQC +MODEL(ExtraLife_ITEM, Item_Model("g_h100.md3")); +SOUND(ExtraLife, "misc/megahealth"); +#endif + +REGISTER_ITEM(ExtraLife, Powerup) { +#ifdef GAMEQC + this.m_model = MDL_ExtraLife_ITEM; + this.m_sound = SND_ExtraLife; +#endif + this.m_name = "Extra life"; + this.m_icon = "item_mega_health"; + this.m_color = '1 0 0'; + this.m_waypoint = _("Extra life"); + this.m_waypointblink = 2; + this.m_itemid = IT_NAILS; +} + +#ifdef GAMEQC +MODEL(Invisibility_ITEM, Item_Model("g_strength.md3")); +SOUND(Invisibility, "misc/powerup"); +#endif + +REGISTER_ITEM(Invisibility, Powerup) { +#ifdef GAMEQC + this.m_model = MDL_Invisibility_ITEM; + this.m_sound = SND_Invisibility; +#endif + this.m_name = "Invisibility"; + this.m_icon = "strength"; + this.m_color = '0 0 1'; + this.m_waypoint = _("Invisibility"); + this.m_waypointblink = 2; + this.m_itemid = IT_STRENGTH; +} + +#ifdef GAMEQC +MODEL(Speed_ITEM, Item_Model("g_invincible.md3")); +SOUND(Speed, "misc/powerup_shield"); +#endif + +REGISTER_ITEM(Speed, Powerup) { +#ifdef GAMEQC + this.m_model = MDL_Speed_ITEM; + this.m_sound = SND_Speed; +#endif + this.m_name = "Speed"; + this.m_icon = "shield"; + this.m_color = '1 0 1'; + this.m_waypoint = _("Speed"); + this.m_waypointblink = 2; + this.m_itemid = IT_INVINCIBLE; +} diff --git a/qcsrc/common/mutators/mutator/instagib/module.inc b/qcsrc/common/mutators/mutator/instagib/module.inc deleted file mode 100644 index 7270067d35..0000000000 --- a/qcsrc/common/mutators/mutator/instagib/module.inc +++ /dev/null @@ -1 +0,0 @@ -#include "instagib.qc" diff --git a/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc new file mode 100644 index 0000000000..0cf3ed9781 --- /dev/null +++ b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc @@ -0,0 +1,490 @@ +#include "sv_instagib.qh" + +int autocvar_g_instagib_ammo_drop; +int autocvar_g_instagib_extralives; +float autocvar_g_instagib_speed_highspeed; + +#include + +#include + +REGISTER_MUTATOR(mutator_instagib, cvar("g_instagib") && !g_nexball); + +spawnfunc(item_minst_cells) +{ + if (!g_instagib) { delete(this); return; } + if (!this.ammo_cells) this.ammo_cells = autocvar_g_instagib_ammo_drop; + StartItem(this, ITEM_VaporizerCells); +} + +void instagib_invisibility(entity this) +{ + this.strength_finished = autocvar_g_balance_powerup_strength_time; + StartItem(this, ITEM_Invisibility); +} + +void instagib_extralife(entity this) +{ + StartItem(this, ITEM_ExtraLife); +} + +void instagib_speed(entity this) +{ + this.invincible_finished = autocvar_g_balance_powerup_invincible_time; + StartItem(this, ITEM_Speed); +} + +.float instagib_nextthink; +.float instagib_needammo; +void instagib_stop_countdown(entity e) +{ + if (!e.instagib_needammo) + return; + Kill_Notification(NOTIF_ONE_ONLY, e, MSG_CENTER, CPID_INSTAGIB_FINDAMMO); + e.instagib_needammo = false; +} +void instagib_ammocheck(entity this) +{ + if(time < this.instagib_nextthink) + return; + if(!IS_PLAYER(this)) + return; // not a player + + if(IS_DEAD(this) || gameover) + instagib_stop_countdown(this); + else if (this.ammo_cells > 0 || (this.items & IT_UNLIMITED_WEAPON_AMMO) || (this.flags & FL_GODMODE)) + instagib_stop_countdown(this); + else if(autocvar_g_rm && autocvar_g_rm_laser) + { + if(!this.instagib_needammo) + { + Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_INSTAGIB_DOWNGRADE); + this.instagib_needammo = true; + } + } + else + { + this.instagib_needammo = true; + if (this.health <= 5) + { + Damage(this, this, this, 5, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); + Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_INSTAGIB_TERMINATED); + } + else if (this.health <= 10) + { + Damage(this, this, this, 5, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); + Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_1); + } + else if (this.health <= 20) + { + Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); + Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_2); + } + else if (this.health <= 30) + { + Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); + Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_3); + } + else if (this.health <= 40) + { + Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); + Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_4); + } + else if (this.health <= 50) + { + Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); + Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_5); + } + else if (this.health <= 60) + { + Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); + Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_6); + } + else if (this.health <= 70) + { + Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); + Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_7); + } + else if (this.health <= 80) + { + Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); + Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_8); + } + else if (this.health <= 90) + { + Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_INSTAGIB_FINDAMMO); + Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); + Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_9); + } + else + { + Send_Notification(NOTIF_ONE_ONLY, this, MSG_MULTI, MULTI_INSTAGIB_FINDAMMO); + Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); + } + } + this.instagib_nextthink = time + 1; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, MatchEnd) +{ + FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(instagib_stop_countdown(it))); +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, MonsterDropItem) +{ + entity item = M_ARGV(1, entity); + + item.monster_loot = spawnfunc_item_minst_cells; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, MonsterSpawn) +{ + entity mon = M_ARGV(0, entity); + + // always refill ammo + if(mon.monsterid == MON_MAGE.monsterid) + mon.skin = 1; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, BotShouldAttack) +{ + entity targ = M_ARGV(1, entity); + + if (targ.items & ITEM_Invisibility.m_itemid) + return true; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, MakePlayerObserver) +{ + entity player = M_ARGV(0, entity); + + instagib_stop_countdown(player); +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerSpawn) +{ + entity player = M_ARGV(0, entity); + + player.effects |= EF_FULLBRIGHT; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerPreThink) +{ + entity player = M_ARGV(0, entity); + + instagib_ammocheck(player); +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerRegen) +{ + // no regeneration in instagib + return true; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerPowerups) +{ + entity player = M_ARGV(0, entity); + + if (!(player.effects & EF_FULLBRIGHT)) + player.effects |= EF_FULLBRIGHT; + + if (player.items & ITEM_Invisibility.m_itemid) + { + play_countdown(player, player.strength_finished, SND_POWEROFF); + if (time > player.strength_finished) + { + player.alpha = default_player_alpha; + player.exteriorweaponentity.alpha = default_weapon_alpha; + player.items &= ~ITEM_Invisibility.m_itemid; + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERDOWN_INVISIBILITY); + } + } + else + { + if (time < player.strength_finished) + { + player.alpha = autocvar_g_instagib_invis_alpha; + player.exteriorweaponentity.alpha = autocvar_g_instagib_invis_alpha; + player.items |= ITEM_Invisibility.m_itemid; + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_INVISIBILITY, player.netname); + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERUP_INVISIBILITY); + } + } + + if (player.items & ITEM_Speed.m_itemid) + { + play_countdown(player, player.invincible_finished, SND_POWEROFF); + if (time > player.invincible_finished) + { + player.items &= ~ITEM_Speed.m_itemid; + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERDOWN_SPEED); + } + } + else + { + if (time < player.invincible_finished) + { + player.items |= ITEM_Speed.m_itemid; + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_SPEED, player.netname); + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERUP_SPEED); + } + } +} + +.float stat_sv_maxspeed; + +MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerPhysics) +{ + entity player = M_ARGV(0, entity); + + if(player.items & ITEM_Speed.m_itemid) + player.stat_sv_maxspeed = player.stat_sv_maxspeed * autocvar_g_instagib_speed_highspeed; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDamage_SplitHealthArmor) +{ + M_ARGV(4, float) = M_ARGV(7, float); // take = damage + M_ARGV(5, float) = 0; // save +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, ForbidThrowCurrentWeapon) +{ + // weapon dropping on death handled by FilterItem + return true; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDamage_Calculate) +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + float frag_deathtype = M_ARGV(3, float); + float frag_damage = M_ARGV(4, float); + float frag_mirrordamage = M_ARGV(5, float); + vector frag_force = M_ARGV(6, vector); + + if(autocvar_g_friendlyfire == 0 && SAME_TEAM(frag_target, frag_attacker) && IS_PLAYER(frag_target) && IS_PLAYER(frag_attacker)) + frag_damage = 0; + + if(IS_PLAYER(frag_target)) + { + if(frag_deathtype == DEATH_FALL.m_id) + frag_damage = 0; // never count fall damage + + if(!autocvar_g_instagib_damagedbycontents) + switch(DEATH_ENT(frag_deathtype)) + { + case DEATH_DROWN: + case DEATH_SLIME: + case DEATH_LAVA: + frag_damage = 0; + break; + } + + if(IS_PLAYER(frag_attacker)) + if(DEATH_ISWEAPON(frag_deathtype, WEP_VAPORIZER)) + { + if(!autocvar_g_instagib_friendlypush && SAME_TEAM(frag_target, frag_attacker)) + frag_force = '0 0 0'; + + if(frag_target.armorvalue) + { + frag_target.armorvalue -= 1; + frag_damage = 0; + frag_target.damage_dealt += 1; + frag_attacker.damage_dealt += 1; + Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, frag_target.armorvalue); + } + } + + if(IS_PLAYER(frag_attacker) && DEATH_ISWEAPON(frag_deathtype, WEP_BLASTER)) + { + if(frag_deathtype & HITTYPE_SECONDARY) + { + if(!autocvar_g_instagib_blaster_keepdamage || frag_attacker == frag_target) + { + frag_damage = 0; + if(!autocvar_g_instagib_mirrordamage) + frag_mirrordamage = 0; // never do mirror damage on enemies + } + + if(frag_target != frag_attacker) + { + if(frag_damage <= 0 && frag_target.health > 0) { Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_SECONDARY_NODAMAGE); } + if(!autocvar_g_instagib_blaster_keepforce) + frag_force = '0 0 0'; + } + } + } + } + + if(!autocvar_g_instagib_mirrordamage) // only apply the taking lives hack if we don't want to support real damage mirroring + if(IS_PLAYER(frag_attacker)) + if(frag_mirrordamage > 0) + { + // just lose extra LIVES, don't kill the player for mirror damage + if(frag_attacker.armorvalue > 0) + { + frag_attacker.armorvalue -= 1; + Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, frag_attacker.armorvalue); + frag_attacker.damage_dealt += frag_mirrordamage; + } + frag_mirrordamage = 0; + } + + if(frag_target.alpha && frag_target.alpha < 1) + if(IS_PLAYER(frag_target)) + yoda = 1; + + M_ARGV(4, float) = frag_damage; + M_ARGV(5, float) = frag_mirrordamage; + M_ARGV(6, vector) = frag_force; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, SetStartItems) +{ + start_health = warmup_start_health = 100; + start_armorvalue = warmup_start_armorvalue = 0; + + start_ammo_shells = warmup_start_ammo_shells = 0; + start_ammo_nails = warmup_start_ammo_nails = 0; + start_ammo_cells = warmup_start_ammo_cells = cvar("g_instagib_ammo_start"); + start_ammo_plasma = warmup_start_ammo_plasma = 0; + start_ammo_rockets = warmup_start_ammo_rockets = 0; + start_ammo_fuel = warmup_start_ammo_fuel = 0; + + start_weapons = warmup_start_weapons = WEPSET(VAPORIZER); + start_items |= IT_UNLIMITED_SUPERWEAPONS; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, FilterItem) +{ + entity item = M_ARGV(0, entity); + + if(item.classname == "item_cells") + return true; // no normal cells? + + if(item.weapon == WEP_VAPORIZER.m_id && item.classname == "droppedweapon") + { + item.ammo_cells = autocvar_g_instagib_ammo_drop; + return false; + } + + if(item.weapon == WEP_DEVASTATOR.m_id || item.weapon == WEP_VORTEX.m_id) + { + entity e = spawn(); + setorigin(e, item.origin); + e.noalign = item.noalign; + e.cnt = item.cnt; + e.team = item.team; + e.spawnfunc_checked = true; + spawnfunc_item_minst_cells(e); + return true; + } + + if(item.flags & FL_POWERUP) + return false; + + if(item.ammo_cells > autocvar_g_instagib_ammo_drop && item.classname != "item_minst_cells") + item.ammo_cells = autocvar_g_instagib_ammo_drop; + + if(item.ammo_cells && !item.weapon) + return false; + + return true; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, CustomizeWaypoint) +{ + entity wp = M_ARGV(0, entity); + entity player = M_ARGV(1, entity); + + entity e = WaypointSprite_getviewentity(player); + + // if you have the invisibility powerup, sprites ALWAYS are restricted to your team + // but only apply this to real players, not to spectators + if((wp.owner.flags & FL_CLIENT) && (wp.owner.items & ITEM_Invisibility.m_itemid) && (e == player)) + if(DIFF_TEAM(wp.owner, e)) + return true; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDies) +{ + float frag_deathtype = M_ARGV(3, float); + + if(DEATH_ISWEAPON(frag_deathtype, WEP_VAPORIZER)) + M_ARGV(4, float) = 1000; // always gib if it was a vaporizer death +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, ItemTouch) +{ + entity item = M_ARGV(0, entity); + entity toucher = M_ARGV(1, entity); + + if(item.ammo_cells) + { + // play some cool sounds ;) + if (IS_CLIENT(toucher)) + { + if(toucher.health <= 5) + Send_Notification(NOTIF_ONE, toucher, MSG_ANNCE, ANNCE_INSTAGIB_LASTSECOND); + else if(toucher.health < 50) + Send_Notification(NOTIF_ONE, toucher, MSG_ANNCE, ANNCE_INSTAGIB_NARROWLY); + } + + if(toucher.health < 100) + toucher.health = 100; + + return MUT_ITEMTOUCH_CONTINUE; + } + + if(item.itemdef == ITEM_ExtraLife) + { + toucher.armorvalue = bound(toucher.armorvalue, 999, toucher.armorvalue + autocvar_g_instagib_extralives); + Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_EXTRALIVES); + return MUT_ITEMTOUCH_PICKUP; + } + + return MUT_ITEMTOUCH_CONTINUE; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, OnEntityPreSpawn) +{ + if (!autocvar_g_powerups) { return; } + entity ent = M_ARGV(0, entity); + // Can't use .itemdef here + if (!(ent.classname == "item_strength" || ent.classname == "item_invincible" || ent.classname == "item_health_mega")) + return; + + entity e = spawn(); + + float r = random(); + if (r < 0.3) + setthink(e, instagib_invisibility); + else if (r < 0.6) + setthink(e, instagib_extralife); + else + setthink(e, instagib_speed); + + e.nextthink = time + 0.1; + e.spawnflags = ent.spawnflags; + e.noalign = ent.noalign; + setorigin(e, ent.origin); + + return true; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":instagib"); +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, BuildMutatorsPrettyString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", instagib"); +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, SetModname) +{ + M_ARGV(0, string) = "InstaGib"; + return true; +} diff --git a/qcsrc/common/mutators/mutator/instagib/sv_instagib.qh b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qh new file mode 100644 index 0000000000..4c6d20b129 --- /dev/null +++ b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qh @@ -0,0 +1,5 @@ +#pragma once + +#include "items.qh" + +float autocvar_g_instagib_invis_alpha; diff --git a/qcsrc/common/mutators/mutator/invincibleproj/_mod.inc b/qcsrc/common/mutators/mutator/invincibleproj/_mod.inc index 68d313e95c..eb8c95fcb8 100644 --- a/qcsrc/common/mutators/mutator/invincibleproj/_mod.inc +++ b/qcsrc/common/mutators/mutator/invincibleproj/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/invincibleproj/_mod.qh b/qcsrc/common/mutators/mutator/invincibleproj/_mod.qh index dc3f32f10e..2d59a0891a 100644 --- a/qcsrc/common/mutators/mutator/invincibleproj/_mod.qh +++ b/qcsrc/common/mutators/mutator/invincibleproj/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/invincibleproj/invincibleproj.qc b/qcsrc/common/mutators/mutator/invincibleproj/invincibleproj.qc deleted file mode 100644 index 5bdafa1f12..0000000000 --- a/qcsrc/common/mutators/mutator/invincibleproj/invincibleproj.qc +++ /dev/null @@ -1,24 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(invincibleprojectiles, cvar("g_invincible_projectiles")); - -MUTATOR_HOOKFUNCTION(invincibleprojectiles, EditProjectile) -{ - entity proj = M_ARGV(1, entity); - - if(proj.health) - { - // disable health which in effect disables damage calculations - proj.health = 0; - } -} - -MUTATOR_HOOKFUNCTION(invincibleprojectiles, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":InvincibleProjectiles"); -} - -MUTATOR_HOOKFUNCTION(invincibleprojectiles, BuildMutatorsPrettyString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Invincible Projectiles"); -} -#endif diff --git a/qcsrc/common/mutators/mutator/invincibleproj/module.inc b/qcsrc/common/mutators/mutator/invincibleproj/module.inc deleted file mode 100644 index 61d038350f..0000000000 --- a/qcsrc/common/mutators/mutator/invincibleproj/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "invincibleproj.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qc b/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qc new file mode 100644 index 0000000000..23e0d0d850 --- /dev/null +++ b/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qc @@ -0,0 +1,24 @@ +#include "sv_invincibleproj.qh" + +REGISTER_MUTATOR(invincibleprojectiles, cvar("g_invincible_projectiles")); + +MUTATOR_HOOKFUNCTION(invincibleprojectiles, EditProjectile) +{ + entity proj = M_ARGV(1, entity); + + if(proj.health) + { + // disable health which in effect disables damage calculations + proj.health = 0; + } +} + +MUTATOR_HOOKFUNCTION(invincibleprojectiles, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":InvincibleProjectiles"); +} + +MUTATOR_HOOKFUNCTION(invincibleprojectiles, BuildMutatorsPrettyString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Invincible Projectiles"); +} diff --git a/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qh b/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/itemstime.qc b/qcsrc/common/mutators/mutator/itemstime.qc deleted file mode 100644 index f47923d4ef..0000000000 --- a/qcsrc/common/mutators/mutator/itemstime.qc +++ /dev/null @@ -1,434 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(itemstime, true); - -REGISTER_NET_TEMP(itemstime) - -#ifdef SVQC -void IT_Write(entity e, int i, float f) { - if (!IS_REAL_CLIENT(e)) return; - msg_entity = e; - WriteHeader(MSG_ONE, itemstime); - WriteByte(MSG_ONE, i); - WriteFloat(MSG_ONE, f); -} -#endif - -#ifdef CSQC -// reserve one more spot for superweapons time -float ItemsTime_time[Items_MAX + 1]; -float ItemsTime_availableTime[Items_MAX + 1]; -NET_HANDLE(itemstime, bool isNew) -{ - int i = ReadByte(); - float f = ReadFloat(); - return = true; - ItemsTime_time[i] = f; -} -#endif - -#ifdef CSQC -void Item_ItemsTime_Init() -{ - FOREACH(Items, true, LAMBDA( - ItemsTime_time[it.m_id] = -1; - )); - ItemsTime_time[Items_MAX] = -1; -} - -STATIC_INIT(ItemsTime_Init) { - Item_ItemsTime_Init(); -} - -int autocvar_hud_panel_itemstime = 2; -float autocvar_hud_panel_itemstime_dynamicsize = 1; -float autocvar_hud_panel_itemstime_ratio = 2; -int autocvar_hud_panel_itemstime_iconalign; -bool autocvar_hud_panel_itemstime_progressbar = 0; -float autocvar_hud_panel_itemstime_progressbar_maxtime = 30; -string autocvar_hud_panel_itemstime_progressbar_name = "progressbar"; -float autocvar_hud_panel_itemstime_progressbar_reduced; -bool autocvar_hud_panel_itemstime_hidespawned = 1; -bool autocvar_hud_panel_itemstime_hidelarge = false; -int autocvar_hud_panel_itemstime_text = 1; -#define hud_panel_itemstime_hidelarge autocvar_hud_panel_itemstime_hidelarge -#else -#define hud_panel_itemstime_hidelarge false -#endif - -bool Item_ItemsTime_SpectatorOnly(GameItem it) -{ - return (false - || it == ITEM_ArmorMega || (it == ITEM_ArmorLarge && !hud_panel_itemstime_hidelarge) - || it == ITEM_HealthMega || (it == ITEM_HealthLarge && !hud_panel_itemstime_hidelarge) - ); -} - -bool Item_ItemsTime_Allow(GameItem it) -{ - return (false - || it.instanceOfPowerup - || Item_ItemsTime_SpectatorOnly(it) - ); -} - -#ifdef SVQC - -// reserve one more spot for superweapons time -float it_times[Items_MAX + 1]; - -void Item_ItemsTime_Init() -{ - FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( - it_times[it.m_id] = -1; - )); - it_times[Items_MAX] = -1; -} - -STATIC_INIT(ItemsTime_Init) { - // items time - Item_ItemsTime_Init(); -} - -void Item_ItemsTime_ResetTimes() -{ - FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( - it_times[it.m_id] = (it_times[it.m_id] == -1) ? -1 : 0; - )); - it_times[Items_MAX] = (it_times[Items_MAX] == -1) ? -1 : 0; -} - -void Item_ItemsTime_ResetTimesForPlayer(entity e) -{ - FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( - IT_Write(e, it.m_id, (it_times[it.m_id] == -1) ? -1 : 0); - )); - IT_Write(e, Items_MAX, (it_times[Items_MAX] == -1) ? -1 : 0); -} - -void Item_ItemsTime_SetTimesForPlayer(entity e) -{ - FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( - IT_Write(e, it.m_id, it_times[it.m_id]); - )); - IT_Write(e, Items_MAX, it_times[Items_MAX]); -} - -void Item_ItemsTime_SetTime(entity e, float t) -{ - if (!autocvar_sv_itemstime) - return; - - GameItem item = e.itemdef; - if (item.instanceOfGameItem) - { - if (!item.instanceOfWeaponPickup) - it_times[item.m_id] = t; - else if (e.weapons & WEPSET_SUPERWEAPONS) - it_times[Items_MAX] = t; - } -} - -void Item_ItemsTime_SetTimesForAllPlayers() -{ - FOREACH_CLIENT(IS_REAL_CLIENT(it) && (warmup_stage || !IS_PLAYER(it) || autocvar_sv_itemstime == 2), LAMBDA(Item_ItemsTime_SetTimesForPlayer(it))); -} - -float Item_ItemsTime_UpdateTime(entity e, float t) -{ - bool isavailable = (t == 0); - FOREACH_ENTITY_FLOAT(pure_data, false, - { - if(!(it.itemdef == e.itemdef || ((e.weapons & WEPSET_SUPERWEAPONS) && (it.weapons & WEPSET_SUPERWEAPONS) && clienttype(it) == CLIENTTYPE_NOTACLIENT))) - continue; - if (e == it) continue; - if (it.scheduledrespawntime <= time) - isavailable = true; - else if (t == 0 || it.scheduledrespawntime < t) - t = it.scheduledrespawntime; - }); - if (isavailable) - t = -t; // let know the client there's another available item - return t; -} - -MUTATOR_HOOKFUNCTION(itemstime, reset_map_global) -{ - Item_ItemsTime_ResetTimes(); - // ALL the times need to be reset before .reset()ing each item - // since Item_Reset schedules respawn of superweapons and powerups - FOREACH_ENTITY_FLOAT(pure_data, false, - { - if(IS_CLIENT(it)) - continue; - if (it.reset) Item_ItemsTime_SetTime(it, 0); - }); - Item_ItemsTime_SetTimesForAllPlayers(); -} - -MUTATOR_HOOKFUNCTION(itemstime, MakePlayerObserver) -{ - entity player = M_ARGV(0, entity); - - Item_ItemsTime_SetTimesForPlayer(player); -} - -MUTATOR_HOOKFUNCTION(itemstime, ClientConnect, CBC_ORDER_LAST) -{ - entity player = M_ARGV(0, entity); - - if(IS_PLAYER(player)) - { - // client became player on connection skipping putObserverInServer step - if (IS_REAL_CLIENT(player)) - if (warmup_stage || autocvar_sv_itemstime == 2) - Item_ItemsTime_SetTimesForPlayer(player); - } -} - -MUTATOR_HOOKFUNCTION(itemstime, PlayerSpawn) -{ - if (warmup_stage || autocvar_sv_itemstime == 2) return; - entity player = M_ARGV(0, entity); - - Item_ItemsTime_ResetTimesForPlayer(player); -} - -#endif - -#ifdef CSQC - -void DrawItemsTimeItem(vector myPos, vector mySize, float ar, string item_icon, float item_time, bool item_available, float item_availableTime) -{ - float t = 0; - vector color = '0 0 0'; - float picalpha; - - if (autocvar_hud_panel_itemstime_hidespawned == 2) - picalpha = 1; - else if (item_available) - { - float BLINK_FACTOR = 0.15; - float BLINK_BASE = 0.85; - float BLINK_FREQ = 5; - picalpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); - } - else - picalpha = 0.5; - t = floor(item_time - time + 0.999); - if (t < 5) - color = '0.7 0 0'; - else if (t < 10) - color = '0.7 0.7 0'; - else - color = '1 1 1'; - - vector picpos, numpos; - if (autocvar_hud_panel_itemstime_iconalign) - { - numpos = myPos; - picpos = myPos + eX * (ar - 1) * mySize_y; - } - else - { - numpos = myPos + eX * mySize_y; - picpos = myPos; - } - - if (t > 0 && autocvar_hud_panel_itemstime_progressbar) - { - vector p_pos, p_size; - if (autocvar_hud_panel_itemstime_progressbar_reduced) - { - p_pos = numpos; - p_size = eX * ((ar - 1)/ar) * mySize_x + eY * mySize_y; - } - else - { - p_pos = myPos; - p_size = mySize; - } - HUD_Panel_DrawProgressBar(p_pos, p_size, autocvar_hud_panel_itemstime_progressbar_name, t/autocvar_hud_panel_itemstime_progressbar_maxtime, 0, autocvar_hud_panel_itemstime_iconalign, color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); - } - - if(autocvar_hud_panel_itemstime_text) - { - if(t > 0) - drawstring_aspect(numpos, ftos(t), eX * ((ar - 1)/ar) * mySize_x + eY * mySize_y, color, panel_fg_alpha, DRAWFLAG_NORMAL); - else if(precache_pic("gfx/hud/default/checkmark")) // COMPAT: check if this image exists, as 0.8.1 clients lack it - drawpic_aspect_skin(numpos, "checkmark", eX * (ar - 1) * mySize_y + eY * mySize_y, '1 1 1', panel_fg_alpha * picalpha, DRAWFLAG_NORMAL); - else // legacy code, if the image is missing just center the icon - picpos.x = myPos.x + mySize.x / 2 - mySize.y / 2; - } - if (item_availableTime) - drawpic_aspect_skin_expanding(picpos, item_icon, '1 1 0' * mySize_y, '1 1 1', panel_fg_alpha * picalpha, DRAWFLAG_NORMAL, item_availableTime); - drawpic_aspect_skin(picpos, item_icon, '1 1 0' * mySize_y, '1 1 1', panel_fg_alpha * picalpha, DRAWFLAG_NORMAL); -} - -void HUD_ItemsTime() -{ - if (!autocvar__hud_configure) - { - if (!( - (autocvar_hud_panel_itemstime == 1 && spectatee_status != 0) - || (autocvar_hud_panel_itemstime == 2 && (spectatee_status != 0 || warmup_stage || STAT(ITEMSTIME) == 2)) - )) { return; } - } - else - { - ItemsTime_time[ITEM_ArmorMega.m_id] = time + 0; - ItemsTime_time[ITEM_HealthMega.m_id] = time + 8; - ItemsTime_time[ITEM_Strength.m_id] = time + 0; - ItemsTime_time[ITEM_Shield.m_id] = time + 4; - } - - int count = 0; - if (autocvar_hud_panel_itemstime_hidespawned == 1) - { - FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( - count += (ItemsTime_time[it.m_id] > time || -ItemsTime_time[it.m_id] > time); - )); - count += (ItemsTime_time[Items_MAX] > time || -ItemsTime_time[Items_MAX] > time); - } - else if (autocvar_hud_panel_itemstime_hidespawned == 2) - { - FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( - count += (ItemsTime_time[it.m_id] > time); - )); - count += (ItemsTime_time[Items_MAX] > time); - } - else - { - FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( - count += (ItemsTime_time[it.m_id] != -1); - )); - count += (ItemsTime_time[Items_MAX] != -1); - } - if (count == 0) - return; - - HUD_Panel_UpdateCvars(); - - vector pos, mySize; - pos = panel_pos; - mySize = panel_size; - - if (panel_bg_padding) - { - pos += '1 1 0' * panel_bg_padding; - mySize -= '2 2 0' * panel_bg_padding; - } - - float rows, columns; - float ar = max(2, autocvar_hud_panel_itemstime_ratio) + 1; - rows = HUD_GetRowCount(count, mySize, ar); - columns = ceil(count/rows); - - vector itemstime_size = eX * mySize.x*(1/columns) + eY * mySize.y*(1/rows); - - vector offset = '0 0 0'; - float newSize; - if (autocvar_hud_panel_itemstime_dynamicsize) - { - if (autocvar__hud_configure) - if (hud_configure_menu_open != 2) - HUD_Panel_DrawBg(1); // also draw the bg of the entire panel - - // reduce panel to avoid spacing items - if (itemstime_size.x / itemstime_size.y < ar) - { - newSize = rows * itemstime_size.x / ar; - pos.y += (mySize.y - newSize) / 2; - mySize.y = newSize; - itemstime_size.y = mySize.y / rows; - } - else - { - newSize = columns * itemstime_size.y * ar; - pos.x += (mySize.x - newSize) / 2; - mySize.x = newSize; - itemstime_size.x = mySize.x / columns; - } - panel_pos = pos - '1 1 0' * panel_bg_padding; - panel_size = mySize + '2 2 0' * panel_bg_padding; - } - else - { - if (itemstime_size.x/itemstime_size.y > ar) - { - newSize = ar * itemstime_size.y; - offset.x = itemstime_size.x - newSize; - pos.x += offset.x/2; - itemstime_size.x = newSize; - } - else - { - newSize = 1/ar * itemstime_size.x; - offset.y = itemstime_size.y - newSize; - pos.y += offset.y/2; - itemstime_size.y = newSize; - } - } - - HUD_Scale_Enable(); - HUD_Panel_DrawBg(1); - - float row = 0, column = 0; - bool item_available; - int id = 0; - string icon = ""; - FOREACH(Items, Item_ItemsTime_Allow(it) && ItemsTime_time[it.m_id] != -1, LAMBDA( - id = it.m_id; - icon = it.m_icon; - -LABEL(iteration) - float item_time = ItemsTime_time[id]; - if (item_time < -1) - { - item_available = true; - item_time = -item_time; - } - else - item_available = (item_time <= time); - - if (ItemsTime_time[id] >= 0) - { - if (time <= ItemsTime_time[id]) - ItemsTime_availableTime[id] = 0; - else if (ItemsTime_availableTime[id] == 0) - ItemsTime_availableTime[id] = time; - } - else if (ItemsTime_availableTime[id] == 0) - ItemsTime_availableTime[id] = time; - - float f = (time - ItemsTime_availableTime[id]) * 2; - f = (f > 1) ? 0 : bound(0, f, 1); - - if (autocvar_hud_panel_itemstime_hidespawned == 1) - if (!(ItemsTime_time[id] > time || -ItemsTime_time[id] > time)) - continue; - - if (autocvar_hud_panel_itemstime_hidespawned == 2) - if (!(ItemsTime_time[id] > time)) - continue; - - DrawItemsTimeItem(pos + eX * column * (itemstime_size.x + offset.x) + eY * row * (itemstime_size.y + offset.y), itemstime_size, ar, icon, item_time, item_available, f); - ++row; - if (row >= rows) - { - row = 0; - column = column + 1; - } - if(id == Items_MAX) // can happen only in the last fake iteration - break; - )); - // add another fake iteration for superweapons time - if(id < Items_MAX && ItemsTime_time[Items_MAX] != -1) - { - id = Items_MAX; - icon = "superweapons"; - goto iteration; - } -} - -#endif -#endif diff --git a/qcsrc/common/mutators/mutator/itemstime/_mod.inc b/qcsrc/common/mutators/mutator/itemstime/_mod.inc new file mode 100644 index 0000000000..5b34dd6210 --- /dev/null +++ b/qcsrc/common/mutators/mutator/itemstime/_mod.inc @@ -0,0 +1,2 @@ +// generated file; do not modify +#include diff --git a/qcsrc/common/mutators/mutator/itemstime/_mod.qh b/qcsrc/common/mutators/mutator/itemstime/_mod.qh new file mode 100644 index 0000000000..5c73eea2ec --- /dev/null +++ b/qcsrc/common/mutators/mutator/itemstime/_mod.qh @@ -0,0 +1,2 @@ +// generated file; do not modify +#include diff --git a/qcsrc/common/mutators/mutator/itemstime/itemstime.qc b/qcsrc/common/mutators/mutator/itemstime/itemstime.qc new file mode 100644 index 0000000000..3fe9de6d7d --- /dev/null +++ b/qcsrc/common/mutators/mutator/itemstime/itemstime.qc @@ -0,0 +1,434 @@ +#include "itemstime.qh" + +REGISTER_MUTATOR(itemstime, true); + +REGISTER_NET_TEMP(itemstime) + +#ifdef SVQC +void IT_Write(entity e, int i, float f) { + if (!IS_REAL_CLIENT(e)) return; + msg_entity = e; + WriteHeader(MSG_ONE, itemstime); + WriteByte(MSG_ONE, i); + WriteFloat(MSG_ONE, f); +} +#endif + +#ifdef CSQC +// reserve one more spot for superweapons time +float ItemsTime_time[Items_MAX + 1]; +float ItemsTime_availableTime[Items_MAX + 1]; +NET_HANDLE(itemstime, bool isNew) +{ + int i = ReadByte(); + float f = ReadFloat(); + return = true; + ItemsTime_time[i] = f; +} +#endif + +#ifdef CSQC +void Item_ItemsTime_Init() +{ + FOREACH(Items, true, LAMBDA( + ItemsTime_time[it.m_id] = -1; + )); + ItemsTime_time[Items_MAX] = -1; +} + +STATIC_INIT(ItemsTime_Init) { + Item_ItemsTime_Init(); +} + +int autocvar_hud_panel_itemstime = 2; +float autocvar_hud_panel_itemstime_dynamicsize = 1; +float autocvar_hud_panel_itemstime_ratio = 2; +int autocvar_hud_panel_itemstime_iconalign; +bool autocvar_hud_panel_itemstime_progressbar = 0; +float autocvar_hud_panel_itemstime_progressbar_maxtime = 30; +string autocvar_hud_panel_itemstime_progressbar_name = "progressbar"; +float autocvar_hud_panel_itemstime_progressbar_reduced; +bool autocvar_hud_panel_itemstime_hidespawned = 1; +bool autocvar_hud_panel_itemstime_hidelarge = false; +int autocvar_hud_panel_itemstime_text = 1; +#define hud_panel_itemstime_hidelarge autocvar_hud_panel_itemstime_hidelarge +#else +#define hud_panel_itemstime_hidelarge false +#endif + +bool Item_ItemsTime_SpectatorOnly(GameItem it) +{ + return (false + || it == ITEM_ArmorMega || (it == ITEM_ArmorLarge && !hud_panel_itemstime_hidelarge) + || it == ITEM_HealthMega || (it == ITEM_HealthLarge && !hud_panel_itemstime_hidelarge) + ); +} + +bool Item_ItemsTime_Allow(GameItem it) +{ + return (false + || it.instanceOfPowerup + || Item_ItemsTime_SpectatorOnly(it) + ); +} + +#ifdef SVQC + +// reserve one more spot for superweapons time +float it_times[Items_MAX + 1]; + +void Item_ItemsTime_Init() +{ + FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( + it_times[it.m_id] = -1; + )); + it_times[Items_MAX] = -1; +} + +STATIC_INIT(ItemsTime_Init) { + // items time + Item_ItemsTime_Init(); +} + +void Item_ItemsTime_ResetTimes() +{ + FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( + it_times[it.m_id] = (it_times[it.m_id] == -1) ? -1 : 0; + )); + it_times[Items_MAX] = (it_times[Items_MAX] == -1) ? -1 : 0; +} + +void Item_ItemsTime_ResetTimesForPlayer(entity e) +{ + FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( + IT_Write(e, it.m_id, (it_times[it.m_id] == -1) ? -1 : 0); + )); + IT_Write(e, Items_MAX, (it_times[Items_MAX] == -1) ? -1 : 0); +} + +void Item_ItemsTime_SetTimesForPlayer(entity e) +{ + FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( + IT_Write(e, it.m_id, it_times[it.m_id]); + )); + IT_Write(e, Items_MAX, it_times[Items_MAX]); +} + +void Item_ItemsTime_SetTime(entity e, float t) +{ + if (!autocvar_sv_itemstime) + return; + + GameItem item = e.itemdef; + if (item.instanceOfGameItem) + { + if (!item.instanceOfWeaponPickup) + it_times[item.m_id] = t; + else if (e.weapons & WEPSET_SUPERWEAPONS) + it_times[Items_MAX] = t; + } +} + +void Item_ItemsTime_SetTimesForAllPlayers() +{ + FOREACH_CLIENT(IS_REAL_CLIENT(it) && (warmup_stage || !IS_PLAYER(it) || autocvar_sv_itemstime == 2), LAMBDA(Item_ItemsTime_SetTimesForPlayer(it))); +} + +float Item_ItemsTime_UpdateTime(entity e, float t) +{ + bool isavailable = (t == 0); + FOREACH_ENTITY_FLOAT(pure_data, false, + { + if(!(it.itemdef == e.itemdef || ((e.weapons & WEPSET_SUPERWEAPONS) && (it.weapons & WEPSET_SUPERWEAPONS) && clienttype(it) == CLIENTTYPE_NOTACLIENT))) + continue; + if (e == it) continue; + if (it.scheduledrespawntime <= time) + isavailable = true; + else if (t == 0 || it.scheduledrespawntime < t) + t = it.scheduledrespawntime; + }); + if (isavailable) + t = -t; // let know the client there's another available item + return t; +} + +MUTATOR_HOOKFUNCTION(itemstime, reset_map_global) +{ + Item_ItemsTime_ResetTimes(); + // ALL the times need to be reset before .reset()ing each item + // since Item_Reset schedules respawn of superweapons and powerups + FOREACH_ENTITY_FLOAT(pure_data, false, + { + if(IS_CLIENT(it)) + continue; + if (it.reset) Item_ItemsTime_SetTime(it, 0); + }); + Item_ItemsTime_SetTimesForAllPlayers(); +} + +MUTATOR_HOOKFUNCTION(itemstime, MakePlayerObserver) +{ + entity player = M_ARGV(0, entity); + + Item_ItemsTime_SetTimesForPlayer(player); +} + +MUTATOR_HOOKFUNCTION(itemstime, ClientConnect, CBC_ORDER_LAST) +{ + entity player = M_ARGV(0, entity); + + if(IS_PLAYER(player)) + { + // client became player on connection skipping putObserverInServer step + if (IS_REAL_CLIENT(player)) + if (warmup_stage || autocvar_sv_itemstime == 2) + Item_ItemsTime_SetTimesForPlayer(player); + } +} + +MUTATOR_HOOKFUNCTION(itemstime, PlayerSpawn) +{ + if (warmup_stage || autocvar_sv_itemstime == 2) return; + entity player = M_ARGV(0, entity); + + Item_ItemsTime_ResetTimesForPlayer(player); +} + +#endif + +#ifdef CSQC + +void DrawItemsTimeItem(vector myPos, vector mySize, float ar, string item_icon, float item_time, bool item_available, float item_availableTime) +{ + float t = 0; + vector color = '0 0 0'; + float picalpha; + + if (autocvar_hud_panel_itemstime_hidespawned == 2) + picalpha = 1; + else if (item_available) + { + float BLINK_FACTOR = 0.15; + float BLINK_BASE = 0.85; + float BLINK_FREQ = 5; + picalpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); + } + else + picalpha = 0.5; + t = floor(item_time - time + 0.999); + if (t < 5) + color = '0.7 0 0'; + else if (t < 10) + color = '0.7 0.7 0'; + else + color = '1 1 1'; + + vector picpos, numpos; + if (autocvar_hud_panel_itemstime_iconalign) + { + numpos = myPos; + picpos = myPos + eX * (ar - 1) * mySize_y; + } + else + { + numpos = myPos + eX * mySize_y; + picpos = myPos; + } + + if (t > 0 && autocvar_hud_panel_itemstime_progressbar) + { + vector p_pos, p_size; + if (autocvar_hud_panel_itemstime_progressbar_reduced) + { + p_pos = numpos; + p_size = eX * ((ar - 1)/ar) * mySize_x + eY * mySize_y; + } + else + { + p_pos = myPos; + p_size = mySize; + } + HUD_Panel_DrawProgressBar(p_pos, p_size, autocvar_hud_panel_itemstime_progressbar_name, t/autocvar_hud_panel_itemstime_progressbar_maxtime, 0, autocvar_hud_panel_itemstime_iconalign, color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); + } + + if(autocvar_hud_panel_itemstime_text) + { + if(t > 0) + drawstring_aspect(numpos, ftos(t), eX * ((ar - 1)/ar) * mySize_x + eY * mySize_y, color, panel_fg_alpha, DRAWFLAG_NORMAL); + else if(precache_pic("gfx/hud/default/checkmark")) // COMPAT: check if this image exists, as 0.8.1 clients lack it + drawpic_aspect_skin(numpos, "checkmark", eX * (ar - 1) * mySize_y + eY * mySize_y, '1 1 1', panel_fg_alpha * picalpha, DRAWFLAG_NORMAL); + else // legacy code, if the image is missing just center the icon + picpos.x = myPos.x + mySize.x / 2 - mySize.y / 2; + } + if (item_availableTime) + drawpic_aspect_skin_expanding(picpos, item_icon, '1 1 0' * mySize_y, '1 1 1', panel_fg_alpha * picalpha, DRAWFLAG_NORMAL, item_availableTime); + drawpic_aspect_skin(picpos, item_icon, '1 1 0' * mySize_y, '1 1 1', panel_fg_alpha * picalpha, DRAWFLAG_NORMAL); +} + +void HUD_ItemsTime() +{ + if (!autocvar__hud_configure) + { + if (!( + (autocvar_hud_panel_itemstime == 1 && spectatee_status != 0) + || (autocvar_hud_panel_itemstime == 2 && (spectatee_status != 0 || warmup_stage || STAT(ITEMSTIME) == 2)) + )) { return; } + } + else + { + ItemsTime_time[ITEM_ArmorMega.m_id] = time + 0; + ItemsTime_time[ITEM_HealthMega.m_id] = time + 8; + ItemsTime_time[ITEM_Strength.m_id] = time + 0; + ItemsTime_time[ITEM_Shield.m_id] = time + 4; + } + + int count = 0; + if (autocvar_hud_panel_itemstime_hidespawned == 1) + { + FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( + count += (ItemsTime_time[it.m_id] > time || -ItemsTime_time[it.m_id] > time); + )); + count += (ItemsTime_time[Items_MAX] > time || -ItemsTime_time[Items_MAX] > time); + } + else if (autocvar_hud_panel_itemstime_hidespawned == 2) + { + FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( + count += (ItemsTime_time[it.m_id] > time); + )); + count += (ItemsTime_time[Items_MAX] > time); + } + else + { + FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( + count += (ItemsTime_time[it.m_id] != -1); + )); + count += (ItemsTime_time[Items_MAX] != -1); + } + if (count == 0) + return; + + HUD_Panel_UpdateCvars(); + + vector pos, mySize; + pos = panel_pos; + mySize = panel_size; + + if (panel_bg_padding) + { + pos += '1 1 0' * panel_bg_padding; + mySize -= '2 2 0' * panel_bg_padding; + } + + float rows, columns; + float ar = max(2, autocvar_hud_panel_itemstime_ratio) + 1; + rows = HUD_GetRowCount(count, mySize, ar); + columns = ceil(count/rows); + + vector itemstime_size = eX * mySize.x*(1/columns) + eY * mySize.y*(1/rows); + + vector offset = '0 0 0'; + float newSize; + if (autocvar_hud_panel_itemstime_dynamicsize) + { + if (autocvar__hud_configure) + if (hud_configure_menu_open != 2) + HUD_Panel_DrawBg(1); // also draw the bg of the entire panel + + // reduce panel to avoid spacing items + if (itemstime_size.x / itemstime_size.y < ar) + { + newSize = rows * itemstime_size.x / ar; + pos.y += (mySize.y - newSize) / 2; + mySize.y = newSize; + itemstime_size.y = mySize.y / rows; + } + else + { + newSize = columns * itemstime_size.y * ar; + pos.x += (mySize.x - newSize) / 2; + mySize.x = newSize; + itemstime_size.x = mySize.x / columns; + } + panel_pos = pos - '1 1 0' * panel_bg_padding; + panel_size = mySize + '2 2 0' * panel_bg_padding; + } + else + { + if (itemstime_size.x/itemstime_size.y > ar) + { + newSize = ar * itemstime_size.y; + offset.x = itemstime_size.x - newSize; + pos.x += offset.x/2; + itemstime_size.x = newSize; + } + else + { + newSize = 1/ar * itemstime_size.x; + offset.y = itemstime_size.y - newSize; + pos.y += offset.y/2; + itemstime_size.y = newSize; + } + } + + HUD_Scale_Enable(); + HUD_Panel_DrawBg(1); + + float row = 0, column = 0; + bool item_available; + int id = 0; + string icon = ""; + FOREACH(Items, Item_ItemsTime_Allow(it) && ItemsTime_time[it.m_id] != -1, LAMBDA( + id = it.m_id; + icon = it.m_icon; + +LABEL(iteration) + float item_time = ItemsTime_time[id]; + if (item_time < -1) + { + item_available = true; + item_time = -item_time; + } + else + item_available = (item_time <= time); + + if (ItemsTime_time[id] >= 0) + { + if (time <= ItemsTime_time[id]) + ItemsTime_availableTime[id] = 0; + else if (ItemsTime_availableTime[id] == 0) + ItemsTime_availableTime[id] = time; + } + else if (ItemsTime_availableTime[id] == 0) + ItemsTime_availableTime[id] = time; + + float f = (time - ItemsTime_availableTime[id]) * 2; + f = (f > 1) ? 0 : bound(0, f, 1); + + if (autocvar_hud_panel_itemstime_hidespawned == 1) + if (!(ItemsTime_time[id] > time || -ItemsTime_time[id] > time)) + continue; + + if (autocvar_hud_panel_itemstime_hidespawned == 2) + if (!(ItemsTime_time[id] > time)) + continue; + + DrawItemsTimeItem(pos + eX * column * (itemstime_size.x + offset.x) + eY * row * (itemstime_size.y + offset.y), itemstime_size, ar, icon, item_time, item_available, f); + ++row; + if (row >= rows) + { + row = 0; + column = column + 1; + } + if(id == Items_MAX) // can happen only in the last fake iteration + break; + )); + // add another fake iteration for superweapons time + if(id < Items_MAX && ItemsTime_time[Items_MAX] != -1) + { + id = Items_MAX; + icon = "superweapons"; + goto iteration; + } +} + +#endif diff --git a/qcsrc/common/mutators/mutator/itemstime/itemstime.qh b/qcsrc/common/mutators/mutator/itemstime/itemstime.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/itemstime/itemstime.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/melee_only/_mod.inc b/qcsrc/common/mutators/mutator/melee_only/_mod.inc index db31be3fdd..da02f08086 100644 --- a/qcsrc/common/mutators/mutator/melee_only/_mod.inc +++ b/qcsrc/common/mutators/mutator/melee_only/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/melee_only/_mod.qh b/qcsrc/common/mutators/mutator/melee_only/_mod.qh index 2228d64f18..297bb2b962 100644 --- a/qcsrc/common/mutators/mutator/melee_only/_mod.qh +++ b/qcsrc/common/mutators/mutator/melee_only/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/melee_only/melee_only.qc b/qcsrc/common/mutators/mutator/melee_only/melee_only.qc deleted file mode 100644 index ecd5fc7c8e..0000000000 --- a/qcsrc/common/mutators/mutator/melee_only/melee_only.qc +++ /dev/null @@ -1,38 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(melee_only, cvar("g_melee_only") && !cvar("g_instagib") && !g_nexball); - -MUTATOR_HOOKFUNCTION(melee_only, SetStartItems) -{ - start_ammo_shells = warmup_start_ammo_shells = 0; - start_weapons = warmup_start_weapons = WEPSET(SHOTGUN); -} - -MUTATOR_HOOKFUNCTION(melee_only, ForbidThrowCurrentWeapon) -{ - return true; -} - -MUTATOR_HOOKFUNCTION(melee_only, FilterItem) -{ - entity item = M_ARGV(0, entity); - - switch (item.items) - { - case ITEM_HealthSmall.m_itemid: - case ITEM_ArmorSmall.m_itemid: - return false; - } - - return true; -} - -MUTATOR_HOOKFUNCTION(melee_only, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":MeleeOnly"); -} - -MUTATOR_HOOKFUNCTION(melee_only, BuildMutatorsPrettyString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Melee Only Arena"); -} -#endif diff --git a/qcsrc/common/mutators/mutator/melee_only/module.inc b/qcsrc/common/mutators/mutator/melee_only/module.inc deleted file mode 100644 index c711556ccf..0000000000 --- a/qcsrc/common/mutators/mutator/melee_only/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "melee_only.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/melee_only/sv_melee_only.qc b/qcsrc/common/mutators/mutator/melee_only/sv_melee_only.qc new file mode 100644 index 0000000000..e07034bafa --- /dev/null +++ b/qcsrc/common/mutators/mutator/melee_only/sv_melee_only.qc @@ -0,0 +1,38 @@ +#include "sv_melee_only.qh" + +REGISTER_MUTATOR(melee_only, cvar("g_melee_only") && !cvar("g_instagib") && !g_nexball); + +MUTATOR_HOOKFUNCTION(melee_only, SetStartItems) +{ + start_ammo_shells = warmup_start_ammo_shells = 0; + start_weapons = warmup_start_weapons = WEPSET(SHOTGUN); +} + +MUTATOR_HOOKFUNCTION(melee_only, ForbidThrowCurrentWeapon) +{ + return true; +} + +MUTATOR_HOOKFUNCTION(melee_only, FilterItem) +{ + entity item = M_ARGV(0, entity); + + switch (item.items) + { + case ITEM_HealthSmall.m_itemid: + case ITEM_ArmorSmall.m_itemid: + return false; + } + + return true; +} + +MUTATOR_HOOKFUNCTION(melee_only, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":MeleeOnly"); +} + +MUTATOR_HOOKFUNCTION(melee_only, BuildMutatorsPrettyString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Melee Only Arena"); +} diff --git a/qcsrc/common/mutators/mutator/melee_only/sv_melee_only.qh b/qcsrc/common/mutators/mutator/melee_only/sv_melee_only.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/melee_only/sv_melee_only.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/midair/_mod.inc b/qcsrc/common/mutators/mutator/midair/_mod.inc index 8fcc96ea21..b144ca4b6a 100644 --- a/qcsrc/common/mutators/mutator/midair/_mod.inc +++ b/qcsrc/common/mutators/mutator/midair/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/midair/_mod.qh b/qcsrc/common/mutators/mutator/midair/_mod.qh index 48272b8709..f96da13867 100644 --- a/qcsrc/common/mutators/mutator/midair/_mod.qh +++ b/qcsrc/common/mutators/mutator/midair/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/midair/midair.qc b/qcsrc/common/mutators/mutator/midair/midair.qc deleted file mode 100644 index 1fa69a60e1..0000000000 --- a/qcsrc/common/mutators/mutator/midair/midair.qc +++ /dev/null @@ -1,49 +0,0 @@ -#ifdef IMPLEMENTATION - -float autocvar_g_midair_shieldtime; - -REGISTER_MUTATOR(midair, cvar("g_midair")); - -.float midair_shieldtime; - -MUTATOR_HOOKFUNCTION(midair, PlayerDamage_Calculate) -{ - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - - if(IS_PLAYER(frag_attacker)) - if(IS_PLAYER(frag_target)) - if(time < frag_target.midair_shieldtime) - M_ARGV(4, float) = 0; -} - -MUTATOR_HOOKFUNCTION(midair, PlayerPowerups) -{ - entity player = M_ARGV(0, entity); - - if(time >= game_starttime) - if(IS_ONGROUND(player)) - { - player.effects |= (EF_ADDITIVE | EF_FULLBRIGHT); - player.midair_shieldtime = max(player.midair_shieldtime, time + autocvar_g_midair_shieldtime); - } -} - -MUTATOR_HOOKFUNCTION(midair, PlayerSpawn) -{ - entity player = M_ARGV(0, entity); - - if(IS_BOT_CLIENT(player)) - player.bot_moveskill = 0; // disable bunnyhopping -} - -MUTATOR_HOOKFUNCTION(midair, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":midair"); -} - -MUTATOR_HOOKFUNCTION(midair, BuildMutatorsPrettyString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Midair"); -} -#endif diff --git a/qcsrc/common/mutators/mutator/midair/module.inc b/qcsrc/common/mutators/mutator/midair/module.inc deleted file mode 100644 index 10b1789159..0000000000 --- a/qcsrc/common/mutators/mutator/midair/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "midair.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/midair/sv_midair.qc b/qcsrc/common/mutators/mutator/midair/sv_midair.qc new file mode 100644 index 0000000000..40747ff056 --- /dev/null +++ b/qcsrc/common/mutators/mutator/midair/sv_midair.qc @@ -0,0 +1,48 @@ +#include "sv_midair.qh" + +float autocvar_g_midair_shieldtime; + +REGISTER_MUTATOR(midair, cvar("g_midair")); + +.float midair_shieldtime; + +MUTATOR_HOOKFUNCTION(midair, PlayerDamage_Calculate) +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + + if(IS_PLAYER(frag_attacker)) + if(IS_PLAYER(frag_target)) + if(time < frag_target.midair_shieldtime) + M_ARGV(4, float) = 0; +} + +MUTATOR_HOOKFUNCTION(midair, PlayerPowerups) +{ + entity player = M_ARGV(0, entity); + + if(time >= game_starttime) + if(IS_ONGROUND(player)) + { + player.effects |= (EF_ADDITIVE | EF_FULLBRIGHT); + player.midair_shieldtime = max(player.midair_shieldtime, time + autocvar_g_midair_shieldtime); + } +} + +MUTATOR_HOOKFUNCTION(midair, PlayerSpawn) +{ + entity player = M_ARGV(0, entity); + + if(IS_BOT_CLIENT(player)) + player.bot_moveskill = 0; // disable bunnyhopping +} + +MUTATOR_HOOKFUNCTION(midair, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":midair"); +} + +MUTATOR_HOOKFUNCTION(midair, BuildMutatorsPrettyString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Midair"); +} diff --git a/qcsrc/common/mutators/mutator/midair/sv_midair.qh b/qcsrc/common/mutators/mutator/midair/sv_midair.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/midair/sv_midair.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/multijump/module.inc b/qcsrc/common/mutators/mutator/multijump/module.inc deleted file mode 100644 index 3103320990..0000000000 --- a/qcsrc/common/mutators/mutator/multijump/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifndef MENUQC -#include "multijump.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/multijump/multijump.qc b/qcsrc/common/mutators/mutator/multijump/multijump.qc index d6dc30cee4..ecedc47596 100644 --- a/qcsrc/common/mutators/mutator/multijump/multijump.qc +++ b/qcsrc/common/mutators/mutator/multijump/multijump.qc @@ -1,4 +1,7 @@ -#ifdef IMPLEMENTATION +#include "multijump.qh" + +#ifdef GAMEQC + #ifdef SVQC #include #endif @@ -126,4 +129,5 @@ MUTATOR_HOOKFUNCTION(multijump, BuildMutatorsPrettyString) } #endif + #endif diff --git a/qcsrc/common/mutators/mutator/multijump/multijump.qh b/qcsrc/common/mutators/mutator/multijump/multijump.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/multijump/multijump.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/nades/module.inc b/qcsrc/common/mutators/mutator/nades/module.inc deleted file mode 100644 index e03900d6a5..0000000000 --- a/qcsrc/common/mutators/mutator/nades/module.inc +++ /dev/null @@ -1,4 +0,0 @@ -#include "nades.qc" -#ifndef MENUQC -#include "net.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/nades/nades.inc b/qcsrc/common/mutators/mutator/nades/nades.inc index 8a7337f610..bcdbe0cd92 100644 --- a/qcsrc/common/mutators/mutator/nades/nades.inc +++ b/qcsrc/common/mutators/mutator/nades/nades.inc @@ -1,4 +1,4 @@ -#ifndef MENUQC +#ifdef GAMEQC #define NADE_PROJECTILE(i, projectile, trail) MACRO_BEGIN { \ this.m_projectile[i] = projectile; \ this.m_trail[i] = trail; \ diff --git a/qcsrc/common/mutators/mutator/nades/nades.qc b/qcsrc/common/mutators/mutator/nades/nades.qc index d785124a76..d15353745e 100644 --- a/qcsrc/common/mutators/mutator/nades/nades.qc +++ b/qcsrc/common/mutators/mutator/nades/nades.qc @@ -1,7 +1,5 @@ #include "nades.qh" -#ifdef IMPLEMENTATION - #ifdef SVQC bool autocvar_g_nades_nade_small; float autocvar_g_nades_spread = 0.04; @@ -9,7 +7,7 @@ float autocvar_g_nades_spread = 0.04; REGISTER_STAT(NADES_SMALL, int, autocvar_g_nades_nade_small) -#ifndef MENUQC +#ifdef GAMEQC entity Nade_TrailEffect(int proj, int nade_team) { switch (proj) @@ -147,7 +145,7 @@ void DrawAmmoNades(vector myPos, vector mySize, bool draw_expanding, float expan #ifdef SVQC -#include +#include #include #include #include @@ -1486,4 +1484,3 @@ MUTATOR_HOOKFUNCTION(nades, BuildGameplayTipsString) } #endif -#endif diff --git a/qcsrc/common/mutators/mutator/nades/nades.qh b/qcsrc/common/mutators/mutator/nades/nades.qh index a7eed65d33..fd8d26902a 100644 --- a/qcsrc/common/mutators/mutator/nades/nades.qh +++ b/qcsrc/common/mutators/mutator/nades/nades.qh @@ -1,5 +1,4 @@ -#ifndef NADES_ALL_H -#define NADES_ALL_H +#pragma once #include @@ -52,7 +51,7 @@ Nade Nade_FromProjectile(int proj) return NADE_TYPE_Null; } -#ifndef MENUQC +#ifdef GAMEQC #include "effects.inc" #endif @@ -86,7 +85,7 @@ bool orb_send(entity this, entity to, int sf); void nades_Clear(entity player); // Give a bonus grenade to a player -void(entity player, float score) nades_GiveBonus; +void nades_GiveBonus(entity player, float score); /** * called to adjust nade damage and force on hit @@ -102,5 +101,3 @@ void(entity player, float score) nades_GiveBonus; MUTATOR_HOOKABLE(Nade_Damage, EV_Nade_Damage); #endif - -#endif diff --git a/qcsrc/common/mutators/mutator/nades/net.qc b/qcsrc/common/mutators/mutator/nades/net.qc index e2659c7af8..498d878d2d 100644 --- a/qcsrc/common/mutators/mutator/nades/net.qc +++ b/qcsrc/common/mutators/mutator/nades/net.qc @@ -1,6 +1,8 @@ -#include "nades.qh" +#include "net.qh" + +#ifdef GAMEQC -#ifdef IMPLEMENTATION +#include "nades.qh" #ifdef CSQC .float ltime; diff --git a/qcsrc/common/mutators/mutator/nades/net.qh b/qcsrc/common/mutators/mutator/nades/net.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/nades/net.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/new_toys/_mod.inc b/qcsrc/common/mutators/mutator/new_toys/_mod.inc index 90e9811ea6..67ee4f5345 100644 --- a/qcsrc/common/mutators/mutator/new_toys/_mod.inc +++ b/qcsrc/common/mutators/mutator/new_toys/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/new_toys/_mod.qh b/qcsrc/common/mutators/mutator/new_toys/_mod.qh index ec3b8105fe..97f88a5192 100644 --- a/qcsrc/common/mutators/mutator/new_toys/_mod.qh +++ b/qcsrc/common/mutators/mutator/new_toys/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/new_toys/module.inc b/qcsrc/common/mutators/mutator/new_toys/module.inc deleted file mode 100644 index 1217177c58..0000000000 --- a/qcsrc/common/mutators/mutator/new_toys/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "new_toys.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/new_toys/new_toys.qc b/qcsrc/common/mutators/mutator/new_toys/new_toys.qc deleted file mode 100644 index 27d1795062..0000000000 --- a/qcsrc/common/mutators/mutator/new_toys/new_toys.qc +++ /dev/null @@ -1,229 +0,0 @@ -#ifdef IMPLEMENTATION -/* - -CORE laser vortex lg rl cry gl elec hagar fireb hook - vaporizer porto - tuba - -NEW rifle hlac minel seeker -IDEAS OPEN flak OPEN FUN FUN FUN FUN - - - -How this mutator works: - ======================= - -When a gun tries to spawn, this mutator is called. It will provide alternate -weaponreplace lists. - -Entity: - -{ -"classname" "weapon_vortex" -"new_toys" "rifle" -} --> This will spawn as Rifle in this mutator ONLY, and as Vortex otherwise. - -{ -"classname" "weapon_vortext" -"new_toys" "vortex rifle" -} --> This will spawn as either Vortex or Rifle in this mutator ONLY, and as Vortex otherwise. - -{ -"classname" "weapon_vortex" -"new_toys" "vortex" -} --> This is always a Vortex. - -If the map specifies no "new_toys" argument - -There will be two default replacements selectable: "replace all" and "replace random". -In "replace all" mode, e.g. Vortex will have the default replacement "rifle". -In "replace random" mode, Vortex will have the default replacement "vortex rifle". - -This mutator's replacements run BEFORE regular weaponreplace! - -The New Toys guns do NOT get a spawn function, so they can only ever be spawned -when this mutator is active. - -Likewise, warmup, give all, give ALL and impulse 99 will not give them unless -this mutator is active. - -Outside this mutator, they still can be spawned by: -- setting their start weapon cvar to 1 -- give weaponname -- weaponreplace -- weaponarena (but all and most weapons arena again won't include them) - -This mutator performs the default replacements on the DEFAULTS of the -start weapon selection. - -These weapons appear in the menu's priority list, BUT get a suffix -"(Mutator weapon)". - -Picking up a "new toys" weapon will not play standard weapon pickup sound, but -roflsound "New toys, new toys!" sound. - -*/ - -bool nt_IsNewToy(int w); - -REGISTER_MUTATOR(nt, cvar("g_new_toys") && !cvar("g_instagib") && !cvar("g_overkill")) -{ - MUTATOR_ONADD - { - if(time > 1) // game loads at time 1 - error("This cannot be added at runtime\n"); - - // mark the guns as ok to use by e.g. impulse 99 - FOREACH(Weapons, it != WEP_Null, LAMBDA( - if(nt_IsNewToy(it.m_id)) - it.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; - )); - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - FOREACH(Weapons, it != WEP_Null, LAMBDA( - if(nt_IsNewToy(it.m_id)) - it.spawnflags |= WEP_FLAG_MUTATORBLOCKED; - )); - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This cannot be removed at runtime\n"); - return -1; - } - - return 0; -} - -.string new_toys; - -float autocvar_g_new_toys_autoreplace; -bool autocvar_g_new_toys_use_pickupsound = true; -const float NT_AUTOREPLACE_NEVER = 0; -const float NT_AUTOREPLACE_ALWAYS = 1; -const float NT_AUTOREPLACE_RANDOM = 2; - -MUTATOR_HOOKFUNCTION(nt, SetModname) -{ - M_ARGV(0, string) = "NewToys"; -} - -bool nt_IsNewToy(int w) -{ - switch(w) - { - case WEP_SEEKER.m_id: - case WEP_MINE_LAYER.m_id: - case WEP_HLAC.m_id: - case WEP_RIFLE.m_id: - case WEP_SHOCKWAVE.m_id: - return true; - default: - return false; - } -} - -string nt_GetFullReplacement(string w) -{ - switch(w) - { - case "hagar": return "seeker"; - case "devastator": return "minelayer"; - case "machinegun": return "hlac"; - case "vortex": return "rifle"; - //case "shotgun": return "shockwave"; - default: return string_null; - } -} - -string nt_GetReplacement(string w, float m) -{ - if(m == NT_AUTOREPLACE_NEVER) - return w; - string s = nt_GetFullReplacement(w); - if (!s) - return w; - if(m == NT_AUTOREPLACE_RANDOM) - s = strcat(w, " ", s); - return s; -} - -MUTATOR_HOOKFUNCTION(nt, SetStartItems) -{ - // rearrange start_weapon_default - // apply those bits that are set by start_weapon_defaultmask - // same for warmup - - float j, n; - - WepSet newdefault; - WepSet warmup_newdefault; - - newdefault = '0 0 0'; - warmup_newdefault = '0 0 0'; - - WepSet seti = '0 0 0'; - - FOREACH(Weapons, it != WEP_Null, LAMBDA( - seti = it.m_wepset; - n = tokenize_console(nt_GetReplacement(it.netname, autocvar_g_new_toys_autoreplace)); - - for(j = 0; j < n; ++j) - FOREACH(Weapons, it != WEP_Null, LAMBDA( - if(it.netname == argv(j)) - { - WepSet setk = it.m_wepset; - if(start_weapons & seti) newdefault |= setk; - if(warmup_start_weapons & seti) warmup_newdefault |= setk; - } - )); - )); - - newdefault &= start_weapons_defaultmask; - start_weapons &= ~start_weapons_defaultmask; - start_weapons |= newdefault; - - warmup_newdefault &= warmup_start_weapons_defaultmask; - warmup_start_weapons &= ~warmup_start_weapons_defaultmask; - warmup_start_weapons |= warmup_newdefault; -} - -MUTATOR_HOOKFUNCTION(nt, SetWeaponreplace) -{ - entity wep = M_ARGV(0, entity); - entity wepinfo = M_ARGV(1, entity); - string ret_string = M_ARGV(2, string); - - // otherwise, we do replace - if(wep.new_toys) - { - // map defined replacement: - ret_string = wep.new_toys; - } - else - { - // auto replacement: - ret_string = nt_GetReplacement(wepinfo.netname, autocvar_g_new_toys_autoreplace); - } - - // apply regular weaponreplace - ret_string = W_Apply_Weaponreplace(ret_string); - - M_ARGV(2, string) = ret_string; -} - -MUTATOR_HOOKFUNCTION(nt, FilterItem) -{ - entity item = M_ARGV(0, entity); - - if(nt_IsNewToy(item.weapon) && autocvar_g_new_toys_use_pickupsound) { - item.item_pickupsound = string_null; - item.item_pickupsound_ent = SND_WEAPONPICKUP_NEW_TOYS; - } -} -#endif diff --git a/qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qc b/qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qc new file mode 100644 index 0000000000..6c0647b1e6 --- /dev/null +++ b/qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qc @@ -0,0 +1,229 @@ +#include "sv_new_toys.qh" + +/* + +CORE laser vortex lg rl cry gl elec hagar fireb hook + vaporizer porto + tuba + +NEW rifle hlac minel seeker +IDEAS OPEN flak OPEN FUN FUN FUN FUN + + + +How this mutator works: + ======================= + +When a gun tries to spawn, this mutator is called. It will provide alternate +weaponreplace lists. + +Entity: + +{ +"classname" "weapon_vortex" +"new_toys" "rifle" +} +-> This will spawn as Rifle in this mutator ONLY, and as Vortex otherwise. + +{ +"classname" "weapon_vortext" +"new_toys" "vortex rifle" +} +-> This will spawn as either Vortex or Rifle in this mutator ONLY, and as Vortex otherwise. + +{ +"classname" "weapon_vortex" +"new_toys" "vortex" +} +-> This is always a Vortex. + +If the map specifies no "new_toys" argument + +There will be two default replacements selectable: "replace all" and "replace random". +In "replace all" mode, e.g. Vortex will have the default replacement "rifle". +In "replace random" mode, Vortex will have the default replacement "vortex rifle". + +This mutator's replacements run BEFORE regular weaponreplace! + +The New Toys guns do NOT get a spawn function, so they can only ever be spawned +when this mutator is active. + +Likewise, warmup, give all, give ALL and impulse 99 will not give them unless +this mutator is active. + +Outside this mutator, they still can be spawned by: +- setting their start weapon cvar to 1 +- give weaponname +- weaponreplace +- weaponarena (but all and most weapons arena again won't include them) + +This mutator performs the default replacements on the DEFAULTS of the +start weapon selection. + +These weapons appear in the menu's priority list, BUT get a suffix +"(Mutator weapon)". + +Picking up a "new toys" weapon will not play standard weapon pickup sound, but +roflsound "New toys, new toys!" sound. + +*/ + +bool nt_IsNewToy(int w); + +REGISTER_MUTATOR(nt, cvar("g_new_toys") && !cvar("g_instagib") && !cvar("g_overkill")) +{ + MUTATOR_ONADD + { + if(time > 1) // game loads at time 1 + error("This cannot be added at runtime\n"); + + // mark the guns as ok to use by e.g. impulse 99 + FOREACH(Weapons, it != WEP_Null, LAMBDA( + if(nt_IsNewToy(it.m_id)) + it.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; + )); + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + FOREACH(Weapons, it != WEP_Null, LAMBDA( + if(nt_IsNewToy(it.m_id)) + it.spawnflags |= WEP_FLAG_MUTATORBLOCKED; + )); + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This cannot be removed at runtime\n"); + return -1; + } + + return 0; +} + +.string new_toys; + +float autocvar_g_new_toys_autoreplace; +bool autocvar_g_new_toys_use_pickupsound = true; +const float NT_AUTOREPLACE_NEVER = 0; +const float NT_AUTOREPLACE_ALWAYS = 1; +const float NT_AUTOREPLACE_RANDOM = 2; + +MUTATOR_HOOKFUNCTION(nt, SetModname) +{ + M_ARGV(0, string) = "NewToys"; +} + +bool nt_IsNewToy(int w) +{ + switch(w) + { + case WEP_SEEKER.m_id: + case WEP_MINE_LAYER.m_id: + case WEP_HLAC.m_id: + case WEP_RIFLE.m_id: + case WEP_SHOCKWAVE.m_id: + return true; + default: + return false; + } +} + +string nt_GetFullReplacement(string w) +{ + switch(w) + { + case "hagar": return "seeker"; + case "devastator": return "minelayer"; + case "machinegun": return "hlac"; + case "vortex": return "rifle"; + //case "shotgun": return "shockwave"; + default: return string_null; + } +} + +string nt_GetReplacement(string w, float m) +{ + if(m == NT_AUTOREPLACE_NEVER) + return w; + string s = nt_GetFullReplacement(w); + if (!s) + return w; + if(m == NT_AUTOREPLACE_RANDOM) + s = strcat(w, " ", s); + return s; +} + +MUTATOR_HOOKFUNCTION(nt, SetStartItems) +{ + // rearrange start_weapon_default + // apply those bits that are set by start_weapon_defaultmask + // same for warmup + + float j, n; + + WepSet newdefault; + WepSet warmup_newdefault; + + newdefault = '0 0 0'; + warmup_newdefault = '0 0 0'; + + WepSet seti = '0 0 0'; + + FOREACH(Weapons, it != WEP_Null, LAMBDA( + seti = it.m_wepset; + n = tokenize_console(nt_GetReplacement(it.netname, autocvar_g_new_toys_autoreplace)); + + for(j = 0; j < n; ++j) + FOREACH(Weapons, it != WEP_Null, LAMBDA( + if(it.netname == argv(j)) + { + WepSet setk = it.m_wepset; + if(start_weapons & seti) newdefault |= setk; + if(warmup_start_weapons & seti) warmup_newdefault |= setk; + } + )); + )); + + newdefault &= start_weapons_defaultmask; + start_weapons &= ~start_weapons_defaultmask; + start_weapons |= newdefault; + + warmup_newdefault &= warmup_start_weapons_defaultmask; + warmup_start_weapons &= ~warmup_start_weapons_defaultmask; + warmup_start_weapons |= warmup_newdefault; +} + +MUTATOR_HOOKFUNCTION(nt, SetWeaponreplace) +{ + entity wep = M_ARGV(0, entity); + entity wepinfo = M_ARGV(1, entity); + string ret_string = M_ARGV(2, string); + + // otherwise, we do replace + if(wep.new_toys) + { + // map defined replacement: + ret_string = wep.new_toys; + } + else + { + // auto replacement: + ret_string = nt_GetReplacement(wepinfo.netname, autocvar_g_new_toys_autoreplace); + } + + // apply regular weaponreplace + ret_string = W_Apply_Weaponreplace(ret_string); + + M_ARGV(2, string) = ret_string; +} + +MUTATOR_HOOKFUNCTION(nt, FilterItem) +{ + entity item = M_ARGV(0, entity); + + if(nt_IsNewToy(item.weapon) && autocvar_g_new_toys_use_pickupsound) { + item.item_pickupsound = string_null; + item.item_pickupsound_ent = SND_WEAPONPICKUP_NEW_TOYS; + } +} diff --git a/qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qh b/qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/nix/_mod.inc b/qcsrc/common/mutators/mutator/nix/_mod.inc index a669175da2..3c3141d6fb 100644 --- a/qcsrc/common/mutators/mutator/nix/_mod.inc +++ b/qcsrc/common/mutators/mutator/nix/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/nix/_mod.qh b/qcsrc/common/mutators/mutator/nix/_mod.qh index 6c012fe65c..affbae20a9 100644 --- a/qcsrc/common/mutators/mutator/nix/_mod.qh +++ b/qcsrc/common/mutators/mutator/nix/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/nix/module.inc b/qcsrc/common/mutators/mutator/nix/module.inc deleted file mode 100644 index fb4f9ec2bc..0000000000 --- a/qcsrc/common/mutators/mutator/nix/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "nix.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/nix/nix.qc b/qcsrc/common/mutators/mutator/nix/nix.qc deleted file mode 100644 index f52d75a9e9..0000000000 --- a/qcsrc/common/mutators/mutator/nix/nix.qc +++ /dev/null @@ -1,288 +0,0 @@ -#ifdef IMPLEMENTATION -int autocvar_g_balance_nix_ammo_cells; -int autocvar_g_balance_nix_ammo_plasma; -int autocvar_g_balance_nix_ammo_fuel; -int autocvar_g_balance_nix_ammo_nails; -int autocvar_g_balance_nix_ammo_rockets; -int autocvar_g_balance_nix_ammo_shells; -int autocvar_g_balance_nix_ammoincr_cells; -int autocvar_g_balance_nix_ammoincr_plasma; -int autocvar_g_balance_nix_ammoincr_fuel; -int autocvar_g_balance_nix_ammoincr_nails; -int autocvar_g_balance_nix_ammoincr_rockets; -int autocvar_g_balance_nix_ammoincr_shells; -float autocvar_g_balance_nix_incrtime; -float autocvar_g_balance_nix_roundtime; -bool autocvar_g_nix_with_healtharmor; -bool autocvar_g_nix_with_blaster; -bool autocvar_g_nix_with_powerups; -int autocvar_g_pickup_cells_max; -int autocvar_g_pickup_plasma_max; -int autocvar_g_pickup_fuel_max; -int autocvar_g_pickup_nails_max; -int autocvar_g_pickup_rockets_max; -int autocvar_g_pickup_shells_max; - -float g_nix_with_blaster; -// WEAPONTODO -int nix_weapon; -float nix_nextchange; -float nix_nextweapon; -.float nix_lastchange_id; -.float nix_lastinfotime; -.float nix_nextincr; - -bool NIX_CanChooseWeapon(int wpn); - -REGISTER_MUTATOR(nix, cvar("g_nix") && !cvar("g_instagib") && !cvar("g_overkill")) -{ - MUTATOR_ONADD - { - g_nix_with_blaster = autocvar_g_nix_with_blaster; - - nix_nextchange = 0; - nix_nextweapon = 0; - - FOREACH(Weapons, it != WEP_Null && NIX_CanChooseWeapon(it.m_id), LAMBDA(it.wr_init(it))); - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // nothing to roll back - } - - MUTATOR_ONREMOVE - { - // as the PlayerSpawn hook will no longer run, NIX is turned off by this! - FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), { - it.ammo_cells = start_ammo_cells; - it.ammo_plasma = start_ammo_plasma; - it.ammo_shells = start_ammo_shells; - it.ammo_nails = start_ammo_nails; - it.ammo_rockets = start_ammo_rockets; - it.ammo_fuel = start_ammo_fuel; - it.weapons = start_weapons; - if(!client_hasweapon(it, PS(it).m_weapon, true, false)) - PS(it).m_switchweapon = w_getbestweapon(it); - }); - } - - return false; -} - -bool NIX_CanChooseWeapon(int wpn) -{ - entity e = Weapons_from(wpn); - if (e == WEP_Null) return false; // skip dummies - if(g_weaponarena) - { - if(!(g_weaponarena_weapons & e.m_wepset)) - return false; - } - else - { - if(wpn == WEP_BLASTER.m_id && g_nix_with_blaster) - return false; - if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED) - return false; - if (!(e.spawnflags & WEP_FLAG_NORMAL)) - return false; - } - return true; -} -void NIX_ChooseNextWeapon() -{ - RandomSelection_Init(); - FOREACH(Weapons, it != WEP_Null, LAMBDA( - if(NIX_CanChooseWeapon(it.m_id)) - RandomSelection_Add(NULL, it.m_id, string_null, 1, (it.m_id != nix_weapon)); - )); - nix_nextweapon = RandomSelection_chosen_float; -} - -void NIX_GiveCurrentWeapon(entity this) -{ - float dt; - - if(!nix_nextweapon) - NIX_ChooseNextWeapon(); - - dt = ceil(nix_nextchange - time); - - if(dt <= 0) - { - nix_weapon = nix_nextweapon; - nix_nextweapon = 0; - if (!nix_nextchange) // no round played yet? - nix_nextchange = time; // start the first round now! - else - nix_nextchange = time + autocvar_g_balance_nix_roundtime; - // Weapon w = Weapons_from(nix_weapon); - // w.wr_init(w); // forget it, too slow - } - - // get weapon info - entity e = Weapons_from(nix_weapon); - - if(nix_nextchange != this.nix_lastchange_id) // this shall only be called once per round! - { - this.ammo_shells = this.ammo_nails = this.ammo_rockets = this.ammo_cells = this.ammo_plasma = this.ammo_fuel = 0; - - if(this.items & IT_UNLIMITED_WEAPON_AMMO) - { - switch(e.ammo_field) - { - case ammo_shells: this.ammo_shells = autocvar_g_pickup_shells_max; break; - case ammo_nails: this.ammo_nails = autocvar_g_pickup_nails_max; break; - case ammo_rockets: this.ammo_rockets = autocvar_g_pickup_rockets_max; break; - case ammo_cells: this.ammo_cells = autocvar_g_pickup_cells_max; break; - case ammo_plasma: this.ammo_plasma = autocvar_g_pickup_plasma_max; break; - case ammo_fuel: this.ammo_fuel = autocvar_g_pickup_fuel_max; break; - } - } - else - { - switch(e.ammo_field) - { - case ammo_shells: this.ammo_shells = autocvar_g_balance_nix_ammo_shells; break; - case ammo_nails: this.ammo_nails = autocvar_g_balance_nix_ammo_nails; break; - case ammo_rockets: this.ammo_rockets = autocvar_g_balance_nix_ammo_rockets; break; - case ammo_cells: this.ammo_cells = autocvar_g_balance_nix_ammo_cells; break; - case ammo_plasma: this.ammo_plasma = autocvar_g_balance_nix_ammo_plasma; break; - case ammo_fuel: this.ammo_fuel = autocvar_g_balance_nix_ammo_fuel; break; - } - } - - this.nix_nextincr = time + autocvar_g_balance_nix_incrtime; - if(dt >= 1 && dt <= 5) - this.nix_lastinfotime = -42; - else - Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_NIX_NEWWEAPON, nix_weapon); - - e.wr_resetplayer(e, this); - - // all weapons must be fully loaded when we spawn - if(e.spawnflags & WEP_FLAG_RELOADABLE) // prevent accessing undefined cvars - this.(weapon_load[nix_weapon]) = e.reloading_ammo; - - // vortex too - if(WEP_CVAR(vortex, charge)) - { - if(WEP_CVAR_SEC(vortex, chargepool)) - this.vortex_chargepool_ammo = 1; - this.vortex_charge = WEP_CVAR(vortex, charge_start); - } - - // set last change info - this.nix_lastchange_id = nix_nextchange; - } - if(this.nix_lastinfotime != dt) - { - this.nix_lastinfotime = dt; // initial value 0 should count as "not seen" - if(dt >= 1 && dt <= 5) - Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_NIX_COUNTDOWN, nix_nextweapon, dt); - } - - if(!(this.items & IT_UNLIMITED_WEAPON_AMMO) && time > this.nix_nextincr) - { - switch(e.ammo_field) - { - case ammo_shells: this.ammo_shells += autocvar_g_balance_nix_ammoincr_shells; break; - case ammo_nails: this.ammo_nails += autocvar_g_balance_nix_ammoincr_nails; break; - case ammo_rockets: this.ammo_rockets += autocvar_g_balance_nix_ammoincr_rockets; break; - case ammo_cells: this.ammo_cells += autocvar_g_balance_nix_ammoincr_cells; break; - case ammo_plasma: this.ammo_plasma += autocvar_g_balance_nix_ammoincr_plasma; break; - case ammo_fuel: this.ammo_fuel += autocvar_g_balance_nix_ammoincr_fuel; break; - } - - this.nix_nextincr = time + autocvar_g_balance_nix_incrtime; - } - - this.weapons = '0 0 0'; - if(g_nix_with_blaster) - this.weapons |= WEPSET(BLASTER); - this.weapons |= e.m_wepset; - - Weapon w = Weapons_from(nix_weapon); - if(PS(this).m_switchweapon != w) - if(!client_hasweapon(this, PS(this).m_switchweapon, true, false)) - { - if(client_hasweapon(this, w, true, false)) - W_SwitchWeapon(this, w); - } -} - -MUTATOR_HOOKFUNCTION(nix, ForbidThrowCurrentWeapon) -{ - return true; // no throwing in NIX -} - -MUTATOR_HOOKFUNCTION(nix, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":NIX"); -} - -MUTATOR_HOOKFUNCTION(nix, BuildMutatorsPrettyString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", NIX"); -} - -MUTATOR_HOOKFUNCTION(nix, FilterItem) -{ - entity item = M_ARGV(0, entity); - - switch (item.items) - { - case ITEM_HealthSmall.m_itemid: - case ITEM_HealthMedium.m_itemid: - case ITEM_HealthLarge.m_itemid: - case ITEM_HealthMega.m_itemid: - case ITEM_ArmorSmall.m_itemid: - case ITEM_ArmorMedium.m_itemid: - case ITEM_ArmorLarge.m_itemid: - case ITEM_ArmorMega.m_itemid: - if (autocvar_g_nix_with_healtharmor) - return false; - break; - case ITEM_Strength.m_itemid: - case ITEM_Shield.m_itemid: - if (autocvar_g_nix_with_powerups) - return false; - break; - } - - return true; // delete all other items -} - -MUTATOR_HOOKFUNCTION(nix, OnEntityPreSpawn) -{ - entity ent = M_ARGV(0, entity); - - if(ent.classname == "target_items") // items triggers cannot work in nix (as they change weapons/ammo) - return true; -} - -MUTATOR_HOOKFUNCTION(nix, PlayerPreThink) -{ - entity player = M_ARGV(0, entity); - - if(!intermission_running) - if(!IS_DEAD(player)) - if(IS_PLAYER(player)) - NIX_GiveCurrentWeapon(player); -} - -MUTATOR_HOOKFUNCTION(nix, PlayerSpawn) -{ - entity player = M_ARGV(0, entity); - - player.nix_lastchange_id = -1; - NIX_GiveCurrentWeapon(player); // overrides the weapons you got when spawning - player.items |= IT_UNLIMITED_SUPERWEAPONS; -} - -MUTATOR_HOOKFUNCTION(nix, SetModname, CBC_ORDER_LAST) -{ - M_ARGV(0, string) = "NIX"; -} -#endif diff --git a/qcsrc/common/mutators/mutator/nix/sv_nix.qc b/qcsrc/common/mutators/mutator/nix/sv_nix.qc new file mode 100644 index 0000000000..97ed4361ba --- /dev/null +++ b/qcsrc/common/mutators/mutator/nix/sv_nix.qc @@ -0,0 +1,288 @@ +#include "sv_nix.qh" + +int autocvar_g_balance_nix_ammo_cells; +int autocvar_g_balance_nix_ammo_plasma; +int autocvar_g_balance_nix_ammo_fuel; +int autocvar_g_balance_nix_ammo_nails; +int autocvar_g_balance_nix_ammo_rockets; +int autocvar_g_balance_nix_ammo_shells; +int autocvar_g_balance_nix_ammoincr_cells; +int autocvar_g_balance_nix_ammoincr_plasma; +int autocvar_g_balance_nix_ammoincr_fuel; +int autocvar_g_balance_nix_ammoincr_nails; +int autocvar_g_balance_nix_ammoincr_rockets; +int autocvar_g_balance_nix_ammoincr_shells; +float autocvar_g_balance_nix_incrtime; +float autocvar_g_balance_nix_roundtime; +bool autocvar_g_nix_with_healtharmor; +bool autocvar_g_nix_with_blaster; +bool autocvar_g_nix_with_powerups; +int autocvar_g_pickup_cells_max; +int autocvar_g_pickup_plasma_max; +int autocvar_g_pickup_fuel_max; +int autocvar_g_pickup_nails_max; +int autocvar_g_pickup_rockets_max; +int autocvar_g_pickup_shells_max; + +float g_nix_with_blaster; +// WEAPONTODO +int nix_weapon; +float nix_nextchange; +float nix_nextweapon; +.float nix_lastchange_id; +.float nix_lastinfotime; +.float nix_nextincr; + +bool NIX_CanChooseWeapon(int wpn); + +REGISTER_MUTATOR(nix, cvar("g_nix") && !cvar("g_instagib") && !cvar("g_overkill")) +{ + MUTATOR_ONADD + { + g_nix_with_blaster = autocvar_g_nix_with_blaster; + + nix_nextchange = 0; + nix_nextweapon = 0; + + FOREACH(Weapons, it != WEP_Null && NIX_CanChooseWeapon(it.m_id), LAMBDA(it.wr_init(it))); + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // nothing to roll back + } + + MUTATOR_ONREMOVE + { + // as the PlayerSpawn hook will no longer run, NIX is turned off by this! + FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), { + it.ammo_cells = start_ammo_cells; + it.ammo_plasma = start_ammo_plasma; + it.ammo_shells = start_ammo_shells; + it.ammo_nails = start_ammo_nails; + it.ammo_rockets = start_ammo_rockets; + it.ammo_fuel = start_ammo_fuel; + it.weapons = start_weapons; + if(!client_hasweapon(it, PS(it).m_weapon, true, false)) + PS(it).m_switchweapon = w_getbestweapon(it); + }); + } + + return false; +} + +bool NIX_CanChooseWeapon(int wpn) +{ + entity e = Weapons_from(wpn); + if (e == WEP_Null) return false; // skip dummies + if(g_weaponarena) + { + if(!(g_weaponarena_weapons & e.m_wepset)) + return false; + } + else + { + if(wpn == WEP_BLASTER.m_id && g_nix_with_blaster) + return false; + if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED) + return false; + if (!(e.spawnflags & WEP_FLAG_NORMAL)) + return false; + } + return true; +} +void NIX_ChooseNextWeapon() +{ + RandomSelection_Init(); + FOREACH(Weapons, it != WEP_Null, LAMBDA( + if(NIX_CanChooseWeapon(it.m_id)) + RandomSelection_Add(NULL, it.m_id, string_null, 1, (it.m_id != nix_weapon)); + )); + nix_nextweapon = RandomSelection_chosen_float; +} + +void NIX_GiveCurrentWeapon(entity this) +{ + float dt; + + if(!nix_nextweapon) + NIX_ChooseNextWeapon(); + + dt = ceil(nix_nextchange - time); + + if(dt <= 0) + { + nix_weapon = nix_nextweapon; + nix_nextweapon = 0; + if (!nix_nextchange) // no round played yet? + nix_nextchange = time; // start the first round now! + else + nix_nextchange = time + autocvar_g_balance_nix_roundtime; + // Weapon w = Weapons_from(nix_weapon); + // w.wr_init(w); // forget it, too slow + } + + // get weapon info + entity e = Weapons_from(nix_weapon); + + if(nix_nextchange != this.nix_lastchange_id) // this shall only be called once per round! + { + this.ammo_shells = this.ammo_nails = this.ammo_rockets = this.ammo_cells = this.ammo_plasma = this.ammo_fuel = 0; + + if(this.items & IT_UNLIMITED_WEAPON_AMMO) + { + switch(e.ammo_field) + { + case ammo_shells: this.ammo_shells = autocvar_g_pickup_shells_max; break; + case ammo_nails: this.ammo_nails = autocvar_g_pickup_nails_max; break; + case ammo_rockets: this.ammo_rockets = autocvar_g_pickup_rockets_max; break; + case ammo_cells: this.ammo_cells = autocvar_g_pickup_cells_max; break; + case ammo_plasma: this.ammo_plasma = autocvar_g_pickup_plasma_max; break; + case ammo_fuel: this.ammo_fuel = autocvar_g_pickup_fuel_max; break; + } + } + else + { + switch(e.ammo_field) + { + case ammo_shells: this.ammo_shells = autocvar_g_balance_nix_ammo_shells; break; + case ammo_nails: this.ammo_nails = autocvar_g_balance_nix_ammo_nails; break; + case ammo_rockets: this.ammo_rockets = autocvar_g_balance_nix_ammo_rockets; break; + case ammo_cells: this.ammo_cells = autocvar_g_balance_nix_ammo_cells; break; + case ammo_plasma: this.ammo_plasma = autocvar_g_balance_nix_ammo_plasma; break; + case ammo_fuel: this.ammo_fuel = autocvar_g_balance_nix_ammo_fuel; break; + } + } + + this.nix_nextincr = time + autocvar_g_balance_nix_incrtime; + if(dt >= 1 && dt <= 5) + this.nix_lastinfotime = -42; + else + Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_NIX_NEWWEAPON, nix_weapon); + + e.wr_resetplayer(e, this); + + // all weapons must be fully loaded when we spawn + if(e.spawnflags & WEP_FLAG_RELOADABLE) // prevent accessing undefined cvars + this.(weapon_load[nix_weapon]) = e.reloading_ammo; + + // vortex too + if(WEP_CVAR(vortex, charge)) + { + if(WEP_CVAR_SEC(vortex, chargepool)) + this.vortex_chargepool_ammo = 1; + this.vortex_charge = WEP_CVAR(vortex, charge_start); + } + + // set last change info + this.nix_lastchange_id = nix_nextchange; + } + if(this.nix_lastinfotime != dt) + { + this.nix_lastinfotime = dt; // initial value 0 should count as "not seen" + if(dt >= 1 && dt <= 5) + Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_NIX_COUNTDOWN, nix_nextweapon, dt); + } + + if(!(this.items & IT_UNLIMITED_WEAPON_AMMO) && time > this.nix_nextincr) + { + switch(e.ammo_field) + { + case ammo_shells: this.ammo_shells += autocvar_g_balance_nix_ammoincr_shells; break; + case ammo_nails: this.ammo_nails += autocvar_g_balance_nix_ammoincr_nails; break; + case ammo_rockets: this.ammo_rockets += autocvar_g_balance_nix_ammoincr_rockets; break; + case ammo_cells: this.ammo_cells += autocvar_g_balance_nix_ammoincr_cells; break; + case ammo_plasma: this.ammo_plasma += autocvar_g_balance_nix_ammoincr_plasma; break; + case ammo_fuel: this.ammo_fuel += autocvar_g_balance_nix_ammoincr_fuel; break; + } + + this.nix_nextincr = time + autocvar_g_balance_nix_incrtime; + } + + this.weapons = '0 0 0'; + if(g_nix_with_blaster) + this.weapons |= WEPSET(BLASTER); + this.weapons |= e.m_wepset; + + Weapon w = Weapons_from(nix_weapon); + if(PS(this).m_switchweapon != w) + if(!client_hasweapon(this, PS(this).m_switchweapon, true, false)) + { + if(client_hasweapon(this, w, true, false)) + W_SwitchWeapon(this, w); + } +} + +MUTATOR_HOOKFUNCTION(nix, ForbidThrowCurrentWeapon) +{ + return true; // no throwing in NIX +} + +MUTATOR_HOOKFUNCTION(nix, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":NIX"); +} + +MUTATOR_HOOKFUNCTION(nix, BuildMutatorsPrettyString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", NIX"); +} + +MUTATOR_HOOKFUNCTION(nix, FilterItem) +{ + entity item = M_ARGV(0, entity); + + switch (item.items) + { + case ITEM_HealthSmall.m_itemid: + case ITEM_HealthMedium.m_itemid: + case ITEM_HealthLarge.m_itemid: + case ITEM_HealthMega.m_itemid: + case ITEM_ArmorSmall.m_itemid: + case ITEM_ArmorMedium.m_itemid: + case ITEM_ArmorLarge.m_itemid: + case ITEM_ArmorMega.m_itemid: + if (autocvar_g_nix_with_healtharmor) + return false; + break; + case ITEM_Strength.m_itemid: + case ITEM_Shield.m_itemid: + if (autocvar_g_nix_with_powerups) + return false; + break; + } + + return true; // delete all other items +} + +MUTATOR_HOOKFUNCTION(nix, OnEntityPreSpawn) +{ + entity ent = M_ARGV(0, entity); + + if(ent.classname == "target_items") // items triggers cannot work in nix (as they change weapons/ammo) + return true; +} + +MUTATOR_HOOKFUNCTION(nix, PlayerPreThink) +{ + entity player = M_ARGV(0, entity); + + if(!intermission_running) + if(!IS_DEAD(player)) + if(IS_PLAYER(player)) + NIX_GiveCurrentWeapon(player); +} + +MUTATOR_HOOKFUNCTION(nix, PlayerSpawn) +{ + entity player = M_ARGV(0, entity); + + player.nix_lastchange_id = -1; + NIX_GiveCurrentWeapon(player); // overrides the weapons you got when spawning + player.items |= IT_UNLIMITED_SUPERWEAPONS; +} + +MUTATOR_HOOKFUNCTION(nix, SetModname, CBC_ORDER_LAST) +{ + M_ARGV(0, string) = "NIX"; +} diff --git a/qcsrc/common/mutators/mutator/nix/sv_nix.qh b/qcsrc/common/mutators/mutator/nix/sv_nix.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/nix/sv_nix.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/overkill/_mod.inc b/qcsrc/common/mutators/mutator/overkill/_mod.inc index 5b42a4dd11..0552173c1a 100644 --- a/qcsrc/common/mutators/mutator/overkill/_mod.inc +++ b/qcsrc/common/mutators/mutator/overkill/_mod.inc @@ -1,4 +1,10 @@ // generated file; do not modify #include #include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif #include diff --git a/qcsrc/common/mutators/mutator/overkill/_mod.qh b/qcsrc/common/mutators/mutator/overkill/_mod.qh index 7a46694446..13e42431b3 100644 --- a/qcsrc/common/mutators/mutator/overkill/_mod.qh +++ b/qcsrc/common/mutators/mutator/overkill/_mod.qh @@ -1,4 +1,10 @@ // generated file; do not modify #include #include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif #include diff --git a/qcsrc/common/mutators/mutator/overkill/cl_overkill.qc b/qcsrc/common/mutators/mutator/overkill/cl_overkill.qc new file mode 100644 index 0000000000..eb21953955 --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/cl_overkill.qc @@ -0,0 +1,11 @@ +#include "cl_overkill.qh" + +REGISTER_MUTATOR(ok, false) +{ + MUTATOR_ONADD { + cvar_settemp("g_overkill", "1"); + WEP_SHOTGUN.mdl = "ok_shotgun"; + WEP_MACHINEGUN.mdl = "ok_mg"; + WEP_VORTEX.mdl = "ok_sniper"; + } +} diff --git a/qcsrc/common/mutators/mutator/overkill/cl_overkill.qh b/qcsrc/common/mutators/mutator/overkill/cl_overkill.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/cl_overkill.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/overkill/hmg.qc b/qcsrc/common/mutators/mutator/overkill/hmg.qc index 1512992c25..024fdb1cc7 100644 --- a/qcsrc/common/mutators/mutator/overkill/hmg.qc +++ b/qcsrc/common/mutators/mutator/overkill/hmg.qc @@ -1,47 +1,5 @@ -#ifndef IMPLEMENTATION -CLASS(HeavyMachineGun, Weapon) -/* ammotype */ ATTRIB(HeavyMachineGun, ammo_field, .int, ammo_nails); -/* impulse */ ATTRIB(HeavyMachineGun, impulse, int, 3); -/* flags */ ATTRIB(HeavyMachineGun, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN | WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_FLAG_SUPERWEAPON); -/* rating */ ATTRIB(HeavyMachineGun, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH); -/* color */ ATTRIB(HeavyMachineGun, wpcolor, vector, '0.5 0.5 0'); -/* modelname */ ATTRIB(HeavyMachineGun, mdl, string, "ok_hmg"); -#ifndef MENUQC -/* model */ ATTRIB(HeavyMachineGun, m_model, Model, MDL_HMG_ITEM); -#endif -/* crosshair */ ATTRIB(HeavyMachineGun, w_crosshair, string, "gfx/crosshairuzi"); -/* crosshair */ ATTRIB(HeavyMachineGun, w_crosshair_size, float, 0.6); -/* wepimg */ ATTRIB(HeavyMachineGun, model2, string, "weaponhmg"); -/* refname */ ATTRIB(HeavyMachineGun, netname, string, "hmg"); -/* wepname */ ATTRIB(HeavyMachineGun, m_name, string, _("Heavy Machine Gun")); - -#define X(BEGIN, P, END, class, prefix) \ - BEGIN(class) \ - P(class, prefix, ammo, float, NONE) \ - P(class, prefix, damage, float, NONE) \ - P(class, prefix, force, float, NONE) \ - P(class, prefix, refire, float, NONE) \ - P(class, prefix, reload_ammo, float, NONE) \ - P(class, prefix, reload_time, float, NONE) \ - P(class, prefix, solidpenetration, float, NONE) \ - P(class, prefix, spread_add, float, NONE) \ - P(class, prefix, spread_max, float, NONE) \ - P(class, prefix, spread_min, float, NONE) \ - P(class, prefix, switchdelay_drop, float, NONE) \ - P(class, prefix, switchdelay_raise, float, NONE) \ - P(class, prefix, weaponreplace, string, NONE) \ - P(class, prefix, weaponstartoverride, float, NONE) \ - P(class, prefix, weaponstart, float, NONE) \ - P(class, prefix, weaponthrowable, float, NONE) \ - END() - W_PROPS(X, HeavyMachineGun, hmg) -#undef X - -ENDCLASS(HeavyMachineGun) -REGISTER_WEAPON(HMG, hmg, NEW(HeavyMachineGun)); +#include "hmg.qh" -#endif -#ifdef IMPLEMENTATION #ifdef SVQC REGISTER_MUTATOR(hmg_nadesupport, true); @@ -174,4 +132,3 @@ METHOD(HeavyMachineGun, wr_impacteffect, void(entity thiswep, entity actor)) } #endif -#endif diff --git a/qcsrc/common/mutators/mutator/overkill/hmg.qh b/qcsrc/common/mutators/mutator/overkill/hmg.qh new file mode 100644 index 0000000000..7219fd1f80 --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/hmg.qh @@ -0,0 +1,42 @@ +#pragma once + +CLASS(HeavyMachineGun, Weapon) +/* ammotype */ ATTRIB(HeavyMachineGun, ammo_field, .int, ammo_nails); +/* impulse */ ATTRIB(HeavyMachineGun, impulse, int, 3); +/* flags */ ATTRIB(HeavyMachineGun, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN | WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_FLAG_SUPERWEAPON); +/* rating */ ATTRIB(HeavyMachineGun, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH); +/* color */ ATTRIB(HeavyMachineGun, wpcolor, vector, '0.5 0.5 0'); +/* modelname */ ATTRIB(HeavyMachineGun, mdl, string, "ok_hmg"); +#ifdef GAMEQC +/* model */ ATTRIB(HeavyMachineGun, m_model, Model, MDL_HMG_ITEM); +#endif +/* crosshair */ ATTRIB(HeavyMachineGun, w_crosshair, string, "gfx/crosshairuzi"); +/* crosshair */ ATTRIB(HeavyMachineGun, w_crosshair_size, float, 0.6); +/* wepimg */ ATTRIB(HeavyMachineGun, model2, string, "weaponhmg"); +/* refname */ ATTRIB(HeavyMachineGun, netname, string, "hmg"); +/* wepname */ ATTRIB(HeavyMachineGun, m_name, string, _("Heavy Machine Gun")); + +#define X(BEGIN, P, END, class, prefix) \ + BEGIN(class) \ + P(class, prefix, ammo, float, NONE) \ + P(class, prefix, damage, float, NONE) \ + P(class, prefix, force, float, NONE) \ + P(class, prefix, refire, float, NONE) \ + P(class, prefix, reload_ammo, float, NONE) \ + P(class, prefix, reload_time, float, NONE) \ + P(class, prefix, solidpenetration, float, NONE) \ + P(class, prefix, spread_add, float, NONE) \ + P(class, prefix, spread_max, float, NONE) \ + P(class, prefix, spread_min, float, NONE) \ + P(class, prefix, switchdelay_drop, float, NONE) \ + P(class, prefix, switchdelay_raise, float, NONE) \ + P(class, prefix, weaponreplace, string, NONE) \ + P(class, prefix, weaponstartoverride, float, NONE) \ + P(class, prefix, weaponstart, float, NONE) \ + P(class, prefix, weaponthrowable, float, NONE) \ + END() + W_PROPS(X, HeavyMachineGun, hmg) +#undef X + +ENDCLASS(HeavyMachineGun) +REGISTER_WEAPON(HMG, hmg, NEW(HeavyMachineGun)); diff --git a/qcsrc/common/mutators/mutator/overkill/module.inc b/qcsrc/common/mutators/mutator/overkill/module.inc deleted file mode 100644 index a7acff5a09..0000000000 --- a/qcsrc/common/mutators/mutator/overkill/module.inc +++ /dev/null @@ -1,19 +0,0 @@ -#include "hmg.qc" -#include "rpc.qc" - -#ifdef SVQC - #include "overkill.qc" -#endif -#ifdef CSQC - #ifdef IMPLEMENTATION - REGISTER_MUTATOR(ok, false) - { - MUTATOR_ONADD { - cvar_settemp("g_overkill", "1"); - WEP_SHOTGUN.mdl = "ok_shotgun"; - WEP_MACHINEGUN.mdl = "ok_mg"; - WEP_VORTEX.mdl = "ok_sniper"; - } - } - #endif -#endif diff --git a/qcsrc/common/mutators/mutator/overkill/overkill.qc b/qcsrc/common/mutators/mutator/overkill/overkill.qc index f49fdc11b4..3cb64ce923 100644 --- a/qcsrc/common/mutators/mutator/overkill/overkill.qc +++ b/qcsrc/common/mutators/mutator/overkill/overkill.qc @@ -1,410 +1 @@ -#ifdef IMPLEMENTATION -bool autocvar_g_overkill_powerups_replace; -float autocvar_g_overkill_superguns_respawn_time; -bool autocvar_g_overkill_100h_anyway; -bool autocvar_g_overkill_100a_anyway; -bool autocvar_g_overkill_ammo_charge; -float autocvar_g_overkill_ammo_charge_notice; -float autocvar_g_overkill_ammo_charge_limit; - -.vector ok_deathloc; -.float ok_spawnsys_timer; -.float ok_lastwep; -.float ok_item; - -.float ok_notice_time; -.float ammo_charge[Weapons_MAX]; -.float ok_use_ammocharge = _STAT(OK_AMMO_CHARGE); -.float ok_ammo_charge = _STAT(OK_AMMO_CHARGEPOOL); - -.float ok_pauseregen_finished; - -void(entity ent, float wep) ok_DecreaseCharge; - -void ok_Initialize(); - -REGISTER_MUTATOR(ok, cvar("g_overkill") && !cvar("g_instagib") && !g_nexball && cvar_string("g_mod_balance") == "Overkill") -{ - MUTATOR_ONADD - { - ok_Initialize(); - } - - MUTATOR_ONREMOVE - { - WEP_RPC.spawnflags |= WEP_FLAG_MUTATORBLOCKED; - WEP_HMG.spawnflags |= WEP_FLAG_MUTATORBLOCKED; - } -} - -MUTATOR_HOOKFUNCTION(ok, W_DecreaseAmmo) -{ - entity actor = M_ARGV(0, entity); - if (actor.ok_use_ammocharge) - { - ok_DecreaseCharge(actor, PS(actor).m_weapon.m_id); - return true; - } -} - -MUTATOR_HOOKFUNCTION(ok, W_Reload) -{ - entity actor = M_ARGV(0, entity); - return actor.ok_use_ammocharge; -} - -void W_Blaster_Attack(entity, .entity, float, float, float, float, float, float, float, float, float, float); -spawnfunc(weapon_hmg); -spawnfunc(weapon_rpc); - -void ok_DecreaseCharge(entity ent, int wep) -{ - if(!ent.ok_use_ammocharge) return; - - entity wepent = Weapons_from(wep); - - if (wepent == WEP_Null) return; // dummy - - ent.ammo_charge[wep] -= max(0, cvar(sprintf("g_overkill_ammo_decharge_%s", wepent.netname))); -} - -void ok_IncreaseCharge(entity ent, int wep) -{ - entity wepent = Weapons_from(wep); - - if (wepent == WEP_Null) return; // dummy - - if(ent.ok_use_ammocharge) - if(!PHYS_INPUT_BUTTON_ATCK(ent)) // not while attacking? - ent.ammo_charge[wep] = min(autocvar_g_overkill_ammo_charge_limit, ent.ammo_charge[wep] + cvar(sprintf("g_overkill_ammo_charge_rate_%s", wepent.netname)) * frametime / W_TICSPERFRAME); -} - -float ok_CheckWeaponCharge(entity ent, int wep) -{ - if(!ent.ok_use_ammocharge) return true; - - entity wepent = Weapons_from(wep); - - if(wepent == WEP_Null) return false; // dummy - - return (ent.ammo_charge[wep] >= cvar(sprintf("g_overkill_ammo_decharge_%s", wepent.netname))); -} - -MUTATOR_HOOKFUNCTION(ok, PlayerDamage_Calculate, CBC_ORDER_LAST) -{ - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - float frag_deathtype = M_ARGV(3, float); - - if(IS_PLAYER(frag_attacker) && IS_PLAYER(frag_target)) - if(DEATH_ISWEAPON(frag_deathtype, WEP_BLASTER)) - { - if(frag_attacker != frag_target) - if(frag_target.health > 0) - if(STAT(FROZEN, frag_target) == 0) - if(!IS_DEAD(frag_target)) - { - Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_SECONDARY_NODAMAGE); - M_ARGV(6, vector) = '0 0 0'; - } - - M_ARGV(4, float) = 0; - } -} - -MUTATOR_HOOKFUNCTION(ok, PlayerDamage_SplitHealthArmor) -{ - entity frag_target = M_ARGV(2, entity); - float damage_take = M_ARGV(4, float); - - if(damage_take) - frag_target.ok_pauseregen_finished = max(frag_target.ok_pauseregen_finished, time + 2); -} - -void ok_DropItem(entity this, entity targ) -{ - entity e = new(droppedweapon); // hax - e.ok_item = true; - e.noalign = true; - e.pickup_anyway = true; - e.spawnfunc_checked = true; - spawnfunc_item_armor_small(e); - if (!wasfreed(e)) { // might have been blocked by a mutator - set_movetype(e, MOVETYPE_TOSS); - e.gravity = 1; - e.reset = SUB_Remove; - setorigin(e, this.origin + '0 0 32'); - e.velocity = '0 0 200' + normalize(targ.origin - this.origin) * 500; - SUB_SetFade(e, time + 5, 1); - } -} - -MUTATOR_HOOKFUNCTION(ok, PlayerDies) -{ - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - - entity targ = ((frag_attacker) ? frag_attacker : frag_target); - - ok_DropItem(frag_target, targ); - - frag_target.ok_lastwep = PS(frag_target).m_switchweapon.m_id; -} - -MUTATOR_HOOKFUNCTION(ok, MonsterDropItem) -{ - entity mon = M_ARGV(0, entity); - entity olditem = M_ARGV(1, entity); - entity frag_attacker = M_ARGV(2, entity); - - delete(olditem); - - M_ARGV(1, entity) = NULL; - - ok_DropItem(mon, frag_attacker); -} - -MUTATOR_HOOKFUNCTION(ok, PlayerRegen) -{ - entity player = M_ARGV(0, entity); - - // overkill's values are different, so use custom regen - if(!STAT(FROZEN, player)) - { - player.armorvalue = CalcRotRegen(player.armorvalue, autocvar_g_balance_armor_regenstable, autocvar_g_balance_armor_regen, autocvar_g_balance_armor_regenlinear, - 1 * frametime * (time > player.ok_pauseregen_finished), 0, 0, 1, 1 * frametime * (time > player.pauserotarmor_finished), autocvar_g_balance_armor_limit); - player.health = CalcRotRegen(player.health, autocvar_g_balance_health_regenstable, 0, 100, 1 * frametime * (time > player.ok_pauseregen_finished), 200, 0, - autocvar_g_balance_health_rotlinear, 1 * frametime * (time > player.pauserothealth_finished), autocvar_g_balance_health_limit); - - float minf, maxf, limitf; - - maxf = autocvar_g_balance_fuel_rotstable; - minf = autocvar_g_balance_fuel_regenstable; - limitf = autocvar_g_balance_fuel_limit; - - player.ammo_fuel = CalcRotRegen(player.ammo_fuel, minf, autocvar_g_balance_fuel_regen, autocvar_g_balance_fuel_regenlinear, - frametime * (time > player.pauseregen_finished) * ((player.items & ITEM_JetpackRegen.m_itemid) != 0), maxf, autocvar_g_balance_fuel_rot, autocvar_g_balance_fuel_rotlinear, frametime * (time > player.pauserotfuel_finished), limitf); - } - return true; // return true anyway, as frozen uses no regen -} - -MUTATOR_HOOKFUNCTION(ok, ForbidThrowCurrentWeapon) -{ - return true; -} - -MUTATOR_HOOKFUNCTION(ok, PlayerPreThink) -{ - if(intermission_running || gameover) - return; - - entity player = M_ARGV(0, entity); - - if(IS_DEAD(player) || !IS_PLAYER(player) || STAT(FROZEN, player)) - return; - - if(player.ok_lastwep) - { - Weapon newwep = Weapons_from(player.ok_lastwep); - if(player.ok_lastwep == WEP_HMG.m_id) - newwep = WEP_MACHINEGUN; - if(player.ok_lastwep == WEP_RPC.m_id) - newwep = WEP_VORTEX; - PS(player).m_switchweapon = newwep; - player.ok_lastwep = 0; - } - - ok_IncreaseCharge(player, PS(player).m_weapon.m_id); - - if(PHYS_INPUT_BUTTON_ATCK2(player)) - if(!forbidWeaponUse(player) || player.weapon_blocked) // allow if weapon is blocked - if(time >= player.jump_interval) - { - player.jump_interval = time + WEP_CVAR_PRI(blaster, refire) * W_WeaponRateFactor(player); - makevectors(player.v_angle); - - Weapon oldwep = PS(player).m_weapon; - PS(player).m_weapon = WEP_BLASTER; - W_Blaster_Attack( - player, - weaponentities[0], // TODO: unhardcode - WEP_BLASTER.m_id | HITTYPE_SECONDARY, - WEP_CVAR_SEC(vaporizer, shotangle), - WEP_CVAR_SEC(vaporizer, damage), - WEP_CVAR_SEC(vaporizer, edgedamage), - WEP_CVAR_SEC(vaporizer, radius), - WEP_CVAR_SEC(vaporizer, force), - WEP_CVAR_SEC(vaporizer, speed), - WEP_CVAR_SEC(vaporizer, spread), - WEP_CVAR_SEC(vaporizer, delay), - WEP_CVAR_SEC(vaporizer, lifetime) - ); - PS(player).m_weapon = oldwep; - } - - player.weapon_blocked = false; - - player.ok_ammo_charge = player.ammo_charge[PS(player).m_weapon.m_id]; - - if(player.ok_use_ammocharge) - if(!ok_CheckWeaponCharge(player, PS(player).m_weapon.m_id)) - { - if(autocvar_g_overkill_ammo_charge_notice && time > player.ok_notice_time && PHYS_INPUT_BUTTON_ATCK(player) && IS_REAL_CLIENT(player) && PS(player).m_weapon == PS(player).m_switchweapon) - { - //Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_OVERKILL_CHARGE); - player.ok_notice_time = time + 2; - play2(player, SND(DRYFIRE)); - } - Weapon wpn = PS(player).m_weapon; - .entity weaponentity = weaponentities[0]; // TODO: unhardcode - if(player.(weaponentity).state != WS_CLEAR) - w_ready(wpn, player, weaponentity, PHYS_INPUT_BUTTON_ATCK(player) | (PHYS_INPUT_BUTTON_ATCK2(player) << 1)); - - player.weapon_blocked = true; - } - - PHYS_INPUT_BUTTON_ATCK2(player) = false; -} - -MUTATOR_HOOKFUNCTION(ok, PlayerSpawn) -{ - entity player = M_ARGV(0, entity); - - if(autocvar_g_overkill_ammo_charge) - { - FOREACH(Weapons, it != WEP_Null, LAMBDA(player.ammo_charge[it.m_id] = autocvar_g_overkill_ammo_charge_limit)); - - player.ok_use_ammocharge = 1; - player.ok_notice_time = time; - } - else - player.ok_use_ammocharge = 0; - - // if player changed their weapon while dead, don't switch to their death weapon - if(player.impulse) - player.ok_lastwep = 0; - - player.ok_pauseregen_finished = time + 2; -} - -void self_spawnfunc_weapon_hmg(entity this) { spawnfunc_weapon_hmg(this); } -void self_spawnfunc_weapon_rpc(entity this) { spawnfunc_weapon_rpc(this); } - -MUTATOR_HOOKFUNCTION(ok, OnEntityPreSpawn) -{ - entity ent = M_ARGV(0, entity); - - if(autocvar_g_powerups) - if(autocvar_g_overkill_powerups_replace) - { - if(ent.classname == "item_strength") - { - entity wep = new(weapon_hmg); - setorigin(wep, ent.origin); - setmodel(wep, MDL_OK_HMG); - wep.ok_item = true; - wep.noalign = ent.noalign; - wep.cnt = ent.cnt; - wep.team = ent.team; - wep.respawntime = autocvar_g_overkill_superguns_respawn_time; - wep.pickup_anyway = true; - wep.spawnfunc_checked = true; - setthink(wep, self_spawnfunc_weapon_hmg); - wep.nextthink = time + 0.1; - return true; - } - - if(ent.classname == "item_invincible") - { - entity wep = new(weapon_rpc); - setorigin(wep, ent.origin); - setmodel(wep, MDL_OK_RPC); - wep.ok_item = true; - wep.noalign = ent.noalign; - wep.cnt = ent.cnt; - wep.team = ent.team; - wep.respawntime = autocvar_g_overkill_superguns_respawn_time; - wep.pickup_anyway = true; - wep.spawnfunc_checked = true; - setthink(wep, self_spawnfunc_weapon_rpc); - wep.nextthink = time + 0.1; - return true; - } - } -} - -MUTATOR_HOOKFUNCTION(ok, FilterItem) -{ - entity item = M_ARGV(0, entity); - - if(item.ok_item) - return; - - switch(item.items) - { - case ITEM_HealthMega.m_itemid: return !(autocvar_g_overkill_100h_anyway); - case ITEM_ArmorMega.m_itemid: return !(autocvar_g_overkill_100a_anyway); - } - - return true; -} - -MUTATOR_HOOKFUNCTION(ok, SpectateCopy) -{ - entity spectatee = M_ARGV(0, entity); - entity client = M_ARGV(1, entity); - - client.ammo_charge[PS(client).m_weapon.m_id] = spectatee.ammo_charge[PS(spectatee).m_weapon.m_id]; - client.ok_use_ammocharge = spectatee.ok_use_ammocharge; -} - -MUTATOR_HOOKFUNCTION(ok, SetStartItems) -{ - WepSet ok_start_items = (WEPSET(MACHINEGUN) | WEPSET(VORTEX) | WEPSET(SHOTGUN)); - - if(WEP_RPC.weaponstart > 0) { ok_start_items |= WEPSET(RPC); } - if(WEP_HMG.weaponstart > 0) { ok_start_items |= WEPSET(HMG); } - - start_items |= IT_UNLIMITED_WEAPON_AMMO; - start_weapons = warmup_start_weapons = ok_start_items; -} - -MUTATOR_HOOKFUNCTION(ok, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":OK"); -} - -MUTATOR_HOOKFUNCTION(ok, BuildMutatorsPrettyString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Overkill"); -} - -MUTATOR_HOOKFUNCTION(ok, SetModname) -{ - M_ARGV(0, string) = "Overkill"; - return true; -} - -void ok_SetCvars() -{ - // hack to force overkill playermodels - cvar_settemp("sv_defaultcharacter", "1"); - cvar_settemp("sv_defaultplayermodel", "models/ok_player/okrobot1.dpm models/ok_player/okrobot2.dpm models/ok_player/okrobot3.dpm models/ok_player/okrobot4.dpm models/ok_player/okmale1.dpm models/ok_player/okmale2.dpm models/ok_player/okmale3.dpm models/ok_player/okmale4.dpm"); - cvar_settemp("sv_defaultplayermodel_red", "models/ok_player/okrobot1.dpm models/ok_player/okrobot2.dpm models/ok_player/okrobot3.dpm models/ok_player/okrobot4.dpm"); - cvar_settemp("sv_defaultplayermodel_blue", "models/ok_player/okmale1.dpm models/ok_player/okmale2.dpm models/ok_player/okmale3.dpm models/ok_player/okmale4.dpm"); -} - -void ok_Initialize() -{ - ok_SetCvars(); - - precache_all_playermodels("models/ok_player/*.dpm"); - - WEP_RPC.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; - WEP_HMG.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; - - WEP_SHOTGUN.mdl = "ok_shotgun"; - WEP_MACHINEGUN.mdl = "ok_mg"; - WEP_VORTEX.mdl = "ok_sniper"; -} -#endif +#include "overkill.qh" diff --git a/qcsrc/common/mutators/mutator/overkill/overkill.qh b/qcsrc/common/mutators/mutator/overkill/overkill.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/overkill.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/overkill/rpc.qc b/qcsrc/common/mutators/mutator/overkill/rpc.qc index f9be2ad9f5..4540a26f2e 100644 --- a/qcsrc/common/mutators/mutator/overkill/rpc.qc +++ b/qcsrc/common/mutators/mutator/overkill/rpc.qc @@ -1,52 +1,5 @@ -#ifndef IMPLEMENTATION -CLASS(RocketPropelledChainsaw, Weapon) -/* ammotype */ ATTRIB(RocketPropelledChainsaw, ammo_field, .int, ammo_rockets); -/* impulse */ ATTRIB(RocketPropelledChainsaw, impulse, int, 7); -/* flags */ ATTRIB(RocketPropelledChainsaw, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN | WEP_FLAG_NORMAL | WEP_FLAG_CANCLIMB | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH | WEP_FLAG_SUPERWEAPON); -/* rating */ ATTRIB(RocketPropelledChainsaw, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH); -/* color */ ATTRIB(RocketPropelledChainsaw, wpcolor, vector, '0.5 0.5 0'); -/* modelname */ ATTRIB(RocketPropelledChainsaw, mdl, string, "ok_rl"); -#ifndef MENUQC -/* model */ ATTRIB(RocketPropelledChainsaw, m_model, Model, MDL_RPC_ITEM); -#endif -/* crosshair */ ATTRIB(RocketPropelledChainsaw, w_crosshair, string, "gfx/crosshairrocketlauncher"); -/* crosshair */ ATTRIB(RocketPropelledChainsaw, w_crosshair_size, float, 0.6); -/* wepimg */ ATTRIB(RocketPropelledChainsaw, model2, string, "weaponrpc"); -/* refname */ ATTRIB(RocketPropelledChainsaw, netname, string, "rpc"); -/* wepname */ ATTRIB(RocketPropelledChainsaw, m_name, string, _("Rocket Propelled Chainsaw")); - -#define X(BEGIN, P, END, class, prefix) \ - BEGIN(class) \ - P(class, prefix, ammo, float, NONE) \ - P(class, prefix, animtime, float, NONE) \ - P(class, prefix, damage2, float, NONE) \ - P(class, prefix, damageforcescale, float, NONE) \ - P(class, prefix, damage, float, NONE) \ - P(class, prefix, edgedamage, float, NONE) \ - P(class, prefix, force, float, NONE) \ - P(class, prefix, health, float, NONE) \ - P(class, prefix, lifetime, float, NONE) \ - P(class, prefix, radius, float, NONE) \ - P(class, prefix, refire, float, NONE) \ - P(class, prefix, reload_ammo, float, NONE) \ - P(class, prefix, reload_time, float, NONE) \ - P(class, prefix, speedaccel, float, NONE) \ - P(class, prefix, speed, float, NONE) \ - P(class, prefix, switchdelay_drop, float, NONE) \ - P(class, prefix, switchdelay_raise, float, NONE) \ - P(class, prefix, weaponreplace, string, NONE) \ - P(class, prefix, weaponstartoverride, float, NONE) \ - P(class, prefix, weaponstart, float, NONE) \ - P(class, prefix, weaponthrowable, float, NONE) \ - END() - W_PROPS(X, RocketPropelledChainsaw, rpc) -#undef X - -ENDCLASS(RocketPropelledChainsaw) -REGISTER_WEAPON(RPC, rpc, NEW(RocketPropelledChainsaw)); +#include "rpc.qh" -#endif -#ifdef IMPLEMENTATION #ifdef SVQC spawnfunc(weapon_rpc) { weapon_defaultspawnfunc(this, WEP_RPC); } @@ -232,4 +185,3 @@ METHOD(RocketPropelledChainsaw, wr_impacteffect, void(entity thiswep, entity act } #endif -#endif diff --git a/qcsrc/common/mutators/mutator/overkill/rpc.qh b/qcsrc/common/mutators/mutator/overkill/rpc.qh new file mode 100644 index 0000000000..535fa5533a --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/rpc.qh @@ -0,0 +1,47 @@ +#pragma once + +CLASS(RocketPropelledChainsaw, Weapon) +/* ammotype */ ATTRIB(RocketPropelledChainsaw, ammo_field, .int, ammo_rockets); +/* impulse */ ATTRIB(RocketPropelledChainsaw, impulse, int, 7); +/* flags */ ATTRIB(RocketPropelledChainsaw, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN | WEP_FLAG_NORMAL | WEP_FLAG_CANCLIMB | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH | WEP_FLAG_SUPERWEAPON); +/* rating */ ATTRIB(RocketPropelledChainsaw, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH); +/* color */ ATTRIB(RocketPropelledChainsaw, wpcolor, vector, '0.5 0.5 0'); +/* modelname */ ATTRIB(RocketPropelledChainsaw, mdl, string, "ok_rl"); +#ifdef GAMEQC +/* model */ ATTRIB(RocketPropelledChainsaw, m_model, Model, MDL_RPC_ITEM); +#endif +/* crosshair */ ATTRIB(RocketPropelledChainsaw, w_crosshair, string, "gfx/crosshairrocketlauncher"); +/* crosshair */ ATTRIB(RocketPropelledChainsaw, w_crosshair_size, float, 0.6); +/* wepimg */ ATTRIB(RocketPropelledChainsaw, model2, string, "weaponrpc"); +/* refname */ ATTRIB(RocketPropelledChainsaw, netname, string, "rpc"); +/* wepname */ ATTRIB(RocketPropelledChainsaw, m_name, string, _("Rocket Propelled Chainsaw")); + +#define X(BEGIN, P, END, class, prefix) \ + BEGIN(class) \ + P(class, prefix, ammo, float, NONE) \ + P(class, prefix, animtime, float, NONE) \ + P(class, prefix, damage2, float, NONE) \ + P(class, prefix, damageforcescale, float, NONE) \ + P(class, prefix, damage, float, NONE) \ + P(class, prefix, edgedamage, float, NONE) \ + P(class, prefix, force, float, NONE) \ + P(class, prefix, health, float, NONE) \ + P(class, prefix, lifetime, float, NONE) \ + P(class, prefix, radius, float, NONE) \ + P(class, prefix, refire, float, NONE) \ + P(class, prefix, reload_ammo, float, NONE) \ + P(class, prefix, reload_time, float, NONE) \ + P(class, prefix, speedaccel, float, NONE) \ + P(class, prefix, speed, float, NONE) \ + P(class, prefix, switchdelay_drop, float, NONE) \ + P(class, prefix, switchdelay_raise, float, NONE) \ + P(class, prefix, weaponreplace, string, NONE) \ + P(class, prefix, weaponstartoverride, float, NONE) \ + P(class, prefix, weaponstart, float, NONE) \ + P(class, prefix, weaponthrowable, float, NONE) \ + END() + W_PROPS(X, RocketPropelledChainsaw, rpc) +#undef X + +ENDCLASS(RocketPropelledChainsaw) +REGISTER_WEAPON(RPC, rpc, NEW(RocketPropelledChainsaw)); diff --git a/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc b/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc new file mode 100644 index 0000000000..ea7ed953ce --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc @@ -0,0 +1,413 @@ +#include "sv_overkill.qh" + +#include "hmg.qh" +#include "rpc.qh" + +bool autocvar_g_overkill_powerups_replace; +float autocvar_g_overkill_superguns_respawn_time; +bool autocvar_g_overkill_100h_anyway; +bool autocvar_g_overkill_100a_anyway; +bool autocvar_g_overkill_ammo_charge; +float autocvar_g_overkill_ammo_charge_notice; +float autocvar_g_overkill_ammo_charge_limit; + +.vector ok_deathloc; +.float ok_spawnsys_timer; +.float ok_lastwep; +.float ok_item; + +.float ok_notice_time; +.float ammo_charge[Weapons_MAX]; +.float ok_use_ammocharge = _STAT(OK_AMMO_CHARGE); +.float ok_ammo_charge = _STAT(OK_AMMO_CHARGEPOOL); + +.float ok_pauseregen_finished; + +void(entity ent, float wep) ok_DecreaseCharge; + +void ok_Initialize(); + +REGISTER_MUTATOR(ok, cvar("g_overkill") && !cvar("g_instagib") && !g_nexball && cvar_string("g_mod_balance") == "Overkill") +{ + MUTATOR_ONADD + { + ok_Initialize(); + } + + MUTATOR_ONREMOVE + { + WEP_RPC.spawnflags |= WEP_FLAG_MUTATORBLOCKED; + WEP_HMG.spawnflags |= WEP_FLAG_MUTATORBLOCKED; + } +} + +MUTATOR_HOOKFUNCTION(ok, W_DecreaseAmmo) +{ + entity actor = M_ARGV(0, entity); + if (actor.ok_use_ammocharge) + { + ok_DecreaseCharge(actor, PS(actor).m_weapon.m_id); + return true; + } +} + +MUTATOR_HOOKFUNCTION(ok, W_Reload) +{ + entity actor = M_ARGV(0, entity); + return actor.ok_use_ammocharge; +} + +void W_Blaster_Attack(entity, .entity, float, float, float, float, float, float, float, float, float, float); +spawnfunc(weapon_hmg); +spawnfunc(weapon_rpc); + +void ok_DecreaseCharge(entity ent, int wep) +{ + if(!ent.ok_use_ammocharge) return; + + entity wepent = Weapons_from(wep); + + if (wepent == WEP_Null) return; // dummy + + ent.ammo_charge[wep] -= max(0, cvar(sprintf("g_overkill_ammo_decharge_%s", wepent.netname))); +} + +void ok_IncreaseCharge(entity ent, int wep) +{ + entity wepent = Weapons_from(wep); + + if (wepent == WEP_Null) return; // dummy + + if(ent.ok_use_ammocharge) + if(!PHYS_INPUT_BUTTON_ATCK(ent)) // not while attacking? + ent.ammo_charge[wep] = min(autocvar_g_overkill_ammo_charge_limit, ent.ammo_charge[wep] + cvar(sprintf("g_overkill_ammo_charge_rate_%s", wepent.netname)) * frametime / W_TICSPERFRAME); +} + +float ok_CheckWeaponCharge(entity ent, int wep) +{ + if(!ent.ok_use_ammocharge) return true; + + entity wepent = Weapons_from(wep); + + if(wepent == WEP_Null) return false; // dummy + + return (ent.ammo_charge[wep] >= cvar(sprintf("g_overkill_ammo_decharge_%s", wepent.netname))); +} + +MUTATOR_HOOKFUNCTION(ok, PlayerDamage_Calculate, CBC_ORDER_LAST) +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + float frag_deathtype = M_ARGV(3, float); + + if(IS_PLAYER(frag_attacker) && IS_PLAYER(frag_target)) + if(DEATH_ISWEAPON(frag_deathtype, WEP_BLASTER)) + { + if(frag_attacker != frag_target) + if(frag_target.health > 0) + if(STAT(FROZEN, frag_target) == 0) + if(!IS_DEAD(frag_target)) + { + Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_SECONDARY_NODAMAGE); + M_ARGV(6, vector) = '0 0 0'; + } + + M_ARGV(4, float) = 0; + } +} + +MUTATOR_HOOKFUNCTION(ok, PlayerDamage_SplitHealthArmor) +{ + entity frag_target = M_ARGV(2, entity); + float damage_take = M_ARGV(4, float); + + if(damage_take) + frag_target.ok_pauseregen_finished = max(frag_target.ok_pauseregen_finished, time + 2); +} + +void ok_DropItem(entity this, entity targ) +{ + entity e = new(droppedweapon); // hax + e.ok_item = true; + e.noalign = true; + e.pickup_anyway = true; + e.spawnfunc_checked = true; + spawnfunc_item_armor_small(e); + if (!wasfreed(e)) { // might have been blocked by a mutator + set_movetype(e, MOVETYPE_TOSS); + e.gravity = 1; + e.reset = SUB_Remove; + setorigin(e, this.origin + '0 0 32'); + e.velocity = '0 0 200' + normalize(targ.origin - this.origin) * 500; + SUB_SetFade(e, time + 5, 1); + } +} + +MUTATOR_HOOKFUNCTION(ok, PlayerDies) +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + + entity targ = ((frag_attacker) ? frag_attacker : frag_target); + + ok_DropItem(frag_target, targ); + + frag_target.ok_lastwep = PS(frag_target).m_switchweapon.m_id; +} + +MUTATOR_HOOKFUNCTION(ok, MonsterDropItem) +{ + entity mon = M_ARGV(0, entity); + entity olditem = M_ARGV(1, entity); + entity frag_attacker = M_ARGV(2, entity); + + delete(olditem); + + M_ARGV(1, entity) = NULL; + + ok_DropItem(mon, frag_attacker); +} + +MUTATOR_HOOKFUNCTION(ok, PlayerRegen) +{ + entity player = M_ARGV(0, entity); + + // overkill's values are different, so use custom regen + if(!STAT(FROZEN, player)) + { + player.armorvalue = CalcRotRegen(player.armorvalue, autocvar_g_balance_armor_regenstable, autocvar_g_balance_armor_regen, autocvar_g_balance_armor_regenlinear, + 1 * frametime * (time > player.ok_pauseregen_finished), 0, 0, 1, 1 * frametime * (time > player.pauserotarmor_finished), autocvar_g_balance_armor_limit); + player.health = CalcRotRegen(player.health, autocvar_g_balance_health_regenstable, 0, 100, 1 * frametime * (time > player.ok_pauseregen_finished), 200, 0, + autocvar_g_balance_health_rotlinear, 1 * frametime * (time > player.pauserothealth_finished), autocvar_g_balance_health_limit); + + float minf, maxf, limitf; + + maxf = autocvar_g_balance_fuel_rotstable; + minf = autocvar_g_balance_fuel_regenstable; + limitf = autocvar_g_balance_fuel_limit; + + player.ammo_fuel = CalcRotRegen(player.ammo_fuel, minf, autocvar_g_balance_fuel_regen, autocvar_g_balance_fuel_regenlinear, + frametime * (time > player.pauseregen_finished) * ((player.items & ITEM_JetpackRegen.m_itemid) != 0), maxf, autocvar_g_balance_fuel_rot, autocvar_g_balance_fuel_rotlinear, frametime * (time > player.pauserotfuel_finished), limitf); + } + return true; // return true anyway, as frozen uses no regen +} + +MUTATOR_HOOKFUNCTION(ok, ForbidThrowCurrentWeapon) +{ + return true; +} + +MUTATOR_HOOKFUNCTION(ok, PlayerPreThink) +{ + if(intermission_running || gameover) + return; + + entity player = M_ARGV(0, entity); + + if(IS_DEAD(player) || !IS_PLAYER(player) || STAT(FROZEN, player)) + return; + + if(player.ok_lastwep) + { + Weapon newwep = Weapons_from(player.ok_lastwep); + if(player.ok_lastwep == WEP_HMG.m_id) + newwep = WEP_MACHINEGUN; + if(player.ok_lastwep == WEP_RPC.m_id) + newwep = WEP_VORTEX; + PS(player).m_switchweapon = newwep; + player.ok_lastwep = 0; + } + + ok_IncreaseCharge(player, PS(player).m_weapon.m_id); + + if(PHYS_INPUT_BUTTON_ATCK2(player)) + if(!forbidWeaponUse(player) || player.weapon_blocked) // allow if weapon is blocked + if(time >= player.jump_interval) + { + player.jump_interval = time + WEP_CVAR_PRI(blaster, refire) * W_WeaponRateFactor(player); + makevectors(player.v_angle); + + Weapon oldwep = PS(player).m_weapon; + PS(player).m_weapon = WEP_BLASTER; + W_Blaster_Attack( + player, + weaponentities[0], // TODO: unhardcode + WEP_BLASTER.m_id | HITTYPE_SECONDARY, + WEP_CVAR_SEC(vaporizer, shotangle), + WEP_CVAR_SEC(vaporizer, damage), + WEP_CVAR_SEC(vaporizer, edgedamage), + WEP_CVAR_SEC(vaporizer, radius), + WEP_CVAR_SEC(vaporizer, force), + WEP_CVAR_SEC(vaporizer, speed), + WEP_CVAR_SEC(vaporizer, spread), + WEP_CVAR_SEC(vaporizer, delay), + WEP_CVAR_SEC(vaporizer, lifetime) + ); + PS(player).m_weapon = oldwep; + } + + player.weapon_blocked = false; + + player.ok_ammo_charge = player.ammo_charge[PS(player).m_weapon.m_id]; + + if(player.ok_use_ammocharge) + if(!ok_CheckWeaponCharge(player, PS(player).m_weapon.m_id)) + { + if(autocvar_g_overkill_ammo_charge_notice && time > player.ok_notice_time && PHYS_INPUT_BUTTON_ATCK(player) && IS_REAL_CLIENT(player) && PS(player).m_weapon == PS(player).m_switchweapon) + { + //Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_OVERKILL_CHARGE); + player.ok_notice_time = time + 2; + play2(player, SND(DRYFIRE)); + } + Weapon wpn = PS(player).m_weapon; + .entity weaponentity = weaponentities[0]; // TODO: unhardcode + if(player.(weaponentity).state != WS_CLEAR) + w_ready(wpn, player, weaponentity, PHYS_INPUT_BUTTON_ATCK(player) | (PHYS_INPUT_BUTTON_ATCK2(player) << 1)); + + player.weapon_blocked = true; + } + + PHYS_INPUT_BUTTON_ATCK2(player) = false; +} + +MUTATOR_HOOKFUNCTION(ok, PlayerSpawn) +{ + entity player = M_ARGV(0, entity); + + if(autocvar_g_overkill_ammo_charge) + { + FOREACH(Weapons, it != WEP_Null, LAMBDA(player.ammo_charge[it.m_id] = autocvar_g_overkill_ammo_charge_limit)); + + player.ok_use_ammocharge = 1; + player.ok_notice_time = time; + } + else + player.ok_use_ammocharge = 0; + + // if player changed their weapon while dead, don't switch to their death weapon + if(player.impulse) + player.ok_lastwep = 0; + + player.ok_pauseregen_finished = time + 2; +} + +void self_spawnfunc_weapon_hmg(entity this) { spawnfunc_weapon_hmg(this); } +void self_spawnfunc_weapon_rpc(entity this) { spawnfunc_weapon_rpc(this); } + +MUTATOR_HOOKFUNCTION(ok, OnEntityPreSpawn) +{ + entity ent = M_ARGV(0, entity); + + if(autocvar_g_powerups) + if(autocvar_g_overkill_powerups_replace) + { + if(ent.classname == "item_strength") + { + entity wep = new(weapon_hmg); + setorigin(wep, ent.origin); + setmodel(wep, MDL_OK_HMG); + wep.ok_item = true; + wep.noalign = ent.noalign; + wep.cnt = ent.cnt; + wep.team = ent.team; + wep.respawntime = autocvar_g_overkill_superguns_respawn_time; + wep.pickup_anyway = true; + wep.spawnfunc_checked = true; + setthink(wep, self_spawnfunc_weapon_hmg); + wep.nextthink = time + 0.1; + return true; + } + + if(ent.classname == "item_invincible") + { + entity wep = new(weapon_rpc); + setorigin(wep, ent.origin); + setmodel(wep, MDL_OK_RPC); + wep.ok_item = true; + wep.noalign = ent.noalign; + wep.cnt = ent.cnt; + wep.team = ent.team; + wep.respawntime = autocvar_g_overkill_superguns_respawn_time; + wep.pickup_anyway = true; + wep.spawnfunc_checked = true; + setthink(wep, self_spawnfunc_weapon_rpc); + wep.nextthink = time + 0.1; + return true; + } + } +} + +MUTATOR_HOOKFUNCTION(ok, FilterItem) +{ + entity item = M_ARGV(0, entity); + + if(item.ok_item) + return; + + switch(item.items) + { + case ITEM_HealthMega.m_itemid: return !(autocvar_g_overkill_100h_anyway); + case ITEM_ArmorMega.m_itemid: return !(autocvar_g_overkill_100a_anyway); + } + + return true; +} + +MUTATOR_HOOKFUNCTION(ok, SpectateCopy) +{ + entity spectatee = M_ARGV(0, entity); + entity client = M_ARGV(1, entity); + + client.ammo_charge[PS(client).m_weapon.m_id] = spectatee.ammo_charge[PS(spectatee).m_weapon.m_id]; + client.ok_use_ammocharge = spectatee.ok_use_ammocharge; +} + +MUTATOR_HOOKFUNCTION(ok, SetStartItems) +{ + WepSet ok_start_items = (WEPSET(MACHINEGUN) | WEPSET(VORTEX) | WEPSET(SHOTGUN)); + + if(WEP_RPC.weaponstart > 0) { ok_start_items |= WEPSET(RPC); } + if(WEP_HMG.weaponstart > 0) { ok_start_items |= WEPSET(HMG); } + + start_items |= IT_UNLIMITED_WEAPON_AMMO; + start_weapons = warmup_start_weapons = ok_start_items; +} + +MUTATOR_HOOKFUNCTION(ok, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":OK"); +} + +MUTATOR_HOOKFUNCTION(ok, BuildMutatorsPrettyString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Overkill"); +} + +MUTATOR_HOOKFUNCTION(ok, SetModname) +{ + M_ARGV(0, string) = "Overkill"; + return true; +} + +void ok_SetCvars() +{ + // hack to force overkill playermodels + cvar_settemp("sv_defaultcharacter", "1"); + cvar_settemp("sv_defaultplayermodel", "models/ok_player/okrobot1.dpm models/ok_player/okrobot2.dpm models/ok_player/okrobot3.dpm models/ok_player/okrobot4.dpm models/ok_player/okmale1.dpm models/ok_player/okmale2.dpm models/ok_player/okmale3.dpm models/ok_player/okmale4.dpm"); + cvar_settemp("sv_defaultplayermodel_red", "models/ok_player/okrobot1.dpm models/ok_player/okrobot2.dpm models/ok_player/okrobot3.dpm models/ok_player/okrobot4.dpm"); + cvar_settemp("sv_defaultplayermodel_blue", "models/ok_player/okmale1.dpm models/ok_player/okmale2.dpm models/ok_player/okmale3.dpm models/ok_player/okmale4.dpm"); +} + +void ok_Initialize() +{ + ok_SetCvars(); + + precache_all_playermodels("models/ok_player/*.dpm"); + + WEP_RPC.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; + WEP_HMG.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; + + WEP_SHOTGUN.mdl = "ok_shotgun"; + WEP_MACHINEGUN.mdl = "ok_mg"; + WEP_VORTEX.mdl = "ok_sniper"; +} diff --git a/qcsrc/common/mutators/mutator/overkill/sv_overkill.qh b/qcsrc/common/mutators/mutator/overkill/sv_overkill.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/sv_overkill.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/physical_items/_mod.inc b/qcsrc/common/mutators/mutator/physical_items/_mod.inc index 4d4ef59f8a..e99d4e257a 100644 --- a/qcsrc/common/mutators/mutator/physical_items/_mod.inc +++ b/qcsrc/common/mutators/mutator/physical_items/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/physical_items/_mod.qh b/qcsrc/common/mutators/mutator/physical_items/_mod.qh index a347cec04e..1aab8b0a84 100644 --- a/qcsrc/common/mutators/mutator/physical_items/_mod.qh +++ b/qcsrc/common/mutators/mutator/physical_items/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/physical_items/module.inc b/qcsrc/common/mutators/mutator/physical_items/module.inc deleted file mode 100644 index 7ed9b039be..0000000000 --- a/qcsrc/common/mutators/mutator/physical_items/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "physical_items.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/physical_items/physical_items.qc b/qcsrc/common/mutators/mutator/physical_items/physical_items.qc deleted file mode 100644 index f015baeaa9..0000000000 --- a/qcsrc/common/mutators/mutator/physical_items/physical_items.qc +++ /dev/null @@ -1,141 +0,0 @@ -#ifdef IMPLEMENTATION -int autocvar_g_physical_items; -float autocvar_g_physical_items_damageforcescale; -float autocvar_g_physical_items_reset; - -REGISTER_MUTATOR(physical_items, cvar("g_physical_items")) -{ - // check if we have a physics engine - MUTATOR_ONADD - { - if (!(autocvar_physics_ode && checkextension("DP_PHYSICS_ODE"))) - { - LOG_TRACE("Warning: Physical items are enabled but no physics engine can be used. Reverting to old items."); - return -1; - } - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // nothing to roll back - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This cannot be removed at runtime\n"); - return -1; - } - - return 0; -} - -.vector spawn_origin, spawn_angles; - -void physical_item_think(entity this) -{ - this.nextthink = time; - - this.alpha = this.owner.alpha; // apply fading and ghosting - - if(!this.cnt) // map item, not dropped - { - // copy ghost item properties - this.colormap = this.owner.colormap; - this.colormod = this.owner.colormod; - this.glowmod = this.owner.glowmod; - - // if the item is not spawned, make sure the invisible / ghost item returns to its origin and stays there - if(autocvar_g_physical_items_reset) - { - if(this.owner.wait > time) // awaiting respawn - { - setorigin(this, this.spawn_origin); - this.angles = this.spawn_angles; - this.solid = SOLID_NOT; - this.alpha = -1; - set_movetype(this, MOVETYPE_NONE); - } - else - { - this.alpha = 1; - this.solid = SOLID_CORPSE; - set_movetype(this, MOVETYPE_PHYSICS); - } - } - } - - if(!this.owner.modelindex) - delete(this); // the real item is gone, remove this -} - -void physical_item_touch(entity this, entity toucher) -{ - if(!this.cnt) // not for dropped items - if (ITEM_TOUCH_NEEDKILL()) - { - setorigin(this, this.spawn_origin); - this.angles = this.spawn_angles; - } -} - -void physical_item_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) -{ - if(!this.cnt) // not for dropped items - if(ITEM_DAMAGE_NEEDKILL(deathtype)) - { - setorigin(this, this.spawn_origin); - this.angles = this.spawn_angles; - } -} - -MUTATOR_HOOKFUNCTION(physical_items, Item_Spawn) -{ - entity item = M_ARGV(0, entity); - - if(item.owner == NULL && autocvar_g_physical_items <= 1) - return; - if (item.spawnflags & 1) // floating item - return; - - // The actual item can't be physical and trigger at the same time, so make it invisible and use a second entity for physics. - // Ugly hack, but unless SOLID_TRIGGER is gotten to work with MOVETYPE_PHYSICS in the engine it can't be fixed. - entity wep; - wep = spawn(); - _setmodel(wep, item.model); - setsize(wep, item.mins, item.maxs); - setorigin(wep, item.origin); - wep.angles = item.angles; - wep.velocity = item.velocity; - - wep.owner = item; - wep.solid = SOLID_CORPSE; - set_movetype(wep, MOVETYPE_PHYSICS); - wep.takedamage = DAMAGE_AIM; - wep.effects |= EF_NOMODELFLAGS; // disable the spinning - wep.colormap = item.owner.colormap; - wep.glowmod = item.owner.glowmod; - wep.damageforcescale = autocvar_g_physical_items_damageforcescale; - wep.dphitcontentsmask = item.dphitcontentsmask; - wep.cnt = (item.owner != NULL); - - setthink(wep, physical_item_think); - wep.nextthink = time; - settouch(wep, physical_item_touch); - wep.event_damage = physical_item_damage; - - if(!wep.cnt) - { - // fix the spawn origin - setorigin(wep, wep.origin + '0 0 1'); - droptofloor(wep); - } - - wep.spawn_origin = wep.origin; - wep.spawn_angles = item.angles; - - item.effects |= EF_NODRAW; // hide the original weapon - set_movetype(item, MOVETYPE_FOLLOW); - item.aiment = wep; // attach the original weapon - setSendEntity(item, func_null); -} -#endif diff --git a/qcsrc/common/mutators/mutator/physical_items/sv_physical_items.qc b/qcsrc/common/mutators/mutator/physical_items/sv_physical_items.qc new file mode 100644 index 0000000000..62b30e2d14 --- /dev/null +++ b/qcsrc/common/mutators/mutator/physical_items/sv_physical_items.qc @@ -0,0 +1,141 @@ +#include "sv_physical_items.qh" + +int autocvar_g_physical_items; +float autocvar_g_physical_items_damageforcescale; +float autocvar_g_physical_items_reset; + +REGISTER_MUTATOR(physical_items, cvar("g_physical_items")) +{ + // check if we have a physics engine + MUTATOR_ONADD + { + if (!(autocvar_physics_ode && checkextension("DP_PHYSICS_ODE"))) + { + LOG_TRACE("Warning: Physical items are enabled but no physics engine can be used. Reverting to old items."); + return -1; + } + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // nothing to roll back + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This cannot be removed at runtime\n"); + return -1; + } + + return 0; +} + +.vector spawn_origin, spawn_angles; + +void physical_item_think(entity this) +{ + this.nextthink = time; + + this.alpha = this.owner.alpha; // apply fading and ghosting + + if(!this.cnt) // map item, not dropped + { + // copy ghost item properties + this.colormap = this.owner.colormap; + this.colormod = this.owner.colormod; + this.glowmod = this.owner.glowmod; + + // if the item is not spawned, make sure the invisible / ghost item returns to its origin and stays there + if(autocvar_g_physical_items_reset) + { + if(this.owner.wait > time) // awaiting respawn + { + setorigin(this, this.spawn_origin); + this.angles = this.spawn_angles; + this.solid = SOLID_NOT; + this.alpha = -1; + set_movetype(this, MOVETYPE_NONE); + } + else + { + this.alpha = 1; + this.solid = SOLID_CORPSE; + set_movetype(this, MOVETYPE_PHYSICS); + } + } + } + + if(!this.owner.modelindex) + delete(this); // the real item is gone, remove this +} + +void physical_item_touch(entity this, entity toucher) +{ + if(!this.cnt) // not for dropped items + if (ITEM_TOUCH_NEEDKILL()) + { + setorigin(this, this.spawn_origin); + this.angles = this.spawn_angles; + } +} + +void physical_item_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) +{ + if(!this.cnt) // not for dropped items + if(ITEM_DAMAGE_NEEDKILL(deathtype)) + { + setorigin(this, this.spawn_origin); + this.angles = this.spawn_angles; + } +} + +MUTATOR_HOOKFUNCTION(physical_items, Item_Spawn) +{ + entity item = M_ARGV(0, entity); + + if(item.owner == NULL && autocvar_g_physical_items <= 1) + return; + if (item.spawnflags & 1) // floating item + return; + + // The actual item can't be physical and trigger at the same time, so make it invisible and use a second entity for physics. + // Ugly hack, but unless SOLID_TRIGGER is gotten to work with MOVETYPE_PHYSICS in the engine it can't be fixed. + entity wep; + wep = spawn(); + _setmodel(wep, item.model); + setsize(wep, item.mins, item.maxs); + setorigin(wep, item.origin); + wep.angles = item.angles; + wep.velocity = item.velocity; + + wep.owner = item; + wep.solid = SOLID_CORPSE; + set_movetype(wep, MOVETYPE_PHYSICS); + wep.takedamage = DAMAGE_AIM; + wep.effects |= EF_NOMODELFLAGS; // disable the spinning + wep.colormap = item.owner.colormap; + wep.glowmod = item.owner.glowmod; + wep.damageforcescale = autocvar_g_physical_items_damageforcescale; + wep.dphitcontentsmask = item.dphitcontentsmask; + wep.cnt = (item.owner != NULL); + + setthink(wep, physical_item_think); + wep.nextthink = time; + settouch(wep, physical_item_touch); + wep.event_damage = physical_item_damage; + + if(!wep.cnt) + { + // fix the spawn origin + setorigin(wep, wep.origin + '0 0 1'); + droptofloor(wep); + } + + wep.spawn_origin = wep.origin; + wep.spawn_angles = item.angles; + + item.effects |= EF_NODRAW; // hide the original weapon + set_movetype(item, MOVETYPE_FOLLOW); + item.aiment = wep; // attach the original weapon + setSendEntity(item, func_null); +} diff --git a/qcsrc/common/mutators/mutator/physical_items/sv_physical_items.qh b/qcsrc/common/mutators/mutator/physical_items/sv_physical_items.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/physical_items/sv_physical_items.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/pinata/_mod.inc b/qcsrc/common/mutators/mutator/pinata/_mod.inc index a0bd94d00e..5859c55107 100644 --- a/qcsrc/common/mutators/mutator/pinata/_mod.inc +++ b/qcsrc/common/mutators/mutator/pinata/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/pinata/_mod.qh b/qcsrc/common/mutators/mutator/pinata/_mod.qh index 1602640e09..fdb51ed25b 100644 --- a/qcsrc/common/mutators/mutator/pinata/_mod.qh +++ b/qcsrc/common/mutators/mutator/pinata/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/pinata/module.inc b/qcsrc/common/mutators/mutator/pinata/module.inc deleted file mode 100644 index 4e22966863..0000000000 --- a/qcsrc/common/mutators/mutator/pinata/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "pinata.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/pinata/pinata.qc b/qcsrc/common/mutators/mutator/pinata/pinata.qc deleted file mode 100644 index acdf1718b0..0000000000 --- a/qcsrc/common/mutators/mutator/pinata/pinata.qc +++ /dev/null @@ -1,28 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(pinata, cvar("g_pinata") && !cvar("g_instagib") && !cvar("g_overkill")); - -MUTATOR_HOOKFUNCTION(pinata, PlayerDies) -{ - entity frag_target = M_ARGV(2, entity); - - FOREACH(Weapons, it != WEP_Null, LAMBDA( - if(frag_target.weapons & WepSet_FromWeapon(it)) - if(PS(frag_target).m_switchweapon != it) - if(W_IsWeaponThrowable(frag_target, it.m_id)) - W_ThrowNewWeapon(frag_target, it.m_id, false, CENTER_OR_VIEWOFS(frag_target), randomvec() * 175 + '0 0 325'); - )); - - return true; -} - -MUTATOR_HOOKFUNCTION(pinata, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Pinata"); -} - -MUTATOR_HOOKFUNCTION(pinata, BuildMutatorsPrettyString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Piñata"); -} - -#endif diff --git a/qcsrc/common/mutators/mutator/pinata/sv_pinata.qc b/qcsrc/common/mutators/mutator/pinata/sv_pinata.qc new file mode 100644 index 0000000000..bc3887e860 --- /dev/null +++ b/qcsrc/common/mutators/mutator/pinata/sv_pinata.qc @@ -0,0 +1,27 @@ +#include "sv_pinata.qh" + +REGISTER_MUTATOR(pinata, cvar("g_pinata") && !cvar("g_instagib") && !cvar("g_overkill")); + +MUTATOR_HOOKFUNCTION(pinata, PlayerDies) +{ + entity frag_target = M_ARGV(2, entity); + + FOREACH(Weapons, it != WEP_Null, LAMBDA( + if(frag_target.weapons & WepSet_FromWeapon(it)) + if(PS(frag_target).m_switchweapon != it) + if(W_IsWeaponThrowable(frag_target, it.m_id)) + W_ThrowNewWeapon(frag_target, it.m_id, false, CENTER_OR_VIEWOFS(frag_target), randomvec() * 175 + '0 0 325'); + )); + + return true; +} + +MUTATOR_HOOKFUNCTION(pinata, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Pinata"); +} + +MUTATOR_HOOKFUNCTION(pinata, BuildMutatorsPrettyString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Piñata"); +} diff --git a/qcsrc/common/mutators/mutator/pinata/sv_pinata.qh b/qcsrc/common/mutators/mutator/pinata/sv_pinata.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/pinata/sv_pinata.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/random_gravity/_mod.inc b/qcsrc/common/mutators/mutator/random_gravity/_mod.inc index feeaec8d69..846bd8bf82 100644 --- a/qcsrc/common/mutators/mutator/random_gravity/_mod.inc +++ b/qcsrc/common/mutators/mutator/random_gravity/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/random_gravity/_mod.qh b/qcsrc/common/mutators/mutator/random_gravity/_mod.qh index 99a11ed638..2cdf724023 100644 --- a/qcsrc/common/mutators/mutator/random_gravity/_mod.qh +++ b/qcsrc/common/mutators/mutator/random_gravity/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/random_gravity/module.inc b/qcsrc/common/mutators/mutator/random_gravity/module.inc deleted file mode 100644 index 91baa43102..0000000000 --- a/qcsrc/common/mutators/mutator/random_gravity/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "random_gravity.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/random_gravity/random_gravity.qc b/qcsrc/common/mutators/mutator/random_gravity/random_gravity.qc deleted file mode 100644 index 9706aeca78..0000000000 --- a/qcsrc/common/mutators/mutator/random_gravity/random_gravity.qc +++ /dev/null @@ -1,50 +0,0 @@ -#ifdef IMPLEMENTATION -// Random Gravity -// -// Mutator by Mario -// Inspired by Player 2 - -float autocvar_g_random_gravity_negative_chance; -float autocvar_g_random_gravity_min; -float autocvar_g_random_gravity_max; -float autocvar_g_random_gravity_positive; -float autocvar_g_random_gravity_negative; -float autocvar_g_random_gravity_delay; - -REGISTER_MUTATOR(random_gravity, cvar("g_random_gravity")) -{ - MUTATOR_ONADD - { - cvar_settemp("sv_gravity", cvar_string("sv_gravity")); // settemp current gravity so it's restored on match end - } -} - -float gravity_delay; - -MUTATOR_HOOKFUNCTION(random_gravity, SV_StartFrame) -{ - if(gameover || !cvar("g_random_gravity")) return false; - if(time < gravity_delay) return false; - if(time < game_starttime) return false; - if(round_handler_IsActive() && !round_handler_IsRoundStarted()) return false; - - if(random() >= autocvar_g_random_gravity_negative_chance) - cvar_set("sv_gravity", ftos(bound(autocvar_g_random_gravity_min, random() - random() * -autocvar_g_random_gravity_negative, autocvar_g_random_gravity_max))); - else - cvar_set("sv_gravity", ftos(bound(autocvar_g_random_gravity_min, random() * autocvar_g_random_gravity_positive, autocvar_g_random_gravity_max))); - - gravity_delay = time + autocvar_g_random_gravity_delay; - - LOG_TRACE("Gravity is now: ", ftos(autocvar_sv_gravity)); -} - -MUTATOR_HOOKFUNCTION(random_gravity, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":RandomGravity"); -} - -MUTATOR_HOOKFUNCTION(random_gravity, BuildMutatorsPrettyString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Random gravity"); -} -#endif diff --git a/qcsrc/common/mutators/mutator/random_gravity/sv_random_gravity.qc b/qcsrc/common/mutators/mutator/random_gravity/sv_random_gravity.qc new file mode 100644 index 0000000000..f6f28a7dcb --- /dev/null +++ b/qcsrc/common/mutators/mutator/random_gravity/sv_random_gravity.qc @@ -0,0 +1,50 @@ +#include "sv_random_gravity.qh" + +// Random Gravity + +// Mutator by Mario +// Inspired by Player 2 + +float autocvar_g_random_gravity_negative_chance; +float autocvar_g_random_gravity_min; +float autocvar_g_random_gravity_max; +float autocvar_g_random_gravity_positive; +float autocvar_g_random_gravity_negative; +float autocvar_g_random_gravity_delay; + +REGISTER_MUTATOR(random_gravity, cvar("g_random_gravity")) +{ + MUTATOR_ONADD + { + cvar_settemp("sv_gravity", cvar_string("sv_gravity")); // settemp current gravity so it's restored on match end + } +} + +float gravity_delay; + +MUTATOR_HOOKFUNCTION(random_gravity, SV_StartFrame) +{ + if(gameover || !cvar("g_random_gravity")) return false; + if(time < gravity_delay) return false; + if(time < game_starttime) return false; + if(round_handler_IsActive() && !round_handler_IsRoundStarted()) return false; + + if(random() >= autocvar_g_random_gravity_negative_chance) + cvar_set("sv_gravity", ftos(bound(autocvar_g_random_gravity_min, random() - random() * -autocvar_g_random_gravity_negative, autocvar_g_random_gravity_max))); + else + cvar_set("sv_gravity", ftos(bound(autocvar_g_random_gravity_min, random() * autocvar_g_random_gravity_positive, autocvar_g_random_gravity_max))); + + gravity_delay = time + autocvar_g_random_gravity_delay; + + LOG_TRACE("Gravity is now: ", ftos(autocvar_sv_gravity)); +} + +MUTATOR_HOOKFUNCTION(random_gravity, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":RandomGravity"); +} + +MUTATOR_HOOKFUNCTION(random_gravity, BuildMutatorsPrettyString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Random gravity"); +} diff --git a/qcsrc/common/mutators/mutator/random_gravity/sv_random_gravity.qh b/qcsrc/common/mutators/mutator/random_gravity/sv_random_gravity.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/random_gravity/sv_random_gravity.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/rocketflying/_mod.inc b/qcsrc/common/mutators/mutator/rocketflying/_mod.inc index 0841ae680b..537a2b37b0 100644 --- a/qcsrc/common/mutators/mutator/rocketflying/_mod.inc +++ b/qcsrc/common/mutators/mutator/rocketflying/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/rocketflying/_mod.qh b/qcsrc/common/mutators/mutator/rocketflying/_mod.qh index 75ca141bf0..b24545f8c4 100644 --- a/qcsrc/common/mutators/mutator/rocketflying/_mod.qh +++ b/qcsrc/common/mutators/mutator/rocketflying/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/rocketflying/module.inc b/qcsrc/common/mutators/mutator/rocketflying/module.inc deleted file mode 100644 index 7036bc49d6..0000000000 --- a/qcsrc/common/mutators/mutator/rocketflying/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "rocketflying.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/rocketflying/rocketflying.qc b/qcsrc/common/mutators/mutator/rocketflying/rocketflying.qc deleted file mode 100644 index da7e1c3ec0..0000000000 --- a/qcsrc/common/mutators/mutator/rocketflying/rocketflying.qc +++ /dev/null @@ -1,24 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(rocketflying, cvar("g_rocket_flying")); - -MUTATOR_HOOKFUNCTION(rocketflying, EditProjectile) -{ - entity proj = M_ARGV(1, entity); - - if(proj.classname == "rocket" || proj.classname == "mine") - { - // kill detonate delay of rockets - proj.spawnshieldtime = time; - } -} - -MUTATOR_HOOKFUNCTION(rocketflying, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":RocketFlying"); -} - -MUTATOR_HOOKFUNCTION(rocketflying, BuildMutatorsPrettyString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Rocket Flying"); -} -#endif diff --git a/qcsrc/common/mutators/mutator/rocketflying/sv_rocketflying.qc b/qcsrc/common/mutators/mutator/rocketflying/sv_rocketflying.qc new file mode 100644 index 0000000000..9f0d8fbf0d --- /dev/null +++ b/qcsrc/common/mutators/mutator/rocketflying/sv_rocketflying.qc @@ -0,0 +1,24 @@ +#include "sv_rocketflying.qh" + +REGISTER_MUTATOR(rocketflying, cvar("g_rocket_flying")); + +MUTATOR_HOOKFUNCTION(rocketflying, EditProjectile) +{ + entity proj = M_ARGV(1, entity); + + if(proj.classname == "rocket" || proj.classname == "mine") + { + // kill detonate delay of rockets + proj.spawnshieldtime = time; + } +} + +MUTATOR_HOOKFUNCTION(rocketflying, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":RocketFlying"); +} + +MUTATOR_HOOKFUNCTION(rocketflying, BuildMutatorsPrettyString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Rocket Flying"); +} diff --git a/qcsrc/common/mutators/mutator/rocketflying/sv_rocketflying.qh b/qcsrc/common/mutators/mutator/rocketflying/sv_rocketflying.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/rocketflying/sv_rocketflying.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/rocketminsta/_mod.inc b/qcsrc/common/mutators/mutator/rocketminsta/_mod.inc index bc579ec512..bb75554bad 100644 --- a/qcsrc/common/mutators/mutator/rocketminsta/_mod.inc +++ b/qcsrc/common/mutators/mutator/rocketminsta/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/rocketminsta/_mod.qh b/qcsrc/common/mutators/mutator/rocketminsta/_mod.qh index 29a367d3cf..832c5da2cb 100644 --- a/qcsrc/common/mutators/mutator/rocketminsta/_mod.qh +++ b/qcsrc/common/mutators/mutator/rocketminsta/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/rocketminsta/module.inc b/qcsrc/common/mutators/mutator/rocketminsta/module.inc deleted file mode 100644 index b7d02a9f6b..0000000000 --- a/qcsrc/common/mutators/mutator/rocketminsta/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "rocketminsta.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/rocketminsta/rocketminsta.qc b/qcsrc/common/mutators/mutator/rocketminsta/rocketminsta.qc deleted file mode 100644 index b0a740391f..0000000000 --- a/qcsrc/common/mutators/mutator/rocketminsta/rocketminsta.qc +++ /dev/null @@ -1,40 +0,0 @@ -#ifdef IMPLEMENTATION -#include -#include - -REGISTER_MUTATOR(rm, cvar("g_instagib")); - -MUTATOR_HOOKFUNCTION(rm, PlayerDamage_Calculate) -{ - // we do it this way, so rm can be toggled during the match - if(!autocvar_g_rm) { return; } - - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - float frag_deathtype = M_ARGV(3, float); - float frag_damage = M_ARGV(4, float); - - if(DEATH_ISWEAPON(frag_deathtype, WEP_DEVASTATOR)) - if(frag_attacker == frag_target || frag_target.classname == "nade") - frag_damage = 0; - - if(autocvar_g_rm_laser) - if(DEATH_ISWEAPON(frag_deathtype, WEP_ELECTRO)) - if(frag_attacker == frag_target || (round_handler_IsActive() && !round_handler_IsRoundStarted())) - frag_damage = 0; - - M_ARGV(4, float) = frag_damage; -} - -MUTATOR_HOOKFUNCTION(rm, PlayerDies) -{ - // we do it this way, so rm can be toggled during the match - if(!autocvar_g_rm) { return; } - - float frag_deathtype = M_ARGV(3, float); - - if(DEATH_ISWEAPON(frag_deathtype, WEP_DEVASTATOR) || DEATH_ISWEAPON(frag_deathtype, WEP_ELECTRO)) - M_ARGV(4, float) = 1000; // always gib if it was a vaporizer death -} - -#endif diff --git a/qcsrc/common/mutators/mutator/rocketminsta/sv_rocketminsta.qc b/qcsrc/common/mutators/mutator/rocketminsta/sv_rocketminsta.qc new file mode 100644 index 0000000000..04d8099e02 --- /dev/null +++ b/qcsrc/common/mutators/mutator/rocketminsta/sv_rocketminsta.qc @@ -0,0 +1,39 @@ +#include "sv_rocketminsta.qh" + +#include +#include + +REGISTER_MUTATOR(rm, cvar("g_instagib")); + +MUTATOR_HOOKFUNCTION(rm, PlayerDamage_Calculate) +{ + // we do it this way, so rm can be toggled during the match + if(!autocvar_g_rm) { return; } + + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + float frag_deathtype = M_ARGV(3, float); + float frag_damage = M_ARGV(4, float); + + if(DEATH_ISWEAPON(frag_deathtype, WEP_DEVASTATOR)) + if(frag_attacker == frag_target || frag_target.classname == "nade") + frag_damage = 0; + + if(autocvar_g_rm_laser) + if(DEATH_ISWEAPON(frag_deathtype, WEP_ELECTRO)) + if(frag_attacker == frag_target || (round_handler_IsActive() && !round_handler_IsRoundStarted())) + frag_damage = 0; + + M_ARGV(4, float) = frag_damage; +} + +MUTATOR_HOOKFUNCTION(rm, PlayerDies) +{ + // we do it this way, so rm can be toggled during the match + if(!autocvar_g_rm) { return; } + + float frag_deathtype = M_ARGV(3, float); + + if(DEATH_ISWEAPON(frag_deathtype, WEP_DEVASTATOR) || DEATH_ISWEAPON(frag_deathtype, WEP_ELECTRO)) + M_ARGV(4, float) = 1000; // always gib if it was a vaporizer death +} diff --git a/qcsrc/common/mutators/mutator/rocketminsta/sv_rocketminsta.qh b/qcsrc/common/mutators/mutator/rocketminsta/sv_rocketminsta.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/rocketminsta/sv_rocketminsta.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/running_guns/_mod.inc b/qcsrc/common/mutators/mutator/running_guns/_mod.inc index f88b36a534..d1db34cf75 100644 --- a/qcsrc/common/mutators/mutator/running_guns/_mod.inc +++ b/qcsrc/common/mutators/mutator/running_guns/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/running_guns/_mod.qh b/qcsrc/common/mutators/mutator/running_guns/_mod.qh index 559be4c987..cc0c58eff4 100644 --- a/qcsrc/common/mutators/mutator/running_guns/_mod.qh +++ b/qcsrc/common/mutators/mutator/running_guns/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/running_guns/module.inc b/qcsrc/common/mutators/mutator/running_guns/module.inc deleted file mode 100644 index 036b70ff62..0000000000 --- a/qcsrc/common/mutators/mutator/running_guns/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "running_guns.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/running_guns/running_guns.qc b/qcsrc/common/mutators/mutator/running_guns/running_guns.qc deleted file mode 100644 index cad4d5f2fe..0000000000 --- a/qcsrc/common/mutators/mutator/running_guns/running_guns.qc +++ /dev/null @@ -1,14 +0,0 @@ -#ifdef IMPLEMENTATION - -bool autocvar_g_running_guns; - -REGISTER_MUTATOR(running_guns, autocvar_g_running_guns); - -MUTATOR_HOOKFUNCTION(running_guns, SetDefaultAlpha) -{ - default_player_alpha = -1; - default_weapon_alpha = +1; - return true; -} - -#endif diff --git a/qcsrc/common/mutators/mutator/running_guns/sv_running_guns.qc b/qcsrc/common/mutators/mutator/running_guns/sv_running_guns.qc new file mode 100644 index 0000000000..797108e012 --- /dev/null +++ b/qcsrc/common/mutators/mutator/running_guns/sv_running_guns.qc @@ -0,0 +1,11 @@ +#include "sv_running_guns.qh" + +bool autocvar_g_running_guns; +REGISTER_MUTATOR(running_guns, autocvar_g_running_guns); + +MUTATOR_HOOKFUNCTION(running_guns, SetDefaultAlpha) +{ + default_player_alpha = -1; + default_weapon_alpha = +1; + return true; +} diff --git a/qcsrc/common/mutators/mutator/running_guns/sv_running_guns.qh b/qcsrc/common/mutators/mutator/running_guns/sv_running_guns.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/running_guns/sv_running_guns.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/sandbox/_mod.inc b/qcsrc/common/mutators/mutator/sandbox/_mod.inc index 8e54c1f95e..569c25319b 100644 --- a/qcsrc/common/mutators/mutator/sandbox/_mod.inc +++ b/qcsrc/common/mutators/mutator/sandbox/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/sandbox/_mod.qh b/qcsrc/common/mutators/mutator/sandbox/_mod.qh index 81e250c7fa..86b112934d 100644 --- a/qcsrc/common/mutators/mutator/sandbox/_mod.qh +++ b/qcsrc/common/mutators/mutator/sandbox/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/sandbox/module.inc b/qcsrc/common/mutators/mutator/sandbox/module.inc deleted file mode 100644 index 0715d5b403..0000000000 --- a/qcsrc/common/mutators/mutator/sandbox/module.inc +++ /dev/null @@ -1,4 +0,0 @@ -#ifdef SVQC -#include "sandbox.qc" - -#endif diff --git a/qcsrc/common/mutators/mutator/sandbox/sandbox.qc b/qcsrc/common/mutators/mutator/sandbox/sandbox.qc deleted file mode 100644 index 577e29c6b0..0000000000 --- a/qcsrc/common/mutators/mutator/sandbox/sandbox.qc +++ /dev/null @@ -1,825 +0,0 @@ -#ifdef IMPLEMENTATION -int autocvar_g_sandbox_info; -bool autocvar_g_sandbox_readonly; -string autocvar_g_sandbox_storage_name; -float autocvar_g_sandbox_storage_autosave; -bool autocvar_g_sandbox_storage_autoload; -float autocvar_g_sandbox_editor_flood; -int autocvar_g_sandbox_editor_maxobjects; -int autocvar_g_sandbox_editor_free; -float autocvar_g_sandbox_editor_distance_spawn; -float autocvar_g_sandbox_editor_distance_edit; -float autocvar_g_sandbox_object_scale_min; -float autocvar_g_sandbox_object_scale_max; -float autocvar_g_sandbox_object_material_velocity_min; -float autocvar_g_sandbox_object_material_velocity_factor; - -float autosave_time; -void sandbox_Database_Load(); - -REGISTER_MUTATOR(sandbox, cvar("g_sandbox")) -{ - MUTATOR_ONADD - { - autosave_time = time + autocvar_g_sandbox_storage_autosave; // don't save the first server frame - if(autocvar_g_sandbox_storage_autoload) - sandbox_Database_Load(); - } -} - -const float MAX_STORAGE_ATTACHMENTS = 16; -float object_count; -.float object_flood; -.entity object_attach; -.string material; - -.float touch_timer; -void sandbox_ObjectFunction_Touch(entity this, entity toucher) -{ - // apply material impact effects - - if(!this.material) - return; - if(this.touch_timer > time) - return; // don't execute each frame - this.touch_timer = time + 0.1; - - // make particle count and sound volume depend on impact speed - float intensity; - intensity = vlen(this.velocity) + vlen(toucher.velocity); - if(intensity) // avoid divisions by 0 - intensity /= 2; // average the two velocities - if (!(intensity >= autocvar_g_sandbox_object_material_velocity_min)) - return; // impact not strong enough to do anything - // now offset intensity and apply it to the effects - intensity -= autocvar_g_sandbox_object_material_velocity_min; // start from minimum velocity, not actual velocity - intensity = bound(0, intensity * autocvar_g_sandbox_object_material_velocity_factor, 1); - - _sound(this, CH_TRIGGER, strcat("object/impact_", this.material, "_", ftos(ceil(random() * 5)) , ".wav"), VOL_BASE * intensity, ATTEN_NORM); - Send_Effect_(strcat("impact_", this.material), this.origin, '0 0 0', ceil(intensity * 10)); // allow a count from 1 to 10 -} - -void sandbox_ObjectFunction_Think(entity this) -{ - // decide if and how this object can be grabbed - if(autocvar_g_sandbox_readonly) - this.grab = 0; // no grabbing - else if(autocvar_g_sandbox_editor_free < 2 && this.crypto_idfp) - this.grab = 1; // owner only - else - this.grab = 3; // anyone - - // Object owner is stored via player UID, but we also need the owner as an entity (if the player is available on the server). - // Therefore, scan for all players, and update the owner as long as the player is present. We must always do this, - // since if the owning player disconnects, the object's owner should also be reset. - - // bots can't have objects - FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), LAMBDA( - if(this.crypto_idfp == it.crypto_idfp) - { - this.realowner = it; - break; - } - this.realowner = NULL; - )); - - this.nextthink = time; - - CSQCMODEL_AUTOUPDATE(this); -} - -.float old_solid, old_movetype; -entity sandbox_ObjectEdit_Get(entity this, float permissions) -{ - // Returns the traced entity if the player can edit it, and NULL if not. - // If permissions if false, the object is returned regardless of editing rights. - // Attached objects are SOLID_NOT and do not get traced. - - crosshair_trace_plusvisibletriggers(this); - if(vdist(this.origin - trace_ent.origin, >, autocvar_g_sandbox_editor_distance_edit)) - return NULL; // out of trace range - if(trace_ent.classname != "object") - return NULL; // entity is not an object - if(!permissions) - return trace_ent; // don't check permissions, anyone can edit this object - if(trace_ent.crypto_idfp == "") - return trace_ent; // the player who spawned this object did not have an UID, so anyone can edit it - if (!(trace_ent.realowner != this && autocvar_g_sandbox_editor_free < 2)) - return trace_ent; // object does not belong to the player, and players can only edit their own objects on this server - return NULL; -} - -void sandbox_ObjectEdit_Scale(entity e, float f) -{ - e.scale = f; - if(e.scale) - { - e.scale = bound(autocvar_g_sandbox_object_scale_min, e.scale, autocvar_g_sandbox_object_scale_max); - _setmodel(e, e.model); // reset mins and maxs based on mesh - setsize(e, e.mins * e.scale, e.maxs * e.scale); // adapt bounding box size to model size - } -} - -void sandbox_ObjectAttach_Remove(entity e); -void sandbox_ObjectAttach_Set(entity e, entity parent, string s) -{ - // attaches e to parent on string s - - // we can't attach to an attachment, for obvious reasons - sandbox_ObjectAttach_Remove(e); - - e.old_solid = e.solid; // persist solidity - e.old_movetype = e.move_movetype; // persist physics - set_movetype(e, MOVETYPE_FOLLOW); - e.solid = SOLID_NOT; - e.takedamage = DAMAGE_NO; - - setattachment(e, parent, s); - e.owner = parent; -} - -void sandbox_ObjectAttach_Remove(entity e) -{ - // detaches any object attached to e - - FOREACH_ENTITY_ENT(owner, e, - { - if(it.classname != "object") continue; - - vector org; - org = gettaginfo(it, 0); - setattachment(it, NULL, ""); - it.owner = NULL; - - // objects change origin and angles when detached, so apply previous position - setorigin(it, org); - it.angles = e.angles; // don't allow detached objects to spin or roll - - it.solid = it.old_solid; // restore persisted solidity - set_movetype(it, it.old_movetype); // restore persisted physics - it.takedamage = DAMAGE_AIM; - }); -} - -entity sandbox_ObjectSpawn(entity this, float database) -{ - // spawn a new object with default properties - - entity e = new(object); - e.takedamage = DAMAGE_AIM; - e.damageforcescale = 1; - e.solid = SOLID_BBOX; // SOLID_BSP would be best, but can lag the server badly - set_movetype(e, MOVETYPE_TOSS); - e.frame = 0; - e.skin = 0; - e.material = string_null; - settouch(e, sandbox_ObjectFunction_Touch); - setthink(e, sandbox_ObjectFunction_Think); - e.nextthink = time; - //e.effects |= EF_SELECTABLE; // don't do this all the time, maybe just when editing objects? - - if(!database) - { - // set the object's owner via player UID - // if the player does not have an UID, the owner cannot be stored and his objects may be edited by anyone - if(this.crypto_idfp != "") - e.crypto_idfp = strzone(this.crypto_idfp); - else - print_to(this, "^1SANDBOX - WARNING: ^7You spawned an object, but lack a player UID. ^1Your objects are not secured and can be edited by any player!"); - - // set public object information - e.netname = strzone(this.netname); // name of the owner - e.message = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // creation time - e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // last editing time - - // set origin and direction based on player position and view angle - makevectors(this.v_angle); - WarpZone_TraceLine(this.origin + this.view_ofs, this.origin + this.view_ofs + v_forward * autocvar_g_sandbox_editor_distance_spawn, MOVE_NORMAL, this); - setorigin(e, trace_endpos); - e.angles_y = this.v_angle.y; - } - - CSQCMODEL_AUTOINIT(e); - - object_count += 1; - return e; -} - -void sandbox_ObjectRemove(entity e) -{ - sandbox_ObjectAttach_Remove(e); // detach child objects - - // if the object being removed has been selected for attachment by a player, unset it - FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && it.object_attach == e, LAMBDA(it.object_attach = NULL)); - - if(e.material) { strunzone(e.material); e.material = string_null; } - if(e.crypto_idfp) { strunzone(e.crypto_idfp); e.crypto_idfp = string_null; } - if(e.netname) { strunzone(e.netname); e.netname = string_null; } - if(e.message) { strunzone(e.message); e.message = string_null; } - if(e.message2) { strunzone(e.message2); e.message2 = string_null; } - delete(e); - e = NULL; - - object_count -= 1; -} - -string port_string[MAX_STORAGE_ATTACHMENTS]; // fteqcc crashes if this isn't defined as a global - -string sandbox_ObjectPort_Save(entity e, float database) -{ - // save object properties, and return them as a string - float i = 0; - string s; - entity head; - - for(head = NULL; (head = find(head, classname, "object")); ) - { - // the main object needs to be first in the array [0] with attached objects following - float slot, physics, solidity; - if(head == e) // this is the main object, place it first - { - slot = 0; - solidity = head.solid; // applied solidity is normal solidity for children - physics = head.move_movetype; // applied physics are normal physics for parents - } - else if(head.owner == e) // child object, list them in order - { - i += 1; // children start from 1 - slot = i; - solidity = head.old_solid; // persisted solidity is normal solidity for children - physics = head.old_movetype; // persisted physics are normal physics for children - gettaginfo(head.owner, head.tag_index); // get the name of the tag our object is attached to, used further below - } - else - continue; - - // ---------------- OBJECT PROPERTY STORAGE: SAVE ---------------- - if(slot) - { - // properties stored only for child objects - if(gettaginfo_name) port_string[slot] = strcat(port_string[slot], "\"", gettaginfo_name, "\" "); else port_string[slot] = strcat(port_string[slot], "\"\" "); // none - } - else - { - // properties stored only for parent objects - if(database) - { - port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.origin), " "); - port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.angles), " "); - } - } - // properties stored for all objects - port_string[slot] = strcat(port_string[slot], "\"", head.model, "\" "); - port_string[slot] = strcat(port_string[slot], ftos(head.skin), " "); - port_string[slot] = strcat(port_string[slot], ftos(head.alpha), " "); - port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.colormod), " "); - port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.glowmod), " "); - port_string[slot] = strcat(port_string[slot], ftos(head.frame), " "); - port_string[slot] = strcat(port_string[slot], ftos(head.scale), " "); - port_string[slot] = strcat(port_string[slot], ftos(solidity), " "); - port_string[slot] = strcat(port_string[slot], ftos(physics), " "); - port_string[slot] = strcat(port_string[slot], ftos(head.damageforcescale), " "); - if(head.material) port_string[slot] = strcat(port_string[slot], "\"", head.material, "\" "); else port_string[slot] = strcat(port_string[slot], "\"\" "); // none - if(database) - { - // properties stored only for the database - if(head.crypto_idfp) port_string[slot] = strcat(port_string[slot], "\"", head.crypto_idfp, "\" "); else port_string[slot] = strcat(port_string[slot], "\"\" "); // none - port_string[slot] = strcat(port_string[slot], "\"", e.netname, "\" "); - port_string[slot] = strcat(port_string[slot], "\"", e.message, "\" "); - port_string[slot] = strcat(port_string[slot], "\"", e.message2, "\" "); - } - } - - // now apply the array to a simple string, with the ; symbol separating objects - s = ""; - for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i) - { - if(port_string[i]) - s = strcat(s, port_string[i], "; "); - port_string[i] = string_null; // fully clear the string - } - - return s; -} - -entity sandbox_ObjectPort_Load(entity this, string s, float database) -{ - // load object properties, and spawn a new object with them - float n, i; - entity e = NULL, parent = NULL; - - // separate objects between the ; symbols - n = tokenizebyseparator(s, "; "); - for(i = 0; i < n; ++i) - port_string[i] = argv(i); - - // now separate and apply the properties of each object - for(i = 0; i < n; ++i) - { - float argv_num; - string tagname = string_null; - argv_num = 0; - tokenize_console(port_string[i]); - e = sandbox_ObjectSpawn(this, database); - - // ---------------- OBJECT PROPERTY STORAGE: LOAD ---------------- - if(i) - { - // properties stored only for child objects - if(argv(argv_num) != "") tagname = argv(argv_num); else tagname = string_null; ++argv_num; - } - else - { - // properties stored only for parent objects - if(database) - { - setorigin(e, stov(argv(argv_num))); ++argv_num; - e.angles = stov(argv(argv_num)); ++argv_num; - } - parent = e; // mark parent objects as such - } - // properties stored for all objects - _setmodel(e, argv(argv_num)); ++argv_num; - e.skin = stof(argv(argv_num)); ++argv_num; - e.alpha = stof(argv(argv_num)); ++argv_num; - e.colormod = stov(argv(argv_num)); ++argv_num; - e.glowmod = stov(argv(argv_num)); ++argv_num; - e.frame = stof(argv(argv_num)); ++argv_num; - sandbox_ObjectEdit_Scale(e, stof(argv(argv_num))); ++argv_num; - e.solid = e.old_solid = stof(argv(argv_num)); ++argv_num; - e.old_movetype = stof(argv(argv_num)); ++argv_num; - set_movetype(e, e.old_movetype); - e.damageforcescale = stof(argv(argv_num)); ++argv_num; - if(e.material) strunzone(e.material); if(argv(argv_num) != "") e.material = strzone(argv(argv_num)); else e.material = string_null; ++argv_num; - if(database) - { - // properties stored only for the database - if(e.crypto_idfp) strunzone(e.crypto_idfp); if(argv(argv_num) != "") e.crypto_idfp = strzone(argv(argv_num)); else e.crypto_idfp = string_null; ++argv_num; - if(e.netname) strunzone(e.netname); e.netname = strzone(argv(argv_num)); ++argv_num; - if(e.message) strunzone(e.message); e.message = strzone(argv(argv_num)); ++argv_num; - if(e.message2) strunzone(e.message2); e.message2 = strzone(argv(argv_num)); ++argv_num; - } - - // attach last - if(i) - sandbox_ObjectAttach_Set(e, parent, tagname); - } - - for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i) - port_string[i] = string_null; // fully clear the string - - return e; -} - -void sandbox_Database_Save() -{ - // saves all objects to the database file - entity head; - string file_name; - float file_get; - - file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt"); - file_get = fopen(file_name, FILE_WRITE); - fputs(file_get, strcat("// sandbox storage \"", autocvar_g_sandbox_storage_name, "\" for map \"", GetMapname(), "\" last updated ", strftime(true, "%d-%m-%Y %H:%M:%S"))); - fputs(file_get, strcat(" containing ", ftos(object_count), " objects\n")); - - for(head = NULL; (head = find(head, classname, "object")); ) - { - // attached objects are persisted separately, ignore them here - if(head.owner != NULL) - continue; - - // use a line of text for each object, listing all properties - fputs(file_get, strcat(sandbox_ObjectPort_Save(head, true), "\n")); - } - fclose(file_get); -} - -void sandbox_Database_Load() -{ - // loads all objects from the database file - string file_read, file_name; - float file_get, i; - - file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt"); - file_get = fopen(file_name, FILE_READ); - if(file_get < 0) - { - if(autocvar_g_sandbox_info > 0) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7could not find storage file ^3", file_name, "^7, no objects were loaded\n")); - } - else - { - for (;;) - { - file_read = fgets(file_get); - if(file_read == "") - break; - if(substring(file_read, 0, 2) == "//") - continue; - if(substring(file_read, 0, 1) == "#") - continue; - - entity e; - e = sandbox_ObjectPort_Load(NULL, file_read, true); - - if(e.material) - { - // since objects are being loaded for the first time, precache material sounds for each - for (i = 1; i <= 5; i++) // 5 sounds in total - precache_sound(strcat("object/impact_", e.material, "_", ftos(i), ".wav")); - } - } - if(autocvar_g_sandbox_info > 0) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7successfully loaded storage file ^3", file_name, "\n")); - } - fclose(file_get); -} - -MUTATOR_HOOKFUNCTION(sandbox, SV_ParseClientCommand) -{ - if(MUTATOR_RETURNVALUE) // command was already handled? - return; - - entity player = M_ARGV(0, entity); - string cmd_name = M_ARGV(1, string); - int cmd_argc = M_ARGV(2, int); - - if(cmd_name == "g_sandbox") - { - if(autocvar_g_sandbox_readonly) - { - print_to(player, "^2SANDBOX - INFO: ^7Sandbox mode is active, but in read-only mode. Sandbox commands cannot be used"); - return true; - } - if(cmd_argc < 2) - { - print_to(player, "^2SANDBOX - INFO: ^7Sandbox mode is active. For usage information, type 'sandbox help'"); - return true; - } - - switch(argv(1)) - { - entity e; - int j; - string s; - - // ---------------- COMMAND: HELP ---------------- - case "help": - print_to(player, "You can use the following sandbox commands:"); - print_to(player, "^7\"^2object_spawn ^3models/foo/bar.md3^7\" spawns a new object in front of the player, and gives it the specified model"); - print_to(player, "^7\"^2object_remove^7\" removes the object the player is looking at. Players can only remove their own objects"); - print_to(player, "^7\"^2object_duplicate ^3value^7\" duplicates the object, if the player has copying rights over the original"); - print_to(player, "^3copy value ^7- copies the properties of the object to the specified client cvar"); - print_to(player, "^3paste value ^7- spawns an object with the given properties. Properties or cvars must be specified as follows; eg1: \"0 1 2 ...\", eg2: \"$cl_cvar\""); - print_to(player, "^7\"^2object_attach ^3property value^7\" attaches one object to another. Players can only attach their own objects"); - print_to(player, "^3get ^7- selects the object you are facing as the object to be attached"); - print_to(player, "^3set value ^7- attaches the previously selected object to the object you are facing, on the specified bone"); - print_to(player, "^3remove ^7- detaches all objects from the object you are facing"); - print_to(player, "^7\"^2object_edit ^3property value^7\" edits the given property of the object. Players can only edit their own objects"); - print_to(player, "^3skin value ^7- changes the skin of the object"); - print_to(player, "^3alpha value ^7- sets object transparency"); - print_to(player, "^3colormod \"value_x value_y value_z\" ^7- main object color"); - print_to(player, "^3glowmod \"value_x value_y value_z\" ^7- glow object color"); - print_to(player, "^3frame value ^7- object animation frame, for self-animated models"); - print_to(player, "^3scale value ^7- changes object scale. 0.5 is half size and 2 is double size"); - print_to(player, "^3solidity value ^7- object collisions, 0 = non-solid, 1 = solid"); - print_to(player, "^3physics value ^7- object physics, 0 = static, 1 = movable, 2 = physical"); - print_to(player, "^3force value ^7- amount of force applied to objects that are shot"); - print_to(player, "^3material value ^7- sets the material of the object. Default materials are: metal, stone, wood, flesh"); - print_to(player, "^7\"^2object_claim^7\" sets the player as the owner of the object, if he has the right to edit it"); - print_to(player, "^7\"^2object_info ^3value^7\" shows public information about the object"); - print_to(player, "^3object ^7- prints general information about the object, such as owner and creation / editing date"); - print_to(player, "^3mesh ^7- prints information about the object's mesh, including skeletal bones"); - print_to(player, "^3attachments ^7- prints information about the object's attachments"); - print_to(player, "^7The ^1drag object ^7key can be used to grab and carry objects. Players can only grab their own objects"); - return true; - - // ---------------- COMMAND: OBJECT, SPAWN ---------------- - case "object_spawn": - if(time < player.object_flood) - { - print_to(player, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(player.object_flood - time), " ^7seconds beofore spawning another object")); - return true; - } - player.object_flood = time + autocvar_g_sandbox_editor_flood; - if(object_count >= autocvar_g_sandbox_editor_maxobjects) - { - print_to(player, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time")); - return true; - } - if(cmd_argc < 3) - { - print_to(player, "^1SANDBOX - WARNING: ^7Attempted to spawn an object without specifying a model. Please specify the path to your model file after the 'object_spawn' command"); - return true; - } - if (!(fexists(argv(2)))) - { - print_to(player, "^1SANDBOX - WARNING: ^7Attempted to spawn an object with a non-existent model. Make sure the path to your model file is correct"); - return true; - } - - e = sandbox_ObjectSpawn(player, false); - _setmodel(e, argv(2)); - - if(autocvar_g_sandbox_info > 0) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " spawned an object at origin ^3", vtos(e.origin), "\n")); - return true; - - // ---------------- COMMAND: OBJECT, REMOVE ---------------- - case "object_remove": - e = sandbox_ObjectEdit_Get(player, true); - if(e != NULL) - { - if(autocvar_g_sandbox_info > 0) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " removed an object at origin ^3", vtos(e.origin), "\n")); - sandbox_ObjectRemove(e); - return true; - } - - print_to(player, "^1SANDBOX - WARNING: ^7Object could not be removed. Make sure you are facing an object that you have edit rights over"); - return true; - - // ---------------- COMMAND: OBJECT, DUPLICATE ---------------- - case "object_duplicate": - switch(argv(2)) - { - case "copy": - // copies customizable properties of the selected object to the clipboard cvar - e = sandbox_ObjectEdit_Get(player, autocvar_g_sandbox_editor_free); // can we copy objects we can't edit? - if(e != NULL) - { - s = sandbox_ObjectPort_Save(e, false); - s = strreplace("\"", "\\\"", s); - stuffcmd(player, strcat("set ", argv(3), " \"", s, "\"")); - - print_to(player, "^2SANDBOX - INFO: ^7Object copied to clipboard"); - return true; - } - print_to(player, "^1SANDBOX - WARNING: ^7Object could not be copied. Make sure you are facing an object that you have copy rights over"); - return true; - - case "paste": - // spawns a new object using the properties in the player's clipboard cvar - if(time < player.object_flood) - { - print_to(player, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(player.object_flood - time), " ^7seconds beofore spawning another object")); - return true; - } - player.object_flood = time + autocvar_g_sandbox_editor_flood; - if(argv(3) == "") // no object in clipboard - { - print_to(player, "^1SANDBOX - WARNING: ^7No object in clipboard. You must copy an object before you can paste it"); - return true; - } - if(object_count >= autocvar_g_sandbox_editor_maxobjects) - { - print_to(player, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time")); - return true; - } - e = sandbox_ObjectPort_Load(player, argv(3), false); - - print_to(player, "^2SANDBOX - INFO: ^7Object pasted successfully"); - if(autocvar_g_sandbox_info > 0) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " pasted an object at origin ^3", vtos(e.origin), "\n")); - return true; - } - return true; - - // ---------------- COMMAND: OBJECT, ATTACH ---------------- - case "object_attach": - switch(argv(2)) - { - case "get": - // select e as the object as meant to be attached - e = sandbox_ObjectEdit_Get(player, true); - if(e != NULL) - { - player.object_attach = e; - print_to(player, "^2SANDBOX - INFO: ^7Object selected for attachment"); - return true; - } - print_to(player, "^1SANDBOX - WARNING: ^7Object could not be selected for attachment. Make sure you are facing an object that you have edit rights over"); - return true; - case "set": - if(player.object_attach == NULL) - { - print_to(player, "^1SANDBOX - WARNING: ^7No object selected for attachment. Please select an object to be attached first."); - return true; - } - - // attaches the previously selected object to e - e = sandbox_ObjectEdit_Get(player, true); - if(e != NULL) - { - sandbox_ObjectAttach_Set(player.object_attach, e, argv(3)); - player.object_attach = NULL; // object was attached, no longer keep it scheduled for attachment - print_to(player, "^2SANDBOX - INFO: ^7Object attached successfully"); - if(autocvar_g_sandbox_info > 1) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " attached objects at origin ^3", vtos(e.origin), "\n")); - return true; - } - print_to(player, "^1SANDBOX - WARNING: ^7Object could not be attached to the parent. Make sure you are facing an object that you have edit rights over"); - return true; - case "remove": - // removes e if it was attached - e = sandbox_ObjectEdit_Get(player, true); - if(e != NULL) - { - sandbox_ObjectAttach_Remove(e); - print_to(player, "^2SANDBOX - INFO: ^7Child objects detached successfully"); - if(autocvar_g_sandbox_info > 1) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " detached objects at origin ^3", vtos(e.origin), "\n")); - return true; - } - print_to(player, "^1SANDBOX - WARNING: ^7Child objects could not be detached. Make sure you are facing an object that you have edit rights over"); - return true; - } - return true; - - // ---------------- COMMAND: OBJECT, EDIT ---------------- - case "object_edit": - if(argv(2) == "") - { - print_to(player, "^1SANDBOX - WARNING: ^7Too few parameters. You must specify a property to edit"); - return true; - } - - e = sandbox_ObjectEdit_Get(player, true); - if(e != NULL) - { - switch(argv(2)) - { - case "skin": - e.skin = stof(argv(3)); - break; - case "alpha": - e.alpha = stof(argv(3)); - break; - case "color_main": - e.colormod = stov(argv(3)); - break; - case "color_glow": - e.glowmod = stov(argv(3)); - break; - case "frame": - e.frame = stof(argv(3)); - break; - case "scale": - sandbox_ObjectEdit_Scale(e, stof(argv(3))); - break; - case "solidity": - switch(argv(3)) - { - case "0": // non-solid - e.solid = SOLID_TRIGGER; - break; - case "1": // solid - e.solid = SOLID_BBOX; - break; - default: - break; - } - case "physics": - switch(argv(3)) - { - case "0": // static - set_movetype(e, MOVETYPE_NONE); - break; - case "1": // movable - set_movetype(e, MOVETYPE_TOSS); - break; - case "2": // physical - set_movetype(e, MOVETYPE_PHYSICS); - break; - default: - break; - } - break; - case "force": - e.damageforcescale = stof(argv(3)); - break; - case "material": - if(e.material) strunzone(e.material); - if(argv(3)) - { - for (j = 1; j <= 5; j++) // precache material sounds, 5 in total - precache_sound(strcat("object/impact_", argv(3), "_", ftos(j), ".wav")); - e.material = strzone(argv(3)); - } - else - e.material = string_null; // no material - break; - default: - print_to(player, "^1SANDBOX - WARNING: ^7Invalid object property. For usage information, type 'sandbox help'"); - return true; - } - - // update last editing time - if(e.message2) strunzone(e.message2); - e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); - - if(autocvar_g_sandbox_info > 1) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " edited property ^3", argv(2), " ^7of an object at origin ^3", vtos(e.origin), "\n")); - return true; - } - - print_to(player, "^1SANDBOX - WARNING: ^7Object could not be edited. Make sure you are facing an object that you have edit rights over"); - return true; - - // ---------------- COMMAND: OBJECT, CLAIM ---------------- - case "object_claim": - // if the player can edit an object but is not its owner, this can be used to claim that object - if(player.crypto_idfp == "") - { - print_to(player, "^1SANDBOX - WARNING: ^7You do not have a player UID, and cannot claim objects"); - return true; - } - e = sandbox_ObjectEdit_Get(player, true); - if(e != NULL) - { - // update the owner's name - // Do this before checking if you're already the owner and skipping if such, so we - // also update the player's nickname if he changed it (but has the same player UID) - if(e.netname != player.netname) - { - if(e.netname) strunzone(e.netname); - e.netname = strzone(player.netname); - print_to(player, "^2SANDBOX - INFO: ^7Object owner name updated"); - } - - if(e.crypto_idfp == player.crypto_idfp) - { - print_to(player, "^2SANDBOX - INFO: ^7Object is already yours, nothing to claim"); - return true; - } - - if(e.crypto_idfp) strunzone(e.crypto_idfp); - e.crypto_idfp = strzone(player.crypto_idfp); - - print_to(player, "^2SANDBOX - INFO: ^7Object claimed successfully"); - } - print_to(player, "^1SANDBOX - WARNING: ^7Object could not be claimed. Make sure you are facing an object that you have edit rights over"); - return true; - - // ---------------- COMMAND: OBJECT, INFO ---------------- - case "object_info": - // prints public information about the object to the player - e = sandbox_ObjectEdit_Get(player, false); - if(e != NULL) - { - switch(argv(2)) - { - case "object": - print_to(player, strcat("^2SANDBOX - INFO: ^7Object is owned by \"^7", e.netname, "^7\", created \"^3", e.message, "^7\", last edited \"^3", e.message2, "^7\"")); - return true; - case "mesh": - s = ""; - FOR_EACH_TAG(e) - s = strcat(s, "^7\"^5", gettaginfo_name, "^7\", "); - print_to(player, strcat("^2SANDBOX - INFO: ^7Object mesh is \"^3", e.model, "^7\" at animation frame ^3", ftos(e.frame), " ^7containing the following tags: ", s)); - return true; - case "attachments": - // this should show the same info as 'mesh' but for attachments - s = ""; - j = 0; - FOREACH_ENTITY_ENT(owner, e, - { - if(it.classname != "object") continue; - - ++j; // start from 1 - gettaginfo(e, it.tag_index); - s = strcat(s, "^1attachment ", ftos(j), "^7 has mesh \"^3", it.model, "^7\" at animation frame ^3", ftos(it.frame)); - s = strcat(s, "^7 and is attached to bone \"^5", gettaginfo_name, "^7\", "); - }); - if(j) // object contains attachments - print_to(player, strcat("^2SANDBOX - INFO: ^7Object contains the following ^1", ftos(j), "^7 attachment(s): ", s)); - else - print_to(player, "^2SANDBOX - INFO: ^7Object contains no attachments"); - return true; - } - } - print_to(player, "^1SANDBOX - WARNING: ^7No information could be found. Make sure you are facing an object"); - return true; - - // ---------------- COMMAND: DEFAULT ---------------- - default: - print_to(player, "Invalid command. For usage information, type 'sandbox help'"); - return true; - } - } -} - -MUTATOR_HOOKFUNCTION(sandbox, SV_StartFrame) -{ - if(!autocvar_g_sandbox_storage_autosave) - return; - if(time < autosave_time) - return; - autosave_time = time + autocvar_g_sandbox_storage_autosave; - - sandbox_Database_Save(); - - return true; -} -#endif diff --git a/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qc b/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qc new file mode 100644 index 0000000000..d0739c2e44 --- /dev/null +++ b/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qc @@ -0,0 +1,825 @@ +#include "sv_sandbox.qh" + +int autocvar_g_sandbox_info; +bool autocvar_g_sandbox_readonly; +string autocvar_g_sandbox_storage_name; +float autocvar_g_sandbox_storage_autosave; +bool autocvar_g_sandbox_storage_autoload; +float autocvar_g_sandbox_editor_flood; +int autocvar_g_sandbox_editor_maxobjects; +int autocvar_g_sandbox_editor_free; +float autocvar_g_sandbox_editor_distance_spawn; +float autocvar_g_sandbox_editor_distance_edit; +float autocvar_g_sandbox_object_scale_min; +float autocvar_g_sandbox_object_scale_max; +float autocvar_g_sandbox_object_material_velocity_min; +float autocvar_g_sandbox_object_material_velocity_factor; + +float autosave_time; +void sandbox_Database_Load(); + +REGISTER_MUTATOR(sandbox, cvar("g_sandbox")) +{ + MUTATOR_ONADD + { + autosave_time = time + autocvar_g_sandbox_storage_autosave; // don't save the first server frame + if(autocvar_g_sandbox_storage_autoload) + sandbox_Database_Load(); + } +} + +const float MAX_STORAGE_ATTACHMENTS = 16; +float object_count; +.float object_flood; +.entity object_attach; +.string material; + +.float touch_timer; +void sandbox_ObjectFunction_Touch(entity this, entity toucher) +{ + // apply material impact effects + + if(!this.material) + return; + if(this.touch_timer > time) + return; // don't execute each frame + this.touch_timer = time + 0.1; + + // make particle count and sound volume depend on impact speed + float intensity; + intensity = vlen(this.velocity) + vlen(toucher.velocity); + if(intensity) // avoid divisions by 0 + intensity /= 2; // average the two velocities + if (!(intensity >= autocvar_g_sandbox_object_material_velocity_min)) + return; // impact not strong enough to do anything + // now offset intensity and apply it to the effects + intensity -= autocvar_g_sandbox_object_material_velocity_min; // start from minimum velocity, not actual velocity + intensity = bound(0, intensity * autocvar_g_sandbox_object_material_velocity_factor, 1); + + _sound(this, CH_TRIGGER, strcat("object/impact_", this.material, "_", ftos(ceil(random() * 5)) , ".wav"), VOL_BASE * intensity, ATTEN_NORM); + Send_Effect_(strcat("impact_", this.material), this.origin, '0 0 0', ceil(intensity * 10)); // allow a count from 1 to 10 +} + +void sandbox_ObjectFunction_Think(entity this) +{ + // decide if and how this object can be grabbed + if(autocvar_g_sandbox_readonly) + this.grab = 0; // no grabbing + else if(autocvar_g_sandbox_editor_free < 2 && this.crypto_idfp) + this.grab = 1; // owner only + else + this.grab = 3; // anyone + + // Object owner is stored via player UID, but we also need the owner as an entity (if the player is available on the server). + // Therefore, scan for all players, and update the owner as long as the player is present. We must always do this, + // since if the owning player disconnects, the object's owner should also be reset. + + // bots can't have objects + FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), LAMBDA( + if(this.crypto_idfp == it.crypto_idfp) + { + this.realowner = it; + break; + } + this.realowner = NULL; + )); + + this.nextthink = time; + + CSQCMODEL_AUTOUPDATE(this); +} + +.float old_solid, old_movetype; +entity sandbox_ObjectEdit_Get(entity this, float permissions) +{ + // Returns the traced entity if the player can edit it, and NULL if not. + // If permissions if false, the object is returned regardless of editing rights. + // Attached objects are SOLID_NOT and do not get traced. + + crosshair_trace_plusvisibletriggers(this); + if(vdist(this.origin - trace_ent.origin, >, autocvar_g_sandbox_editor_distance_edit)) + return NULL; // out of trace range + if(trace_ent.classname != "object") + return NULL; // entity is not an object + if(!permissions) + return trace_ent; // don't check permissions, anyone can edit this object + if(trace_ent.crypto_idfp == "") + return trace_ent; // the player who spawned this object did not have an UID, so anyone can edit it + if (!(trace_ent.realowner != this && autocvar_g_sandbox_editor_free < 2)) + return trace_ent; // object does not belong to the player, and players can only edit their own objects on this server + return NULL; +} + +void sandbox_ObjectEdit_Scale(entity e, float f) +{ + e.scale = f; + if(e.scale) + { + e.scale = bound(autocvar_g_sandbox_object_scale_min, e.scale, autocvar_g_sandbox_object_scale_max); + _setmodel(e, e.model); // reset mins and maxs based on mesh + setsize(e, e.mins * e.scale, e.maxs * e.scale); // adapt bounding box size to model size + } +} + +void sandbox_ObjectAttach_Remove(entity e); +void sandbox_ObjectAttach_Set(entity e, entity parent, string s) +{ + // attaches e to parent on string s + + // we can't attach to an attachment, for obvious reasons + sandbox_ObjectAttach_Remove(e); + + e.old_solid = e.solid; // persist solidity + e.old_movetype = e.move_movetype; // persist physics + set_movetype(e, MOVETYPE_FOLLOW); + e.solid = SOLID_NOT; + e.takedamage = DAMAGE_NO; + + setattachment(e, parent, s); + e.owner = parent; +} + +void sandbox_ObjectAttach_Remove(entity e) +{ + // detaches any object attached to e + + FOREACH_ENTITY_ENT(owner, e, + { + if(it.classname != "object") continue; + + vector org; + org = gettaginfo(it, 0); + setattachment(it, NULL, ""); + it.owner = NULL; + + // objects change origin and angles when detached, so apply previous position + setorigin(it, org); + it.angles = e.angles; // don't allow detached objects to spin or roll + + it.solid = it.old_solid; // restore persisted solidity + set_movetype(it, it.old_movetype); // restore persisted physics + it.takedamage = DAMAGE_AIM; + }); +} + +entity sandbox_ObjectSpawn(entity this, float database) +{ + // spawn a new object with default properties + + entity e = new(object); + e.takedamage = DAMAGE_AIM; + e.damageforcescale = 1; + e.solid = SOLID_BBOX; // SOLID_BSP would be best, but can lag the server badly + set_movetype(e, MOVETYPE_TOSS); + e.frame = 0; + e.skin = 0; + e.material = string_null; + settouch(e, sandbox_ObjectFunction_Touch); + setthink(e, sandbox_ObjectFunction_Think); + e.nextthink = time; + //e.effects |= EF_SELECTABLE; // don't do this all the time, maybe just when editing objects? + + if(!database) + { + // set the object's owner via player UID + // if the player does not have an UID, the owner cannot be stored and his objects may be edited by anyone + if(this.crypto_idfp != "") + e.crypto_idfp = strzone(this.crypto_idfp); + else + print_to(this, "^1SANDBOX - WARNING: ^7You spawned an object, but lack a player UID. ^1Your objects are not secured and can be edited by any player!"); + + // set public object information + e.netname = strzone(this.netname); // name of the owner + e.message = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // creation time + e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // last editing time + + // set origin and direction based on player position and view angle + makevectors(this.v_angle); + WarpZone_TraceLine(this.origin + this.view_ofs, this.origin + this.view_ofs + v_forward * autocvar_g_sandbox_editor_distance_spawn, MOVE_NORMAL, this); + setorigin(e, trace_endpos); + e.angles_y = this.v_angle.y; + } + + CSQCMODEL_AUTOINIT(e); + + object_count += 1; + return e; +} + +void sandbox_ObjectRemove(entity e) +{ + sandbox_ObjectAttach_Remove(e); // detach child objects + + // if the object being removed has been selected for attachment by a player, unset it + FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && it.object_attach == e, LAMBDA(it.object_attach = NULL)); + + if(e.material) { strunzone(e.material); e.material = string_null; } + if(e.crypto_idfp) { strunzone(e.crypto_idfp); e.crypto_idfp = string_null; } + if(e.netname) { strunzone(e.netname); e.netname = string_null; } + if(e.message) { strunzone(e.message); e.message = string_null; } + if(e.message2) { strunzone(e.message2); e.message2 = string_null; } + delete(e); + e = NULL; + + object_count -= 1; +} + +string port_string[MAX_STORAGE_ATTACHMENTS]; // fteqcc crashes if this isn't defined as a global + +string sandbox_ObjectPort_Save(entity e, float database) +{ + // save object properties, and return them as a string + float i = 0; + string s; + entity head; + + for(head = NULL; (head = find(head, classname, "object")); ) + { + // the main object needs to be first in the array [0] with attached objects following + float slot, physics, solidity; + if(head == e) // this is the main object, place it first + { + slot = 0; + solidity = head.solid; // applied solidity is normal solidity for children + physics = head.move_movetype; // applied physics are normal physics for parents + } + else if(head.owner == e) // child object, list them in order + { + i += 1; // children start from 1 + slot = i; + solidity = head.old_solid; // persisted solidity is normal solidity for children + physics = head.old_movetype; // persisted physics are normal physics for children + gettaginfo(head.owner, head.tag_index); // get the name of the tag our object is attached to, used further below + } + else + continue; + + // ---------------- OBJECT PROPERTY STORAGE: SAVE ---------------- + if(slot) + { + // properties stored only for child objects + if(gettaginfo_name) port_string[slot] = strcat(port_string[slot], "\"", gettaginfo_name, "\" "); else port_string[slot] = strcat(port_string[slot], "\"\" "); // none + } + else + { + // properties stored only for parent objects + if(database) + { + port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.origin), " "); + port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.angles), " "); + } + } + // properties stored for all objects + port_string[slot] = strcat(port_string[slot], "\"", head.model, "\" "); + port_string[slot] = strcat(port_string[slot], ftos(head.skin), " "); + port_string[slot] = strcat(port_string[slot], ftos(head.alpha), " "); + port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.colormod), " "); + port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.glowmod), " "); + port_string[slot] = strcat(port_string[slot], ftos(head.frame), " "); + port_string[slot] = strcat(port_string[slot], ftos(head.scale), " "); + port_string[slot] = strcat(port_string[slot], ftos(solidity), " "); + port_string[slot] = strcat(port_string[slot], ftos(physics), " "); + port_string[slot] = strcat(port_string[slot], ftos(head.damageforcescale), " "); + if(head.material) port_string[slot] = strcat(port_string[slot], "\"", head.material, "\" "); else port_string[slot] = strcat(port_string[slot], "\"\" "); // none + if(database) + { + // properties stored only for the database + if(head.crypto_idfp) port_string[slot] = strcat(port_string[slot], "\"", head.crypto_idfp, "\" "); else port_string[slot] = strcat(port_string[slot], "\"\" "); // none + port_string[slot] = strcat(port_string[slot], "\"", e.netname, "\" "); + port_string[slot] = strcat(port_string[slot], "\"", e.message, "\" "); + port_string[slot] = strcat(port_string[slot], "\"", e.message2, "\" "); + } + } + + // now apply the array to a simple string, with the ; symbol separating objects + s = ""; + for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i) + { + if(port_string[i]) + s = strcat(s, port_string[i], "; "); + port_string[i] = string_null; // fully clear the string + } + + return s; +} + +entity sandbox_ObjectPort_Load(entity this, string s, float database) +{ + // load object properties, and spawn a new object with them + float n, i; + entity e = NULL, parent = NULL; + + // separate objects between the ; symbols + n = tokenizebyseparator(s, "; "); + for(i = 0; i < n; ++i) + port_string[i] = argv(i); + + // now separate and apply the properties of each object + for(i = 0; i < n; ++i) + { + float argv_num; + string tagname = string_null; + argv_num = 0; + tokenize_console(port_string[i]); + e = sandbox_ObjectSpawn(this, database); + + // ---------------- OBJECT PROPERTY STORAGE: LOAD ---------------- + if(i) + { + // properties stored only for child objects + if(argv(argv_num) != "") tagname = argv(argv_num); else tagname = string_null; ++argv_num; + } + else + { + // properties stored only for parent objects + if(database) + { + setorigin(e, stov(argv(argv_num))); ++argv_num; + e.angles = stov(argv(argv_num)); ++argv_num; + } + parent = e; // mark parent objects as such + } + // properties stored for all objects + _setmodel(e, argv(argv_num)); ++argv_num; + e.skin = stof(argv(argv_num)); ++argv_num; + e.alpha = stof(argv(argv_num)); ++argv_num; + e.colormod = stov(argv(argv_num)); ++argv_num; + e.glowmod = stov(argv(argv_num)); ++argv_num; + e.frame = stof(argv(argv_num)); ++argv_num; + sandbox_ObjectEdit_Scale(e, stof(argv(argv_num))); ++argv_num; + e.solid = e.old_solid = stof(argv(argv_num)); ++argv_num; + e.old_movetype = stof(argv(argv_num)); ++argv_num; + set_movetype(e, e.old_movetype); + e.damageforcescale = stof(argv(argv_num)); ++argv_num; + if(e.material) strunzone(e.material); if(argv(argv_num) != "") e.material = strzone(argv(argv_num)); else e.material = string_null; ++argv_num; + if(database) + { + // properties stored only for the database + if(e.crypto_idfp) strunzone(e.crypto_idfp); if(argv(argv_num) != "") e.crypto_idfp = strzone(argv(argv_num)); else e.crypto_idfp = string_null; ++argv_num; + if(e.netname) strunzone(e.netname); e.netname = strzone(argv(argv_num)); ++argv_num; + if(e.message) strunzone(e.message); e.message = strzone(argv(argv_num)); ++argv_num; + if(e.message2) strunzone(e.message2); e.message2 = strzone(argv(argv_num)); ++argv_num; + } + + // attach last + if(i) + sandbox_ObjectAttach_Set(e, parent, tagname); + } + + for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i) + port_string[i] = string_null; // fully clear the string + + return e; +} + +void sandbox_Database_Save() +{ + // saves all objects to the database file + entity head; + string file_name; + float file_get; + + file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt"); + file_get = fopen(file_name, FILE_WRITE); + fputs(file_get, strcat("// sandbox storage \"", autocvar_g_sandbox_storage_name, "\" for map \"", GetMapname(), "\" last updated ", strftime(true, "%d-%m-%Y %H:%M:%S"))); + fputs(file_get, strcat(" containing ", ftos(object_count), " objects\n")); + + for(head = NULL; (head = find(head, classname, "object")); ) + { + // attached objects are persisted separately, ignore them here + if(head.owner != NULL) + continue; + + // use a line of text for each object, listing all properties + fputs(file_get, strcat(sandbox_ObjectPort_Save(head, true), "\n")); + } + fclose(file_get); +} + +void sandbox_Database_Load() +{ + // loads all objects from the database file + string file_read, file_name; + float file_get, i; + + file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt"); + file_get = fopen(file_name, FILE_READ); + if(file_get < 0) + { + if(autocvar_g_sandbox_info > 0) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7could not find storage file ^3", file_name, "^7, no objects were loaded\n")); + } + else + { + for (;;) + { + file_read = fgets(file_get); + if(file_read == "") + break; + if(substring(file_read, 0, 2) == "//") + continue; + if(substring(file_read, 0, 1) == "#") + continue; + + entity e; + e = sandbox_ObjectPort_Load(NULL, file_read, true); + + if(e.material) + { + // since objects are being loaded for the first time, precache material sounds for each + for (i = 1; i <= 5; i++) // 5 sounds in total + precache_sound(strcat("object/impact_", e.material, "_", ftos(i), ".wav")); + } + } + if(autocvar_g_sandbox_info > 0) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7successfully loaded storage file ^3", file_name, "\n")); + } + fclose(file_get); +} + +MUTATOR_HOOKFUNCTION(sandbox, SV_ParseClientCommand) +{ + if(MUTATOR_RETURNVALUE) // command was already handled? + return; + + entity player = M_ARGV(0, entity); + string cmd_name = M_ARGV(1, string); + int cmd_argc = M_ARGV(2, int); + + if(cmd_name == "g_sandbox") + { + if(autocvar_g_sandbox_readonly) + { + print_to(player, "^2SANDBOX - INFO: ^7Sandbox mode is active, but in read-only mode. Sandbox commands cannot be used"); + return true; + } + if(cmd_argc < 2) + { + print_to(player, "^2SANDBOX - INFO: ^7Sandbox mode is active. For usage information, type 'sandbox help'"); + return true; + } + + switch(argv(1)) + { + entity e; + int j; + string s; + + // ---------------- COMMAND: HELP ---------------- + case "help": + print_to(player, "You can use the following sandbox commands:"); + print_to(player, "^7\"^2object_spawn ^3models/foo/bar.md3^7\" spawns a new object in front of the player, and gives it the specified model"); + print_to(player, "^7\"^2object_remove^7\" removes the object the player is looking at. Players can only remove their own objects"); + print_to(player, "^7\"^2object_duplicate ^3value^7\" duplicates the object, if the player has copying rights over the original"); + print_to(player, "^3copy value ^7- copies the properties of the object to the specified client cvar"); + print_to(player, "^3paste value ^7- spawns an object with the given properties. Properties or cvars must be specified as follows; eg1: \"0 1 2 ...\", eg2: \"$cl_cvar\""); + print_to(player, "^7\"^2object_attach ^3property value^7\" attaches one object to another. Players can only attach their own objects"); + print_to(player, "^3get ^7- selects the object you are facing as the object to be attached"); + print_to(player, "^3set value ^7- attaches the previously selected object to the object you are facing, on the specified bone"); + print_to(player, "^3remove ^7- detaches all objects from the object you are facing"); + print_to(player, "^7\"^2object_edit ^3property value^7\" edits the given property of the object. Players can only edit their own objects"); + print_to(player, "^3skin value ^7- changes the skin of the object"); + print_to(player, "^3alpha value ^7- sets object transparency"); + print_to(player, "^3colormod \"value_x value_y value_z\" ^7- main object color"); + print_to(player, "^3glowmod \"value_x value_y value_z\" ^7- glow object color"); + print_to(player, "^3frame value ^7- object animation frame, for self-animated models"); + print_to(player, "^3scale value ^7- changes object scale. 0.5 is half size and 2 is double size"); + print_to(player, "^3solidity value ^7- object collisions, 0 = non-solid, 1 = solid"); + print_to(player, "^3physics value ^7- object physics, 0 = static, 1 = movable, 2 = physical"); + print_to(player, "^3force value ^7- amount of force applied to objects that are shot"); + print_to(player, "^3material value ^7- sets the material of the object. Default materials are: metal, stone, wood, flesh"); + print_to(player, "^7\"^2object_claim^7\" sets the player as the owner of the object, if he has the right to edit it"); + print_to(player, "^7\"^2object_info ^3value^7\" shows public information about the object"); + print_to(player, "^3object ^7- prints general information about the object, such as owner and creation / editing date"); + print_to(player, "^3mesh ^7- prints information about the object's mesh, including skeletal bones"); + print_to(player, "^3attachments ^7- prints information about the object's attachments"); + print_to(player, "^7The ^1drag object ^7key can be used to grab and carry objects. Players can only grab their own objects"); + return true; + + // ---------------- COMMAND: OBJECT, SPAWN ---------------- + case "object_spawn": + if(time < player.object_flood) + { + print_to(player, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(player.object_flood - time), " ^7seconds beofore spawning another object")); + return true; + } + player.object_flood = time + autocvar_g_sandbox_editor_flood; + if(object_count >= autocvar_g_sandbox_editor_maxobjects) + { + print_to(player, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time")); + return true; + } + if(cmd_argc < 3) + { + print_to(player, "^1SANDBOX - WARNING: ^7Attempted to spawn an object without specifying a model. Please specify the path to your model file after the 'object_spawn' command"); + return true; + } + if (!(fexists(argv(2)))) + { + print_to(player, "^1SANDBOX - WARNING: ^7Attempted to spawn an object with a non-existent model. Make sure the path to your model file is correct"); + return true; + } + + e = sandbox_ObjectSpawn(player, false); + _setmodel(e, argv(2)); + + if(autocvar_g_sandbox_info > 0) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " spawned an object at origin ^3", vtos(e.origin), "\n")); + return true; + + // ---------------- COMMAND: OBJECT, REMOVE ---------------- + case "object_remove": + e = sandbox_ObjectEdit_Get(player, true); + if(e != NULL) + { + if(autocvar_g_sandbox_info > 0) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " removed an object at origin ^3", vtos(e.origin), "\n")); + sandbox_ObjectRemove(e); + return true; + } + + print_to(player, "^1SANDBOX - WARNING: ^7Object could not be removed. Make sure you are facing an object that you have edit rights over"); + return true; + + // ---------------- COMMAND: OBJECT, DUPLICATE ---------------- + case "object_duplicate": + switch(argv(2)) + { + case "copy": + // copies customizable properties of the selected object to the clipboard cvar + e = sandbox_ObjectEdit_Get(player, autocvar_g_sandbox_editor_free); // can we copy objects we can't edit? + if(e != NULL) + { + s = sandbox_ObjectPort_Save(e, false); + s = strreplace("\"", "\\\"", s); + stuffcmd(player, strcat("set ", argv(3), " \"", s, "\"")); + + print_to(player, "^2SANDBOX - INFO: ^7Object copied to clipboard"); + return true; + } + print_to(player, "^1SANDBOX - WARNING: ^7Object could not be copied. Make sure you are facing an object that you have copy rights over"); + return true; + + case "paste": + // spawns a new object using the properties in the player's clipboard cvar + if(time < player.object_flood) + { + print_to(player, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(player.object_flood - time), " ^7seconds beofore spawning another object")); + return true; + } + player.object_flood = time + autocvar_g_sandbox_editor_flood; + if(argv(3) == "") // no object in clipboard + { + print_to(player, "^1SANDBOX - WARNING: ^7No object in clipboard. You must copy an object before you can paste it"); + return true; + } + if(object_count >= autocvar_g_sandbox_editor_maxobjects) + { + print_to(player, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time")); + return true; + } + e = sandbox_ObjectPort_Load(player, argv(3), false); + + print_to(player, "^2SANDBOX - INFO: ^7Object pasted successfully"); + if(autocvar_g_sandbox_info > 0) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " pasted an object at origin ^3", vtos(e.origin), "\n")); + return true; + } + return true; + + // ---------------- COMMAND: OBJECT, ATTACH ---------------- + case "object_attach": + switch(argv(2)) + { + case "get": + // select e as the object as meant to be attached + e = sandbox_ObjectEdit_Get(player, true); + if(e != NULL) + { + player.object_attach = e; + print_to(player, "^2SANDBOX - INFO: ^7Object selected for attachment"); + return true; + } + print_to(player, "^1SANDBOX - WARNING: ^7Object could not be selected for attachment. Make sure you are facing an object that you have edit rights over"); + return true; + case "set": + if(player.object_attach == NULL) + { + print_to(player, "^1SANDBOX - WARNING: ^7No object selected for attachment. Please select an object to be attached first."); + return true; + } + + // attaches the previously selected object to e + e = sandbox_ObjectEdit_Get(player, true); + if(e != NULL) + { + sandbox_ObjectAttach_Set(player.object_attach, e, argv(3)); + player.object_attach = NULL; // object was attached, no longer keep it scheduled for attachment + print_to(player, "^2SANDBOX - INFO: ^7Object attached successfully"); + if(autocvar_g_sandbox_info > 1) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " attached objects at origin ^3", vtos(e.origin), "\n")); + return true; + } + print_to(player, "^1SANDBOX - WARNING: ^7Object could not be attached to the parent. Make sure you are facing an object that you have edit rights over"); + return true; + case "remove": + // removes e if it was attached + e = sandbox_ObjectEdit_Get(player, true); + if(e != NULL) + { + sandbox_ObjectAttach_Remove(e); + print_to(player, "^2SANDBOX - INFO: ^7Child objects detached successfully"); + if(autocvar_g_sandbox_info > 1) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " detached objects at origin ^3", vtos(e.origin), "\n")); + return true; + } + print_to(player, "^1SANDBOX - WARNING: ^7Child objects could not be detached. Make sure you are facing an object that you have edit rights over"); + return true; + } + return true; + + // ---------------- COMMAND: OBJECT, EDIT ---------------- + case "object_edit": + if(argv(2) == "") + { + print_to(player, "^1SANDBOX - WARNING: ^7Too few parameters. You must specify a property to edit"); + return true; + } + + e = sandbox_ObjectEdit_Get(player, true); + if(e != NULL) + { + switch(argv(2)) + { + case "skin": + e.skin = stof(argv(3)); + break; + case "alpha": + e.alpha = stof(argv(3)); + break; + case "color_main": + e.colormod = stov(argv(3)); + break; + case "color_glow": + e.glowmod = stov(argv(3)); + break; + case "frame": + e.frame = stof(argv(3)); + break; + case "scale": + sandbox_ObjectEdit_Scale(e, stof(argv(3))); + break; + case "solidity": + switch(argv(3)) + { + case "0": // non-solid + e.solid = SOLID_TRIGGER; + break; + case "1": // solid + e.solid = SOLID_BBOX; + break; + default: + break; + } + case "physics": + switch(argv(3)) + { + case "0": // static + set_movetype(e, MOVETYPE_NONE); + break; + case "1": // movable + set_movetype(e, MOVETYPE_TOSS); + break; + case "2": // physical + set_movetype(e, MOVETYPE_PHYSICS); + break; + default: + break; + } + break; + case "force": + e.damageforcescale = stof(argv(3)); + break; + case "material": + if(e.material) strunzone(e.material); + if(argv(3)) + { + for (j = 1; j <= 5; j++) // precache material sounds, 5 in total + precache_sound(strcat("object/impact_", argv(3), "_", ftos(j), ".wav")); + e.material = strzone(argv(3)); + } + else + e.material = string_null; // no material + break; + default: + print_to(player, "^1SANDBOX - WARNING: ^7Invalid object property. For usage information, type 'sandbox help'"); + return true; + } + + // update last editing time + if(e.message2) strunzone(e.message2); + e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); + + if(autocvar_g_sandbox_info > 1) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " edited property ^3", argv(2), " ^7of an object at origin ^3", vtos(e.origin), "\n")); + return true; + } + + print_to(player, "^1SANDBOX - WARNING: ^7Object could not be edited. Make sure you are facing an object that you have edit rights over"); + return true; + + // ---------------- COMMAND: OBJECT, CLAIM ---------------- + case "object_claim": + // if the player can edit an object but is not its owner, this can be used to claim that object + if(player.crypto_idfp == "") + { + print_to(player, "^1SANDBOX - WARNING: ^7You do not have a player UID, and cannot claim objects"); + return true; + } + e = sandbox_ObjectEdit_Get(player, true); + if(e != NULL) + { + // update the owner's name + // Do this before checking if you're already the owner and skipping if such, so we + // also update the player's nickname if he changed it (but has the same player UID) + if(e.netname != player.netname) + { + if(e.netname) strunzone(e.netname); + e.netname = strzone(player.netname); + print_to(player, "^2SANDBOX - INFO: ^7Object owner name updated"); + } + + if(e.crypto_idfp == player.crypto_idfp) + { + print_to(player, "^2SANDBOX - INFO: ^7Object is already yours, nothing to claim"); + return true; + } + + if(e.crypto_idfp) strunzone(e.crypto_idfp); + e.crypto_idfp = strzone(player.crypto_idfp); + + print_to(player, "^2SANDBOX - INFO: ^7Object claimed successfully"); + } + print_to(player, "^1SANDBOX - WARNING: ^7Object could not be claimed. Make sure you are facing an object that you have edit rights over"); + return true; + + // ---------------- COMMAND: OBJECT, INFO ---------------- + case "object_info": + // prints public information about the object to the player + e = sandbox_ObjectEdit_Get(player, false); + if(e != NULL) + { + switch(argv(2)) + { + case "object": + print_to(player, strcat("^2SANDBOX - INFO: ^7Object is owned by \"^7", e.netname, "^7\", created \"^3", e.message, "^7\", last edited \"^3", e.message2, "^7\"")); + return true; + case "mesh": + s = ""; + FOR_EACH_TAG(e) + s = strcat(s, "^7\"^5", gettaginfo_name, "^7\", "); + print_to(player, strcat("^2SANDBOX - INFO: ^7Object mesh is \"^3", e.model, "^7\" at animation frame ^3", ftos(e.frame), " ^7containing the following tags: ", s)); + return true; + case "attachments": + // this should show the same info as 'mesh' but for attachments + s = ""; + j = 0; + FOREACH_ENTITY_ENT(owner, e, + { + if(it.classname != "object") continue; + + ++j; // start from 1 + gettaginfo(e, it.tag_index); + s = strcat(s, "^1attachment ", ftos(j), "^7 has mesh \"^3", it.model, "^7\" at animation frame ^3", ftos(it.frame)); + s = strcat(s, "^7 and is attached to bone \"^5", gettaginfo_name, "^7\", "); + }); + if(j) // object contains attachments + print_to(player, strcat("^2SANDBOX - INFO: ^7Object contains the following ^1", ftos(j), "^7 attachment(s): ", s)); + else + print_to(player, "^2SANDBOX - INFO: ^7Object contains no attachments"); + return true; + } + } + print_to(player, "^1SANDBOX - WARNING: ^7No information could be found. Make sure you are facing an object"); + return true; + + // ---------------- COMMAND: DEFAULT ---------------- + default: + print_to(player, "Invalid command. For usage information, type 'sandbox help'"); + return true; + } + } +} + +MUTATOR_HOOKFUNCTION(sandbox, SV_StartFrame) +{ + if(!autocvar_g_sandbox_storage_autosave) + return; + if(time < autosave_time) + return; + autosave_time = time + autocvar_g_sandbox_storage_autosave; + + sandbox_Database_Save(); + + return true; +} diff --git a/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qh b/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/spawn_near_teammate/_mod.inc b/qcsrc/common/mutators/mutator/spawn_near_teammate/_mod.inc index b7d3af7f4d..fe3a6ebc5d 100644 --- a/qcsrc/common/mutators/mutator/spawn_near_teammate/_mod.inc +++ b/qcsrc/common/mutators/mutator/spawn_near_teammate/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/spawn_near_teammate/_mod.qh b/qcsrc/common/mutators/mutator/spawn_near_teammate/_mod.qh index 5f53e95c88..b34f8f8f16 100644 --- a/qcsrc/common/mutators/mutator/spawn_near_teammate/_mod.qh +++ b/qcsrc/common/mutators/mutator/spawn_near_teammate/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/spawn_near_teammate/module.inc b/qcsrc/common/mutators/mutator/spawn_near_teammate/module.inc deleted file mode 100644 index f88a768a21..0000000000 --- a/qcsrc/common/mutators/mutator/spawn_near_teammate/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "spawn_near_teammate.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/spawn_near_teammate/spawn_near_teammate.qc b/qcsrc/common/mutators/mutator/spawn_near_teammate/spawn_near_teammate.qc deleted file mode 100644 index b16a6c9acf..0000000000 --- a/qcsrc/common/mutators/mutator/spawn_near_teammate/spawn_near_teammate.qc +++ /dev/null @@ -1,189 +0,0 @@ -#ifdef IMPLEMENTATION - -float autocvar_g_spawn_near_teammate_distance; -int autocvar_g_spawn_near_teammate_ignore_spawnpoint; -float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay; -float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death; -int autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health; -bool autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath; - -REGISTER_MUTATOR(spawn_near_teammate, cvar("g_spawn_near_teammate")); - -.entity msnt_lookat; - -.float msnt_timer; -.vector msnt_deathloc; - -.float cvar_cl_spawn_near_teammate; - -MUTATOR_HOOKFUNCTION(spawn_near_teammate, Spawn_Score) -{ - entity player = M_ARGV(0, entity); - entity spawn_spot = M_ARGV(1, entity); - vector spawn_score = M_ARGV(2, vector); - - if(autocvar_g_spawn_near_teammate_ignore_spawnpoint == 1 || (autocvar_g_spawn_near_teammate_ignore_spawnpoint == 2 && player.cvar_cl_spawn_near_teammate)) - return; - - spawn_spot.msnt_lookat = NULL; - - if(!teamplay) - return; - - RandomSelection_Init(); - FOREACH_CLIENT(IS_PLAYER(it) && it != player && SAME_TEAM(it, player) && !IS_DEAD(it), LAMBDA( - if(vdist(spawn_spot.origin - it.origin, >, autocvar_g_spawn_near_teammate_distance)) - continue; - if(vdist(spawn_spot.origin - it.origin, <, 48)) - continue; - if(!checkpvs(spawn_spot.origin, it)) - continue; - RandomSelection_Add(it, 0, string_null, 1, 1); - )); - - if(RandomSelection_chosen_ent) - { - spawn_spot.msnt_lookat = RandomSelection_chosen_ent; - spawn_score.x += SPAWN_PRIO_NEAR_TEAMMATE_FOUND; - } - else if(player.team == spawn_spot.team) - spawn_score.x += SPAWN_PRIO_NEAR_TEAMMATE_SAMETEAM; // prefer same team, if we can't find a spawn near teammate - - M_ARGV(2, vector) = spawn_score; -} - -MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerSpawn) -{ - if(!teamplay) { return; } - entity player = M_ARGV(0, entity); - entity spawn_spot = M_ARGV(1, entity); - - int num_red = 0, num_blue = 0, num_yellow = 0, num_pink = 0; - FOREACH_CLIENT(IS_PLAYER(it), - { - switch(it.team) - { - case NUM_TEAM_1: ++num_red; break; - case NUM_TEAM_2: ++num_blue; break; - case NUM_TEAM_3: ++num_yellow; break; - case NUM_TEAM_4: ++num_pink; break; - } - }); - - if(num_red == 1 || num_blue == 1 || num_yellow == 1 || num_pink == 1) - return; // at least 1 team has only 1 player, let's not give the bigger team too much of an advantage! - - // Note: when entering this, fixangle is already set. - if(autocvar_g_spawn_near_teammate_ignore_spawnpoint == 1 || (autocvar_g_spawn_near_teammate_ignore_spawnpoint == 2 && player.cvar_cl_spawn_near_teammate)) - { - if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death) - player.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death; - - entity best_mate = NULL; - vector best_spot = '0 0 0'; - float pc = 0, best_dist = 0, dist = 0; - FOREACH_CLIENT(IS_PLAYER(it), LAMBDA( - if((autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health >= 0 && it.health >= autocvar_g_balance_health_regenstable) || autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health == 0) - if(!IS_DEAD(it)) - if(it.msnt_timer < time) - if(SAME_TEAM(player, it)) - if(time > it.spawnshieldtime) // spawn shielding - if(STAT(FROZEN, it) == 0) - if(it != player) - { - tracebox(it.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - '0 0 100', MOVE_NOMONSTERS, it); - if(trace_fraction != 1.0) - if(!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)) - { - pc = pointcontents(trace_endpos + '0 0 1'); - if(pc == CONTENT_EMPTY) - { - if(vdist(it.velocity, >, 5)) - fixedmakevectors(vectoangles(it.velocity)); - else - fixedmakevectors(it.angles); - - for(pc = 0; pc < 4; ++pc) // test 4 diffrent spots close to mate - { - switch(pc) - { - case 0: - tracebox(it.origin , STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin + v_right * 128, MOVE_NOMONSTERS, it); - break; - case 1: - tracebox(it.origin , STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - v_right * 128 , MOVE_NOMONSTERS, it); - break; - case 2: - tracebox(it.origin , STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin + v_right * 128 - v_forward * 64, MOVE_NOMONSTERS, it); - break; - case 3: - tracebox(it.origin , STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - v_right * 128 - v_forward * 64, MOVE_NOMONSTERS, it); - break; - //case 4: - //tracebox(it.origin , STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - v_forward * 128, MOVE_NOMONSTERS, it); - //break; - } - - if(trace_fraction == 1.0) - { - traceline(trace_endpos + '0 0 4', trace_endpos - '0 0 100', MOVE_NOMONSTERS, it); - if(trace_fraction != 1.0) - { - if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath) - { - dist = vlen(trace_endpos - player.msnt_deathloc); - if(dist < best_dist || best_dist == 0) - { - best_dist = dist; - best_spot = trace_endpos; - best_mate = it; - } - } - else - { - setorigin(player, trace_endpos); - player.angles = it.angles; - player.angles_z = 0; // never spawn tilted even if the spot says to - it.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay; - return; - } - } - } - } - } - } - } - )); - - if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath) - if(best_dist) - { - setorigin(player, best_spot); - player.angles = best_mate.angles; - player.angles_z = 0; // never spawn tilted even if the spot says to - best_mate.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay; - } - } - else if(spawn_spot.msnt_lookat) - { - player.angles = vectoangles(spawn_spot.msnt_lookat.origin - player.origin); - player.angles_x = -player.angles.x; - player.angles_z = 0; // never spawn tilted even if the spot says to - /* - sprint(player, "You should be looking at ", spawn_spot.msnt_lookat.netname, "^7.\n"); - sprint(player, "distance: ", vtos(spawn_spot.msnt_lookat.origin - player.origin), "\n"); - sprint(player, "angles: ", vtos(player.angles), "\n"); - */ - } -} - -MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerDies) -{ - entity frag_target = M_ARGV(0, entity); - - frag_target.msnt_deathloc = frag_target.origin; -} - -REPLICATE(cvar_cl_spawn_near_teammate, bool, "cl_spawn_near_teammate"); - -#endif diff --git a/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc b/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc new file mode 100644 index 0000000000..fb14f27db8 --- /dev/null +++ b/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc @@ -0,0 +1,188 @@ +#include "sv_spawn_near_teammate.qh" + +float autocvar_g_spawn_near_teammate_distance; +int autocvar_g_spawn_near_teammate_ignore_spawnpoint; +float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay; +float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death; +int autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health; +bool autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath; + +REGISTER_MUTATOR(spawn_near_teammate, cvar("g_spawn_near_teammate")); + +.entity msnt_lookat; + +.float msnt_timer; +.vector msnt_deathloc; + +.float cvar_cl_spawn_near_teammate; + +MUTATOR_HOOKFUNCTION(spawn_near_teammate, Spawn_Score) +{ + entity player = M_ARGV(0, entity); + entity spawn_spot = M_ARGV(1, entity); + vector spawn_score = M_ARGV(2, vector); + + if(autocvar_g_spawn_near_teammate_ignore_spawnpoint == 1 || (autocvar_g_spawn_near_teammate_ignore_spawnpoint == 2 && player.cvar_cl_spawn_near_teammate)) + return; + + spawn_spot.msnt_lookat = NULL; + + if(!teamplay) + return; + + RandomSelection_Init(); + FOREACH_CLIENT(IS_PLAYER(it) && it != player && SAME_TEAM(it, player) && !IS_DEAD(it), LAMBDA( + if(vdist(spawn_spot.origin - it.origin, >, autocvar_g_spawn_near_teammate_distance)) + continue; + if(vdist(spawn_spot.origin - it.origin, <, 48)) + continue; + if(!checkpvs(spawn_spot.origin, it)) + continue; + RandomSelection_Add(it, 0, string_null, 1, 1); + )); + + if(RandomSelection_chosen_ent) + { + spawn_spot.msnt_lookat = RandomSelection_chosen_ent; + spawn_score.x += SPAWN_PRIO_NEAR_TEAMMATE_FOUND; + } + else if(player.team == spawn_spot.team) + spawn_score.x += SPAWN_PRIO_NEAR_TEAMMATE_SAMETEAM; // prefer same team, if we can't find a spawn near teammate + + M_ARGV(2, vector) = spawn_score; +} + +MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerSpawn) +{ + if(!teamplay) { return; } + entity player = M_ARGV(0, entity); + entity spawn_spot = M_ARGV(1, entity); + + int num_red = 0, num_blue = 0, num_yellow = 0, num_pink = 0; + FOREACH_CLIENT(IS_PLAYER(it), + { + switch(it.team) + { + case NUM_TEAM_1: ++num_red; break; + case NUM_TEAM_2: ++num_blue; break; + case NUM_TEAM_3: ++num_yellow; break; + case NUM_TEAM_4: ++num_pink; break; + } + }); + + if(num_red == 1 || num_blue == 1 || num_yellow == 1 || num_pink == 1) + return; // at least 1 team has only 1 player, let's not give the bigger team too much of an advantage! + + // Note: when entering this, fixangle is already set. + if(autocvar_g_spawn_near_teammate_ignore_spawnpoint == 1 || (autocvar_g_spawn_near_teammate_ignore_spawnpoint == 2 && player.cvar_cl_spawn_near_teammate)) + { + if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death) + player.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death; + + entity best_mate = NULL; + vector best_spot = '0 0 0'; + float pc = 0, best_dist = 0, dist = 0; + FOREACH_CLIENT(IS_PLAYER(it), LAMBDA( + if((autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health >= 0 && it.health >= autocvar_g_balance_health_regenstable) || autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health == 0) + if(!IS_DEAD(it)) + if(it.msnt_timer < time) + if(SAME_TEAM(player, it)) + if(time > it.spawnshieldtime) // spawn shielding + if(!forbidWeaponUse(it)) + if(STAT(FROZEN, it) == 0) + if(it != player) + { + tracebox(it.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - '0 0 100', MOVE_NOMONSTERS, it); + if(trace_fraction != 1.0) + if(!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)) + { + pc = pointcontents(trace_endpos + '0 0 1'); + if(pc == CONTENT_EMPTY) + { + if(vdist(it.velocity, >, 5)) + fixedmakevectors(vectoangles(it.velocity)); + else + fixedmakevectors(it.angles); + + for(pc = 0; pc < 4; ++pc) // test 4 diffrent spots close to mate + { + switch(pc) + { + case 0: + tracebox(it.origin , STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin + v_right * 128, MOVE_NOMONSTERS, it); + break; + case 1: + tracebox(it.origin , STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - v_right * 128 , MOVE_NOMONSTERS, it); + break; + case 2: + tracebox(it.origin , STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin + v_right * 128 - v_forward * 64, MOVE_NOMONSTERS, it); + break; + case 3: + tracebox(it.origin , STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - v_right * 128 - v_forward * 64, MOVE_NOMONSTERS, it); + break; + //case 4: + //tracebox(it.origin , STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - v_forward * 128, MOVE_NOMONSTERS, it); + //break; + } + + if(trace_fraction == 1.0) + { + traceline(trace_endpos + '0 0 4', trace_endpos - '0 0 100', MOVE_NOMONSTERS, it); + if(trace_fraction != 1.0) + { + if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath) + { + dist = vlen(trace_endpos - player.msnt_deathloc); + if(dist < best_dist || best_dist == 0) + { + best_dist = dist; + best_spot = trace_endpos; + best_mate = it; + } + } + else + { + setorigin(player, trace_endpos); + player.angles = it.angles; + player.angles_z = 0; // never spawn tilted even if the spot says to + it.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay; + return; + } + } + } + } + } + } + } + )); + + if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath) + if(best_dist) + { + setorigin(player, best_spot); + player.angles = best_mate.angles; + player.angles_z = 0; // never spawn tilted even if the spot says to + best_mate.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay; + } + } + else if(spawn_spot.msnt_lookat) + { + player.angles = vectoangles(spawn_spot.msnt_lookat.origin - player.origin); + player.angles_x = -player.angles.x; + player.angles_z = 0; // never spawn tilted even if the spot says to + /* + sprint(player, "You should be looking at ", spawn_spot.msnt_lookat.netname, "^7.\n"); + sprint(player, "distance: ", vtos(spawn_spot.msnt_lookat.origin - player.origin), "\n"); + sprint(player, "angles: ", vtos(player.angles), "\n"); + */ + } +} + +MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerDies) +{ + entity frag_target = M_ARGV(0, entity); + + frag_target.msnt_deathloc = frag_target.origin; +} + +REPLICATE(cvar_cl_spawn_near_teammate, bool, "cl_spawn_near_teammate"); diff --git a/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qh b/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/superspec/_mod.inc b/qcsrc/common/mutators/mutator/superspec/_mod.inc index d5005242f2..262c7fcdbc 100644 --- a/qcsrc/common/mutators/mutator/superspec/_mod.inc +++ b/qcsrc/common/mutators/mutator/superspec/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/superspec/_mod.qh b/qcsrc/common/mutators/mutator/superspec/_mod.qh index b544ffc611..110087b674 100644 --- a/qcsrc/common/mutators/mutator/superspec/_mod.qh +++ b/qcsrc/common/mutators/mutator/superspec/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/superspec/module.inc b/qcsrc/common/mutators/mutator/superspec/module.inc deleted file mode 100644 index 8e0a998c2d..0000000000 --- a/qcsrc/common/mutators/mutator/superspec/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "superspec.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/superspec/superspec.qc b/qcsrc/common/mutators/mutator/superspec/superspec.qc deleted file mode 100644 index 947ff01bb7..0000000000 --- a/qcsrc/common/mutators/mutator/superspec/superspec.qc +++ /dev/null @@ -1,459 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(superspec, cvar("g_superspectate")); - -#define _SSMAGIX "SUPERSPEC_OPTIONSFILE_V1" -#define _ISLOCAL(ent) ((edict_num(1) == (ent)) ? true : false) - -const float ASF_STRENGTH = BIT(0); -const float ASF_SHIELD = BIT(1); -const float ASF_MEGA_AR = BIT(2); -const float ASF_MEGA_HP = BIT(3); -const float ASF_FLAG_GRAB = BIT(4); -const float ASF_OBSERVER_ONLY = BIT(5); -const float ASF_SHOWWHAT = BIT(6); -const float ASF_SSIM = BIT(7); -const float ASF_FOLLOWKILLER = BIT(8); -const float ASF_ALL = 0xFFFFFF; -.float autospec_flags; - -const float SSF_SILENT = 1; -const float SSF_VERBOSE = 2; -const float SSF_ITEMMSG = 4; -.float superspec_flags; - -.string superspec_itemfilter; //"classname1 classname2 ..." - -bool superspec_Spectate(entity this, entity targ) -{ - if(Spectate(this, targ) == 1) - TRANSMUTE(Spectator, this); - - return true; -} - -void superspec_save_client_conf(entity this) -{ - string fn = "superspec-local.options"; - float fh; - - if (!_ISLOCAL(this)) - { - if(this.crypto_idfp == "") - return; - - fn = sprintf("superspec-%s.options", uri_escape(this.crypto_idfp)); - } - - fh = fopen(fn, FILE_WRITE); - if(fh < 0) - { - LOG_TRACE("^1ERROR: ^7 superspec can not open ", fn, " for writing."); - } - else - { - fputs(fh, _SSMAGIX); - fputs(fh, "\n"); - fputs(fh, ftos(this.autospec_flags)); - fputs(fh, "\n"); - fputs(fh, ftos(this.superspec_flags)); - fputs(fh, "\n"); - fputs(fh, this.superspec_itemfilter); - fputs(fh, "\n"); - fclose(fh); - } -} - -void superspec_msg(string _center_title, string _con_title, entity _to, string _msg, float _spamlevel) -{ - sprint(_to, strcat(_con_title, _msg)); - - if(_to.superspec_flags & SSF_SILENT) - return; - - if(_spamlevel > 1) - if (!(_to.superspec_flags & SSF_VERBOSE)) - return; - - centerprint(_to, strcat(_center_title, _msg)); -} - -float superspec_filteritem(entity _for, entity _item) -{ - float i; - - if(_for.superspec_itemfilter == "") - return true; - - if(_for.superspec_itemfilter == "") - return true; - - float l = tokenize_console(_for.superspec_itemfilter); - for(i = 0; i < l; ++i) - { - if(argv(i) == _item.classname) - return true; - } - - return false; -} - -MUTATOR_HOOKFUNCTION(superspec, ItemTouch) -{ - entity item = M_ARGV(0, entity); - entity toucher = M_ARGV(1, entity); - - FOREACH_CLIENT(true, LAMBDA( - if(!IS_SPEC(it) && !IS_OBSERVER(it)) - continue; - if(it.superspec_flags & SSF_ITEMMSG) - if(superspec_filteritem(it, item)) - { - if(it.superspec_flags & SSF_VERBOSE) - superspec_msg("", "", it, sprintf("Player %s^7 just picked up ^3%s\n", toucher.netname, item.netname), 1); - else - superspec_msg("", "", it, sprintf("Player %s^7 just picked up ^3%s\n^8(%s^8)\n", toucher.netname, item.netname, item.classname), 1); - if((it.autospec_flags & ASF_SSIM) && it.enemy != toucher) - { - superspec_Spectate(it, toucher); - return MUT_ITEMTOUCH_CONTINUE; - } - } - - if((it.autospec_flags & ASF_SHIELD && item.invincible_finished) || - (it.autospec_flags & ASF_STRENGTH && item.strength_finished) || - (it.autospec_flags & ASF_MEGA_AR && item.itemdef == ITEM_ArmorMega) || - (it.autospec_flags & ASF_MEGA_HP && item.itemdef == ITEM_HealthMega) || - (it.autospec_flags & ASF_FLAG_GRAB && item.classname == "item_flag_team")) - { - - if((it.enemy != toucher) || IS_OBSERVER(it)) - { - if(it.autospec_flags & ASF_OBSERVER_ONLY && !IS_OBSERVER(it)) - { - if(it.superspec_flags & SSF_VERBOSE) - superspec_msg("", "", it, sprintf("^8Ignored that ^7%s^8 grabbed %s^8 since the observer_only option is ON\n", toucher.netname, item.netname), 2); - } - else - { - if(it.autospec_flags & ASF_SHOWWHAT) - superspec_msg("", "", it, sprintf("^7Following %s^7 due to picking up %s\n", toucher.netname, item.netname), 2); - - superspec_Spectate(it, toucher); - } - } - } - )); - - return MUT_ITEMTOUCH_CONTINUE; -} - -MUTATOR_HOOKFUNCTION(superspec, SV_ParseClientCommand) -{ -#define OPTIONINFO(flag,var,test,text,long,short) \ - var = strcat(var, ((flag & test) ? "^2[ON] ^7" : "^1[OFF] ^7")); \ - var = strcat(var, text," ^7(^3 ", long, "^7 | ^3", short, " ^7)\n") - - if(MUTATOR_RETURNVALUE) // command was already handled? - return; - - entity player = M_ARGV(0, entity); - string cmd_name = M_ARGV(1, string); - int cmd_argc = M_ARGV(2, int); - - if(IS_PLAYER(player)) - return; - - if(cmd_name == "superspec_itemfilter") - { - if(argv(1) == "help") - { - string _aspeco; - _aspeco = "^7 superspec_itemfilter ^3\"item_classname1 item_classname2\"^7 only show thise items when ^2superspec ^3item_message^7 is on\n"; - _aspeco = strcat(_aspeco, "^3 clear^7 Remove the filter (show all pickups)\n"); - _aspeco = strcat(_aspeco, "^3 show ^7 Display current filter\n"); - superspec_msg("^3superspec_itemfilter help:\n\n\n", "\n^3superspec_itemfilter help:\n", player, _aspeco, 1); - } - else if(argv(1) == "clear") - { - if(player.superspec_itemfilter != "") - strunzone(player.superspec_itemfilter); - - player.superspec_itemfilter = ""; - } - else if(argv(1) == "show" || argv(1) == "") - { - if(player.superspec_itemfilter == "") - { - superspec_msg("^3superspec_itemfilter^7 is ^1not^7 set", "\n^3superspec_itemfilter^7 is ^1not^7 set\n", player, "", 1); - return true; - } - float i; - float l = tokenize_console(player.superspec_itemfilter); - string _msg = ""; - for(i = 0; i < l; ++i) - _msg = strcat(_msg, "^3#", ftos(i), " ^7", argv(i), "\n"); - //_msg = sprintf("^3#%d^7 %s\n%s", i, _msg, argv(i)); - - _msg = strcat(_msg,"\n"); - - superspec_msg("^3superspec_itemfilter is:\n\n\n", "\n^3superspec_itemfilter is:\n", player, _msg, 1); - } - else - { - if(player.superspec_itemfilter != "") - strunzone(player.superspec_itemfilter); - - player.superspec_itemfilter = strzone(argv(1)); - } - - return true; - } - - if(cmd_name == "superspec") - { - string _aspeco; - - if(cmd_argc > 1) - { - float i, _bits = 0, _start = 1; - if(argv(1) == "help") - { - _aspeco = "use cmd superspec [option] [on|off] to set options\n\n"; - _aspeco = strcat(_aspeco, "^3 silent ^7(short^5 si^7) supresses ALL messages from superspectate.\n"); - _aspeco = strcat(_aspeco, "^3 verbose ^7(short^5 ve^7) makes superspectate print some additional information.\n"); - _aspeco = strcat(_aspeco, "^3 item_message ^7(short^5 im^7) makes superspectate print items that were picked up.\n"); - _aspeco = strcat(_aspeco, "^7 Use cmd superspec_itemfilter \"item_class1 item_class2\" to set up a filter of what to show with ^3item_message.\n"); - superspec_msg("^2Available Super Spectate ^3options:\n\n\n", "\n^2Available Super Spectate ^3options:\n", player, _aspeco, 1); - return true; - } - - if(argv(1) == "clear") - { - player.superspec_flags = 0; - _start = 2; - } - - for(i = _start; i < cmd_argc; ++i) - { - if(argv(i) == "on" || argv(i) == "1") - { - player.superspec_flags |= _bits; - _bits = 0; - } - else if(argv(i) == "off" || argv(i) == "0") - { - if(_start == 1) - player.superspec_flags &= ~_bits; - - _bits = 0; - } - else - { - if((argv(i) == "silent") || (argv(i) == "si")) _bits |= SSF_SILENT ; - if((argv(i) == "verbose") || (argv(i) == "ve")) _bits |= SSF_VERBOSE; - if((argv(i) == "item_message") || (argv(i) == "im")) _bits |= SSF_ITEMMSG; - } - } - } - - _aspeco = ""; - OPTIONINFO(player.superspec_flags, _aspeco, SSF_SILENT, "Silent", "silent", "si"); - OPTIONINFO(player.superspec_flags, _aspeco, SSF_VERBOSE, "Verbose", "verbose", "ve"); - OPTIONINFO(player.superspec_flags, _aspeco, SSF_ITEMMSG, "Item pickup messages", "item_message", "im"); - - superspec_msg("^3Current Super Spectate options are:\n\n\n\n\n", "\n^3Current Super Spectate options are:\n", player, _aspeco, 1); - - return true; - } - -///////////////////// - - if(cmd_name == "autospec") - { - string _aspeco; - if(cmd_argc > 1) - { - if(argv(1) == "help") - { - _aspeco = "use cmd autospec [option] [on|off] to set options\n\n"; - _aspeco = strcat(_aspeco, "^3 strength ^7(short^5 st^7) for automatic spectate on strength powerup\n"); - _aspeco = strcat(_aspeco, "^3 shield ^7(short^5 sh^7) for automatic spectate on shield powerup\n"); - _aspeco = strcat(_aspeco, "^3 mega_health ^7(short^5 mh^7) for automatic spectate on mega health\n"); - _aspeco = strcat(_aspeco, "^3 mega_armor ^7(short^5 ma^7) for automatic spectate on mega armor\n"); - _aspeco = strcat(_aspeco, "^3 flag_grab ^7(short^5 fg^7) for automatic spectate on CTF flag grab\n"); - _aspeco = strcat(_aspeco, "^3 observer_only ^7(short^5 oo^7) for automatic spectate only if in observer mode\n"); - _aspeco = strcat(_aspeco, "^3 show_what ^7(short^5 sw^7) to display what event triggered autospectate\n"); - _aspeco = strcat(_aspeco, "^3 item_msg ^7(short^5 im^7) to autospec when item_message in superspectate is triggered\n"); - _aspeco = strcat(_aspeco, "^3 followkiller ^7(short ^5fk^7) to autospec the killer/off\n"); - _aspeco = strcat(_aspeco, "^3 all ^7(short ^5aa^7) to turn everything on/off\n"); - superspec_msg("^2Available Auto Spectate ^3options:\n\n\n", "\n^2Available Auto Spectate ^3options:\n", player, _aspeco, 1); - return true; - } - - float i, _bits = 0, _start = 1; - if(argv(1) == "clear") - { - player.autospec_flags = 0; - _start = 2; - } - - for(i = _start; i < cmd_argc; ++i) - { - if(argv(i) == "on" || argv(i) == "1") - { - player.autospec_flags |= _bits; - _bits = 0; - } - else if(argv(i) == "off" || argv(i) == "0") - { - if(_start == 1) - player.autospec_flags &= ~_bits; - - _bits = 0; - } - else - { - if((argv(i) == "strength") || (argv(i) == "st")) _bits |= ASF_STRENGTH; - if((argv(i) == "shield") || (argv(i) == "sh")) _bits |= ASF_SHIELD; - if((argv(i) == "mega_health") || (argv(i) == "mh")) _bits |= ASF_MEGA_HP; - if((argv(i) == "mega_armor") || (argv(i) == "ma")) _bits |= ASF_MEGA_AR; - if((argv(i) == "flag_grab") || (argv(i) == "fg")) _bits |= ASF_FLAG_GRAB; - if((argv(i) == "observer_only") || (argv(i) == "oo")) _bits |= ASF_OBSERVER_ONLY; - if((argv(i) == "show_what") || (argv(i) == "sw")) _bits |= ASF_SHOWWHAT; - if((argv(i) == "item_msg") || (argv(i) == "im")) _bits |= ASF_SSIM; - if((argv(i) == "followkiller") || (argv(i) == "fk")) _bits |= ASF_FOLLOWKILLER; - if((argv(i) == "all") || (argv(i) == "aa")) _bits |= ASF_ALL; - } - } - } - - _aspeco = ""; - OPTIONINFO(player.autospec_flags, _aspeco, ASF_STRENGTH, "Strength", "strength", "st"); - OPTIONINFO(player.autospec_flags, _aspeco, ASF_SHIELD, "Shield", "shield", "sh"); - OPTIONINFO(player.autospec_flags, _aspeco, ASF_MEGA_HP, "Mega Health", "mega_health", "mh"); - OPTIONINFO(player.autospec_flags, _aspeco, ASF_MEGA_AR, "Mega Armor", "mega_armor", "ma"); - OPTIONINFO(player.autospec_flags, _aspeco, ASF_FLAG_GRAB, "Flag grab", "flag_grab","fg"); - OPTIONINFO(player.autospec_flags, _aspeco, ASF_OBSERVER_ONLY, "Only switch if observer", "observer_only", "oo"); - OPTIONINFO(player.autospec_flags, _aspeco, ASF_SHOWWHAT, "Show what item triggered spectate", "show_what", "sw"); - OPTIONINFO(player.autospec_flags, _aspeco, ASF_SSIM, "Switch on superspec item message", "item_msg", "im"); - OPTIONINFO(player.autospec_flags, _aspeco, ASF_FOLLOWKILLER, "Followkiller", "followkiller", "fk"); - - superspec_msg("^3Current auto spectate options are:\n\n\n\n\n", "\n^3Current auto spectate options are:\n", player, _aspeco, 1); - return true; - } - - if(cmd_name == "followpowerup") - { - FOREACH_CLIENT(IS_PLAYER(it) && (it.strength_finished > time || it.invincible_finished > time), LAMBDA(return superspec_Spectate(player, it))); - - superspec_msg("", "", player, "No active powerup\n", 1); - return true; - } - - if(cmd_name == "followstrength") - { - FOREACH_CLIENT(IS_PLAYER(it) && it.strength_finished > time, LAMBDA(return superspec_Spectate(player, it))); - - superspec_msg("", "", player, "No active Strength\n", 1); - return true; - } - - if(cmd_name == "followshield") - { - FOREACH_CLIENT(IS_PLAYER(it) && it.invincible_finished > time, LAMBDA(return superspec_Spectate(player, it))); - - superspec_msg("", "", player, "No active Shield\n", 1); - return true; - } -#undef OPTIONINFO -} - -MUTATOR_HOOKFUNCTION(superspec, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":SS"); -} - -MUTATOR_HOOKFUNCTION(superspec, BuildMutatorsPrettyString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Super Spectators"); -} - -void superspec_hello(entity this) -{ - if(this.enemy.crypto_idfp == "") - Send_Notification(NOTIF_ONE_ONLY, this.enemy, MSG_INFO, INFO_SUPERSPEC_MISSING_UID); - - delete(this); -} - -MUTATOR_HOOKFUNCTION(superspec, ClientConnect) -{ - entity player = M_ARGV(0, entity); - - if(!IS_REAL_CLIENT(player)) - return; - - string fn = "superspec-local.options"; - float fh; - - player.superspec_flags = SSF_VERBOSE; - player.superspec_itemfilter = ""; - - entity _hello = spawn(); - _hello.enemy = player; - setthink(_hello, superspec_hello); - _hello.nextthink = time + 5; - - if (!_ISLOCAL(player)) - { - if(player.crypto_idfp == "") - return; - - fn = sprintf("superspec-%s.options", uri_escape(player.crypto_idfp)); - } - - fh = fopen(fn, FILE_READ); - if(fh < 0) - { - LOG_TRACE("^1ERROR: ^7 superspec can not open ", fn, " for reading."); - } - else - { - string _magic = fgets(fh); - if(_magic != _SSMAGIX) - { - LOG_TRACE("^1ERROR^7 While reading superspec options file: unknown magic"); - } - else - { - player.autospec_flags = stof(fgets(fh)); - player.superspec_flags = stof(fgets(fh)); - player.superspec_itemfilter = strzone(fgets(fh)); - } - fclose(fh); - } -} - -MUTATOR_HOOKFUNCTION(superspec, PlayerDies) -{ - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - - FOREACH_CLIENT(IS_SPEC(it), LAMBDA( - if(it.autospec_flags & ASF_FOLLOWKILLER && IS_PLAYER(frag_attacker) && it.enemy == frag_target) - { - if(it.autospec_flags & ASF_SHOWWHAT) - superspec_msg("", "", it, sprintf("^7Following %s^7 due to followkiller\n", frag_attacker.netname), 2); - - superspec_Spectate(it, frag_attacker); - } - )); -} - -MUTATOR_HOOKFUNCTION(superspec, ClientDisconnect) -{ - entity player = M_ARGV(0, entity); - - superspec_save_client_conf(player); -} -#endif diff --git a/qcsrc/common/mutators/mutator/superspec/sv_superspec.qc b/qcsrc/common/mutators/mutator/superspec/sv_superspec.qc new file mode 100644 index 0000000000..c423042a3c --- /dev/null +++ b/qcsrc/common/mutators/mutator/superspec/sv_superspec.qc @@ -0,0 +1,459 @@ +#include "sv_superspec.qh" + +REGISTER_MUTATOR(superspec, cvar("g_superspectate")); + +#define _SSMAGIX "SUPERSPEC_OPTIONSFILE_V1" +#define _ISLOCAL(ent) ((edict_num(1) == (ent)) ? true : false) + +const float ASF_STRENGTH = BIT(0); +const float ASF_SHIELD = BIT(1); +const float ASF_MEGA_AR = BIT(2); +const float ASF_MEGA_HP = BIT(3); +const float ASF_FLAG_GRAB = BIT(4); +const float ASF_OBSERVER_ONLY = BIT(5); +const float ASF_SHOWWHAT = BIT(6); +const float ASF_SSIM = BIT(7); +const float ASF_FOLLOWKILLER = BIT(8); +const float ASF_ALL = 0xFFFFFF; +.float autospec_flags; + +const float SSF_SILENT = 1; +const float SSF_VERBOSE = 2; +const float SSF_ITEMMSG = 4; +.float superspec_flags; + +.string superspec_itemfilter; //"classname1 classname2 ..." + +bool superspec_Spectate(entity this, entity targ) +{ + if(Spectate(this, targ) == 1) + TRANSMUTE(Spectator, this); + + return true; +} + +void superspec_save_client_conf(entity this) +{ + string fn = "superspec-local.options"; + float fh; + + if (!_ISLOCAL(this)) + { + if(this.crypto_idfp == "") + return; + + fn = sprintf("superspec-%s.options", uri_escape(this.crypto_idfp)); + } + + fh = fopen(fn, FILE_WRITE); + if(fh < 0) + { + LOG_TRACE("^1ERROR: ^7 superspec can not open ", fn, " for writing."); + } + else + { + fputs(fh, _SSMAGIX); + fputs(fh, "\n"); + fputs(fh, ftos(this.autospec_flags)); + fputs(fh, "\n"); + fputs(fh, ftos(this.superspec_flags)); + fputs(fh, "\n"); + fputs(fh, this.superspec_itemfilter); + fputs(fh, "\n"); + fclose(fh); + } +} + +void superspec_msg(string _center_title, string _con_title, entity _to, string _msg, float _spamlevel) +{ + sprint(_to, strcat(_con_title, _msg)); + + if(_to.superspec_flags & SSF_SILENT) + return; + + if(_spamlevel > 1) + if (!(_to.superspec_flags & SSF_VERBOSE)) + return; + + centerprint(_to, strcat(_center_title, _msg)); +} + +float superspec_filteritem(entity _for, entity _item) +{ + float i; + + if(_for.superspec_itemfilter == "") + return true; + + if(_for.superspec_itemfilter == "") + return true; + + float l = tokenize_console(_for.superspec_itemfilter); + for(i = 0; i < l; ++i) + { + if(argv(i) == _item.classname) + return true; + } + + return false; +} + +MUTATOR_HOOKFUNCTION(superspec, ItemTouch) +{ + entity item = M_ARGV(0, entity); + entity toucher = M_ARGV(1, entity); + + FOREACH_CLIENT(true, LAMBDA( + if(!IS_SPEC(it) && !IS_OBSERVER(it)) + continue; + if(it.superspec_flags & SSF_ITEMMSG) + if(superspec_filteritem(it, item)) + { + if(it.superspec_flags & SSF_VERBOSE) + superspec_msg("", "", it, sprintf("Player %s^7 just picked up ^3%s\n", toucher.netname, item.netname), 1); + else + superspec_msg("", "", it, sprintf("Player %s^7 just picked up ^3%s\n^8(%s^8)\n", toucher.netname, item.netname, item.classname), 1); + if((it.autospec_flags & ASF_SSIM) && it.enemy != toucher) + { + superspec_Spectate(it, toucher); + return MUT_ITEMTOUCH_CONTINUE; + } + } + + if((it.autospec_flags & ASF_SHIELD && item.invincible_finished) || + (it.autospec_flags & ASF_STRENGTH && item.strength_finished) || + (it.autospec_flags & ASF_MEGA_AR && item.itemdef == ITEM_ArmorMega) || + (it.autospec_flags & ASF_MEGA_HP && item.itemdef == ITEM_HealthMega) || + (it.autospec_flags & ASF_FLAG_GRAB && item.classname == "item_flag_team")) + { + + if((it.enemy != toucher) || IS_OBSERVER(it)) + { + if(it.autospec_flags & ASF_OBSERVER_ONLY && !IS_OBSERVER(it)) + { + if(it.superspec_flags & SSF_VERBOSE) + superspec_msg("", "", it, sprintf("^8Ignored that ^7%s^8 grabbed %s^8 since the observer_only option is ON\n", toucher.netname, item.netname), 2); + } + else + { + if(it.autospec_flags & ASF_SHOWWHAT) + superspec_msg("", "", it, sprintf("^7Following %s^7 due to picking up %s\n", toucher.netname, item.netname), 2); + + superspec_Spectate(it, toucher); + } + } + } + )); + + return MUT_ITEMTOUCH_CONTINUE; +} + +MUTATOR_HOOKFUNCTION(superspec, SV_ParseClientCommand) +{ +#define OPTIONINFO(flag,var,test,text,long,short) \ + var = strcat(var, ((flag & test) ? "^2[ON] ^7" : "^1[OFF] ^7")); \ + var = strcat(var, text," ^7(^3 ", long, "^7 | ^3", short, " ^7)\n") + + if(MUTATOR_RETURNVALUE) // command was already handled? + return; + + entity player = M_ARGV(0, entity); + string cmd_name = M_ARGV(1, string); + int cmd_argc = M_ARGV(2, int); + + if(IS_PLAYER(player)) + return; + + if(cmd_name == "superspec_itemfilter") + { + if(argv(1) == "help") + { + string _aspeco; + _aspeco = "^7 superspec_itemfilter ^3\"item_classname1 item_classname2\"^7 only show thise items when ^2superspec ^3item_message^7 is on\n"; + _aspeco = strcat(_aspeco, "^3 clear^7 Remove the filter (show all pickups)\n"); + _aspeco = strcat(_aspeco, "^3 show ^7 Display current filter\n"); + superspec_msg("^3superspec_itemfilter help:\n\n\n", "\n^3superspec_itemfilter help:\n", player, _aspeco, 1); + } + else if(argv(1) == "clear") + { + if(player.superspec_itemfilter != "") + strunzone(player.superspec_itemfilter); + + player.superspec_itemfilter = ""; + } + else if(argv(1) == "show" || argv(1) == "") + { + if(player.superspec_itemfilter == "") + { + superspec_msg("^3superspec_itemfilter^7 is ^1not^7 set", "\n^3superspec_itemfilter^7 is ^1not^7 set\n", player, "", 1); + return true; + } + float i; + float l = tokenize_console(player.superspec_itemfilter); + string _msg = ""; + for(i = 0; i < l; ++i) + _msg = strcat(_msg, "^3#", ftos(i), " ^7", argv(i), "\n"); + //_msg = sprintf("^3#%d^7 %s\n%s", i, _msg, argv(i)); + + _msg = strcat(_msg,"\n"); + + superspec_msg("^3superspec_itemfilter is:\n\n\n", "\n^3superspec_itemfilter is:\n", player, _msg, 1); + } + else + { + if(player.superspec_itemfilter != "") + strunzone(player.superspec_itemfilter); + + player.superspec_itemfilter = strzone(argv(1)); + } + + return true; + } + + if(cmd_name == "superspec") + { + string _aspeco; + + if(cmd_argc > 1) + { + float i, _bits = 0, _start = 1; + if(argv(1) == "help") + { + _aspeco = "use cmd superspec [option] [on|off] to set options\n\n"; + _aspeco = strcat(_aspeco, "^3 silent ^7(short^5 si^7) supresses ALL messages from superspectate.\n"); + _aspeco = strcat(_aspeco, "^3 verbose ^7(short^5 ve^7) makes superspectate print some additional information.\n"); + _aspeco = strcat(_aspeco, "^3 item_message ^7(short^5 im^7) makes superspectate print items that were picked up.\n"); + _aspeco = strcat(_aspeco, "^7 Use cmd superspec_itemfilter \"item_class1 item_class2\" to set up a filter of what to show with ^3item_message.\n"); + superspec_msg("^2Available Super Spectate ^3options:\n\n\n", "\n^2Available Super Spectate ^3options:\n", player, _aspeco, 1); + return true; + } + + if(argv(1) == "clear") + { + player.superspec_flags = 0; + _start = 2; + } + + for(i = _start; i < cmd_argc; ++i) + { + if(argv(i) == "on" || argv(i) == "1") + { + player.superspec_flags |= _bits; + _bits = 0; + } + else if(argv(i) == "off" || argv(i) == "0") + { + if(_start == 1) + player.superspec_flags &= ~_bits; + + _bits = 0; + } + else + { + if((argv(i) == "silent") || (argv(i) == "si")) _bits |= SSF_SILENT ; + if((argv(i) == "verbose") || (argv(i) == "ve")) _bits |= SSF_VERBOSE; + if((argv(i) == "item_message") || (argv(i) == "im")) _bits |= SSF_ITEMMSG; + } + } + } + + _aspeco = ""; + OPTIONINFO(player.superspec_flags, _aspeco, SSF_SILENT, "Silent", "silent", "si"); + OPTIONINFO(player.superspec_flags, _aspeco, SSF_VERBOSE, "Verbose", "verbose", "ve"); + OPTIONINFO(player.superspec_flags, _aspeco, SSF_ITEMMSG, "Item pickup messages", "item_message", "im"); + + superspec_msg("^3Current Super Spectate options are:\n\n\n\n\n", "\n^3Current Super Spectate options are:\n", player, _aspeco, 1); + + return true; + } + +///////////////////// + + if(cmd_name == "autospec") + { + string _aspeco; + if(cmd_argc > 1) + { + if(argv(1) == "help") + { + _aspeco = "use cmd autospec [option] [on|off] to set options\n\n"; + _aspeco = strcat(_aspeco, "^3 strength ^7(short^5 st^7) for automatic spectate on strength powerup\n"); + _aspeco = strcat(_aspeco, "^3 shield ^7(short^5 sh^7) for automatic spectate on shield powerup\n"); + _aspeco = strcat(_aspeco, "^3 mega_health ^7(short^5 mh^7) for automatic spectate on mega health\n"); + _aspeco = strcat(_aspeco, "^3 mega_armor ^7(short^5 ma^7) for automatic spectate on mega armor\n"); + _aspeco = strcat(_aspeco, "^3 flag_grab ^7(short^5 fg^7) for automatic spectate on CTF flag grab\n"); + _aspeco = strcat(_aspeco, "^3 observer_only ^7(short^5 oo^7) for automatic spectate only if in observer mode\n"); + _aspeco = strcat(_aspeco, "^3 show_what ^7(short^5 sw^7) to display what event triggered autospectate\n"); + _aspeco = strcat(_aspeco, "^3 item_msg ^7(short^5 im^7) to autospec when item_message in superspectate is triggered\n"); + _aspeco = strcat(_aspeco, "^3 followkiller ^7(short ^5fk^7) to autospec the killer/off\n"); + _aspeco = strcat(_aspeco, "^3 all ^7(short ^5aa^7) to turn everything on/off\n"); + superspec_msg("^2Available Auto Spectate ^3options:\n\n\n", "\n^2Available Auto Spectate ^3options:\n", player, _aspeco, 1); + return true; + } + + float i, _bits = 0, _start = 1; + if(argv(1) == "clear") + { + player.autospec_flags = 0; + _start = 2; + } + + for(i = _start; i < cmd_argc; ++i) + { + if(argv(i) == "on" || argv(i) == "1") + { + player.autospec_flags |= _bits; + _bits = 0; + } + else if(argv(i) == "off" || argv(i) == "0") + { + if(_start == 1) + player.autospec_flags &= ~_bits; + + _bits = 0; + } + else + { + if((argv(i) == "strength") || (argv(i) == "st")) _bits |= ASF_STRENGTH; + if((argv(i) == "shield") || (argv(i) == "sh")) _bits |= ASF_SHIELD; + if((argv(i) == "mega_health") || (argv(i) == "mh")) _bits |= ASF_MEGA_HP; + if((argv(i) == "mega_armor") || (argv(i) == "ma")) _bits |= ASF_MEGA_AR; + if((argv(i) == "flag_grab") || (argv(i) == "fg")) _bits |= ASF_FLAG_GRAB; + if((argv(i) == "observer_only") || (argv(i) == "oo")) _bits |= ASF_OBSERVER_ONLY; + if((argv(i) == "show_what") || (argv(i) == "sw")) _bits |= ASF_SHOWWHAT; + if((argv(i) == "item_msg") || (argv(i) == "im")) _bits |= ASF_SSIM; + if((argv(i) == "followkiller") || (argv(i) == "fk")) _bits |= ASF_FOLLOWKILLER; + if((argv(i) == "all") || (argv(i) == "aa")) _bits |= ASF_ALL; + } + } + } + + _aspeco = ""; + OPTIONINFO(player.autospec_flags, _aspeco, ASF_STRENGTH, "Strength", "strength", "st"); + OPTIONINFO(player.autospec_flags, _aspeco, ASF_SHIELD, "Shield", "shield", "sh"); + OPTIONINFO(player.autospec_flags, _aspeco, ASF_MEGA_HP, "Mega Health", "mega_health", "mh"); + OPTIONINFO(player.autospec_flags, _aspeco, ASF_MEGA_AR, "Mega Armor", "mega_armor", "ma"); + OPTIONINFO(player.autospec_flags, _aspeco, ASF_FLAG_GRAB, "Flag grab", "flag_grab","fg"); + OPTIONINFO(player.autospec_flags, _aspeco, ASF_OBSERVER_ONLY, "Only switch if observer", "observer_only", "oo"); + OPTIONINFO(player.autospec_flags, _aspeco, ASF_SHOWWHAT, "Show what item triggered spectate", "show_what", "sw"); + OPTIONINFO(player.autospec_flags, _aspeco, ASF_SSIM, "Switch on superspec item message", "item_msg", "im"); + OPTIONINFO(player.autospec_flags, _aspeco, ASF_FOLLOWKILLER, "Followkiller", "followkiller", "fk"); + + superspec_msg("^3Current auto spectate options are:\n\n\n\n\n", "\n^3Current auto spectate options are:\n", player, _aspeco, 1); + return true; + } + + if(cmd_name == "followpowerup") + { + FOREACH_CLIENT(IS_PLAYER(it) && (it.strength_finished > time || it.invincible_finished > time), LAMBDA(return superspec_Spectate(player, it))); + + superspec_msg("", "", player, "No active powerup\n", 1); + return true; + } + + if(cmd_name == "followstrength") + { + FOREACH_CLIENT(IS_PLAYER(it) && it.strength_finished > time, LAMBDA(return superspec_Spectate(player, it))); + + superspec_msg("", "", player, "No active Strength\n", 1); + return true; + } + + if(cmd_name == "followshield") + { + FOREACH_CLIENT(IS_PLAYER(it) && it.invincible_finished > time, LAMBDA(return superspec_Spectate(player, it))); + + superspec_msg("", "", player, "No active Shield\n", 1); + return true; + } +#undef OPTIONINFO +} + +MUTATOR_HOOKFUNCTION(superspec, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":SS"); +} + +MUTATOR_HOOKFUNCTION(superspec, BuildMutatorsPrettyString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Super Spectators"); +} + +void superspec_hello(entity this) +{ + if(this.enemy.crypto_idfp == "") + Send_Notification(NOTIF_ONE_ONLY, this.enemy, MSG_INFO, INFO_SUPERSPEC_MISSING_UID); + + delete(this); +} + +MUTATOR_HOOKFUNCTION(superspec, ClientConnect) +{ + entity player = M_ARGV(0, entity); + + if(!IS_REAL_CLIENT(player)) + return; + + string fn = "superspec-local.options"; + float fh; + + player.superspec_flags = SSF_VERBOSE; + player.superspec_itemfilter = ""; + + entity _hello = spawn(); + _hello.enemy = player; + setthink(_hello, superspec_hello); + _hello.nextthink = time + 5; + + if (!_ISLOCAL(player)) + { + if(player.crypto_idfp == "") + return; + + fn = sprintf("superspec-%s.options", uri_escape(player.crypto_idfp)); + } + + fh = fopen(fn, FILE_READ); + if(fh < 0) + { + LOG_TRACE("^1ERROR: ^7 superspec can not open ", fn, " for reading."); + } + else + { + string _magic = fgets(fh); + if(_magic != _SSMAGIX) + { + LOG_TRACE("^1ERROR^7 While reading superspec options file: unknown magic"); + } + else + { + player.autospec_flags = stof(fgets(fh)); + player.superspec_flags = stof(fgets(fh)); + player.superspec_itemfilter = strzone(fgets(fh)); + } + fclose(fh); + } +} + +MUTATOR_HOOKFUNCTION(superspec, PlayerDies) +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + + FOREACH_CLIENT(IS_SPEC(it), LAMBDA( + if(it.autospec_flags & ASF_FOLLOWKILLER && IS_PLAYER(frag_attacker) && it.enemy == frag_target) + { + if(it.autospec_flags & ASF_SHOWWHAT) + superspec_msg("", "", it, sprintf("^7Following %s^7 due to followkiller\n", frag_attacker.netname), 2); + + superspec_Spectate(it, frag_attacker); + } + )); +} + +MUTATOR_HOOKFUNCTION(superspec, ClientDisconnect) +{ + entity player = M_ARGV(0, entity); + + superspec_save_client_conf(player); +} diff --git a/qcsrc/common/mutators/mutator/superspec/sv_superspec.qh b/qcsrc/common/mutators/mutator/superspec/sv_superspec.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/superspec/sv_superspec.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/touchexplode/_mod.inc b/qcsrc/common/mutators/mutator/touchexplode/_mod.inc index 42dad3926e..f341e2afe1 100644 --- a/qcsrc/common/mutators/mutator/touchexplode/_mod.inc +++ b/qcsrc/common/mutators/mutator/touchexplode/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/touchexplode/_mod.qh b/qcsrc/common/mutators/mutator/touchexplode/_mod.qh index ec71f52d74..18cdcc60f6 100644 --- a/qcsrc/common/mutators/mutator/touchexplode/_mod.qh +++ b/qcsrc/common/mutators/mutator/touchexplode/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/touchexplode/module.inc b/qcsrc/common/mutators/mutator/touchexplode/module.inc deleted file mode 100644 index d3b0ea5af9..0000000000 --- a/qcsrc/common/mutators/mutator/touchexplode/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "touchexplode.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/touchexplode/sv_touchexplode.qc b/qcsrc/common/mutators/mutator/touchexplode/sv_touchexplode.qc new file mode 100644 index 0000000000..38ac30595d --- /dev/null +++ b/qcsrc/common/mutators/mutator/touchexplode/sv_touchexplode.qc @@ -0,0 +1,47 @@ +#include "sv_touchexplode.qh" + +float autocvar_g_touchexplode_radius; +float autocvar_g_touchexplode_damage; +float autocvar_g_touchexplode_edgedamage; +float autocvar_g_touchexplode_force; + +REGISTER_MUTATOR(touchexplode, cvar("g_touchexplode")); + +.float touchexplode_time; + +void PlayerTouchExplode(entity p1, entity p2) +{ + vector org = (p1.origin + p2.origin) * 0.5; + org.z += (p1.mins.z + p2.mins.z) * 0.5; + + sound(p1, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM); + Send_Effect(EFFECT_EXPLOSION_SMALL, org, '0 0 0', 1); + + entity e = spawn(); + setorigin(e, org); + RadiusDamage(e, NULL, autocvar_g_touchexplode_damage, autocvar_g_touchexplode_edgedamage, autocvar_g_touchexplode_radius, NULL, NULL, autocvar_g_touchexplode_force, DEATH_TOUCHEXPLODE.m_id, NULL); + delete(e); +} + +MUTATOR_HOOKFUNCTION(touchexplode, PlayerPreThink) +{ + entity player = M_ARGV(0, entity); + + if(time > player.touchexplode_time) + if(!gameover) + if(!STAT(FROZEN, player)) + if(IS_PLAYER(player)) + if(!IS_DEAD(player)) + if(!IS_INDEPENDENT_PLAYER(player)) + FOREACH_CLIENT(IS_PLAYER(it) && it != player, LAMBDA( + if(time > it.touchexplode_time) + if(!STAT(FROZEN, it)) + if(!IS_DEAD(it)) + if (!IS_INDEPENDENT_PLAYER(it)) + if(boxesoverlap(player.absmin, player.absmax, it.absmin, it.absmax)) + { + PlayerTouchExplode(player, it); + player.touchexplode_time = it.touchexplode_time = time + 0.2; + } + )); +} diff --git a/qcsrc/common/mutators/mutator/touchexplode/sv_touchexplode.qh b/qcsrc/common/mutators/mutator/touchexplode/sv_touchexplode.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/touchexplode/sv_touchexplode.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/touchexplode/touchexplode.qc b/qcsrc/common/mutators/mutator/touchexplode/touchexplode.qc deleted file mode 100644 index c585e7e906..0000000000 --- a/qcsrc/common/mutators/mutator/touchexplode/touchexplode.qc +++ /dev/null @@ -1,47 +0,0 @@ -#ifdef IMPLEMENTATION -float autocvar_g_touchexplode_radius; -float autocvar_g_touchexplode_damage; -float autocvar_g_touchexplode_edgedamage; -float autocvar_g_touchexplode_force; - -REGISTER_MUTATOR(touchexplode, cvar("g_touchexplode")); - -.float touchexplode_time; - -void PlayerTouchExplode(entity p1, entity p2) -{ - vector org = (p1.origin + p2.origin) * 0.5; - org.z += (p1.mins.z + p2.mins.z) * 0.5; - - sound(p1, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM); - Send_Effect(EFFECT_EXPLOSION_SMALL, org, '0 0 0', 1); - - entity e = spawn(); - setorigin(e, org); - RadiusDamage(e, NULL, autocvar_g_touchexplode_damage, autocvar_g_touchexplode_edgedamage, autocvar_g_touchexplode_radius, NULL, NULL, autocvar_g_touchexplode_force, DEATH_TOUCHEXPLODE.m_id, NULL); - delete(e); -} - -MUTATOR_HOOKFUNCTION(touchexplode, PlayerPreThink) -{ - entity player = M_ARGV(0, entity); - - if(time > player.touchexplode_time) - if(!gameover) - if(!STAT(FROZEN, player)) - if(IS_PLAYER(player)) - if(!IS_DEAD(player)) - if(!IS_INDEPENDENT_PLAYER(player)) - FOREACH_CLIENT(IS_PLAYER(it) && it != player, LAMBDA( - if(time > it.touchexplode_time) - if(!STAT(FROZEN, it)) - if(!IS_DEAD(it)) - if (!IS_INDEPENDENT_PLAYER(it)) - if(boxesoverlap(player.absmin, player.absmax, it.absmin, it.absmax)) - { - PlayerTouchExplode(player, it); - player.touchexplode_time = it.touchexplode_time = time + 0.2; - } - )); -} -#endif diff --git a/qcsrc/common/mutators/mutator/vampire/_mod.inc b/qcsrc/common/mutators/mutator/vampire/_mod.inc index 856ed84c46..57b844451a 100644 --- a/qcsrc/common/mutators/mutator/vampire/_mod.inc +++ b/qcsrc/common/mutators/mutator/vampire/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/vampire/_mod.qh b/qcsrc/common/mutators/mutator/vampire/_mod.qh index 551184c77b..d7d8a4143c 100644 --- a/qcsrc/common/mutators/mutator/vampire/_mod.qh +++ b/qcsrc/common/mutators/mutator/vampire/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/vampire/module.inc b/qcsrc/common/mutators/mutator/vampire/module.inc deleted file mode 100644 index 864ea28b24..0000000000 --- a/qcsrc/common/mutators/mutator/vampire/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "vampire.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/vampire/sv_vampire.qc b/qcsrc/common/mutators/mutator/vampire/sv_vampire.qc new file mode 100644 index 0000000000..3a435c5ed1 --- /dev/null +++ b/qcsrc/common/mutators/mutator/vampire/sv_vampire.qc @@ -0,0 +1,28 @@ +#include "sv_vampire.qh" + +REGISTER_MUTATOR(vampire, cvar("g_vampire") && !cvar("g_instagib")); + +MUTATOR_HOOKFUNCTION(vampire, PlayerDamage_SplitHealthArmor) +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + float damage_take = M_ARGV(4, float); + + if(time >= frag_target.spawnshieldtime) + if(frag_target != frag_attacker) + if(!IS_DEAD(frag_target)) + { + frag_attacker.health += bound(0, damage_take, frag_target.health); + frag_attacker.health = bound(0, frag_attacker.health, autocvar_g_balance_health_limit); + } +} + +MUTATOR_HOOKFUNCTION(vampire, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Vampire"); +} + +MUTATOR_HOOKFUNCTION(vampire, BuildMutatorsPrettyString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Vampire"); +} diff --git a/qcsrc/common/mutators/mutator/vampire/sv_vampire.qh b/qcsrc/common/mutators/mutator/vampire/sv_vampire.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/vampire/sv_vampire.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/vampire/vampire.qc b/qcsrc/common/mutators/mutator/vampire/vampire.qc deleted file mode 100644 index d245c8059b..0000000000 --- a/qcsrc/common/mutators/mutator/vampire/vampire.qc +++ /dev/null @@ -1,28 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(vampire, cvar("g_vampire") && !cvar("g_instagib")); - -MUTATOR_HOOKFUNCTION(vampire, PlayerDamage_SplitHealthArmor) -{ - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - float damage_take = M_ARGV(4, float); - - if(time >= frag_target.spawnshieldtime) - if(frag_target != frag_attacker) - if(!IS_DEAD(frag_target)) - { - frag_attacker.health += bound(0, damage_take, frag_target.health); - frag_attacker.health = bound(0, frag_attacker.health, autocvar_g_balance_health_limit); - } -} - -MUTATOR_HOOKFUNCTION(vampire, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Vampire"); -} - -MUTATOR_HOOKFUNCTION(vampire, BuildMutatorsPrettyString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Vampire"); -} -#endif diff --git a/qcsrc/common/mutators/mutator/vampirehook/_mod.inc b/qcsrc/common/mutators/mutator/vampirehook/_mod.inc index 868a4ef3fc..72b2bacb9c 100644 --- a/qcsrc/common/mutators/mutator/vampirehook/_mod.inc +++ b/qcsrc/common/mutators/mutator/vampirehook/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/vampirehook/_mod.qh b/qcsrc/common/mutators/mutator/vampirehook/_mod.qh index 5d57816c96..eaa7b320a8 100644 --- a/qcsrc/common/mutators/mutator/vampirehook/_mod.qh +++ b/qcsrc/common/mutators/mutator/vampirehook/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/vampirehook/module.inc b/qcsrc/common/mutators/mutator/vampirehook/module.inc deleted file mode 100644 index 17ecf6005f..0000000000 --- a/qcsrc/common/mutators/mutator/vampirehook/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "vampirehook.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qc b/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qc new file mode 100644 index 0000000000..e2b0f57d76 --- /dev/null +++ b/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qc @@ -0,0 +1,37 @@ +#include "sv_vampirehook.qh" + +REGISTER_MUTATOR(vh, cvar("g_vampirehook")); + +bool autocvar_g_vampirehook_teamheal; +float autocvar_g_vampirehook_damage; +float autocvar_g_vampirehook_damagerate; +float autocvar_g_vampirehook_health_steal; + +.float last_dmg; + +MUTATOR_HOOKFUNCTION(vh, GrappleHookThink) +{ + entity thehook = M_ARGV(0, entity); + + entity dmgent = ((SAME_TEAM(thehook.owner, thehook.aiment) && autocvar_g_vampirehook_teamheal) ? thehook.owner : thehook.aiment); + + if(IS_PLAYER(thehook.aiment)) + if(thehook.last_dmg < time) + if(!STAT(FROZEN, thehook.aiment)) + if(time >= game_starttime) + if(DIFF_TEAM(thehook.owner, thehook.aiment) || autocvar_g_vampirehook_teamheal) + if(thehook.aiment.health > 0) + if(autocvar_g_vampirehook_damage) + { + thehook.last_dmg = time + autocvar_g_vampirehook_damagerate; + thehook.owner.damage_dealt += autocvar_g_vampirehook_damage; + Damage(dmgent, thehook, thehook.owner, autocvar_g_vampirehook_damage, WEP_HOOK.m_id, thehook.origin, '0 0 0'); + if(SAME_TEAM(thehook.owner, thehook.aiment)) + thehook.aiment.health = min(thehook.aiment.health + autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max); + else + thehook.owner.health = min(thehook.owner.health + autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max); + + if(dmgent == thehook.owner) + dmgent.health -= autocvar_g_vampirehook_damage; // FIXME: friendly fire?! + } +} diff --git a/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qh b/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/vampirehook/vampirehook.qc b/qcsrc/common/mutators/mutator/vampirehook/vampirehook.qc deleted file mode 100644 index a54dc74e40..0000000000 --- a/qcsrc/common/mutators/mutator/vampirehook/vampirehook.qc +++ /dev/null @@ -1,38 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(vh, cvar("g_vampirehook")); - -bool autocvar_g_vampirehook_teamheal; -float autocvar_g_vampirehook_damage; -float autocvar_g_vampirehook_damagerate; -float autocvar_g_vampirehook_health_steal; - -.float last_dmg; - -MUTATOR_HOOKFUNCTION(vh, GrappleHookThink) -{ - entity thehook = M_ARGV(0, entity); - - entity dmgent = ((SAME_TEAM(thehook.owner, thehook.aiment) && autocvar_g_vampirehook_teamheal) ? thehook.owner : thehook.aiment); - - if(IS_PLAYER(thehook.aiment)) - if(thehook.last_dmg < time) - if(!STAT(FROZEN, thehook.aiment)) - if(time >= game_starttime) - if(DIFF_TEAM(thehook.owner, thehook.aiment) || autocvar_g_vampirehook_teamheal) - if(thehook.aiment.health > 0) - if(autocvar_g_vampirehook_damage) - { - thehook.last_dmg = time + autocvar_g_vampirehook_damagerate; - thehook.owner.damage_dealt += autocvar_g_vampirehook_damage; - Damage(dmgent, thehook, thehook.owner, autocvar_g_vampirehook_damage, WEP_HOOK.m_id, thehook.origin, '0 0 0'); - if(SAME_TEAM(thehook.owner, thehook.aiment)) - thehook.aiment.health = min(thehook.aiment.health + autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max); - else - thehook.owner.health = min(thehook.owner.health + autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max); - - if(dmgent == thehook.owner) - dmgent.health -= autocvar_g_vampirehook_damage; // FIXME: friendly fire?! - } -} - -#endif diff --git a/qcsrc/common/mutators/mutator/waypoints/all.qh b/qcsrc/common/mutators/mutator/waypoints/all.qh index 4645130551..77c4312001 100644 --- a/qcsrc/common/mutators/mutator/waypoints/all.qh +++ b/qcsrc/common/mutators/mutator/waypoints/all.qh @@ -1,9 +1,8 @@ -#ifndef WAYPOINTS_ALL_H -#define WAYPOINTS_ALL_H +#pragma once #include "waypointsprites.qh" -REGISTRY(Waypoints, BITS(6)) +REGISTRY(Waypoints, BITS(7)) #define Waypoints_from(i) _Waypoints_from(i, WP_Null) REGISTER_REGISTRY(Waypoints) REGISTRY_CHECK(Waypoints) @@ -58,5 +57,3 @@ REGISTER_RADARICON(Vehicle, 1); REGISTER_RADARICON(Weapon, 1); #include "all.inc" - -#endif diff --git a/qcsrc/common/mutators/mutator/waypoints/module.inc b/qcsrc/common/mutators/mutator/waypoints/module.inc deleted file mode 100644 index 50bb5b4d6e..0000000000 --- a/qcsrc/common/mutators/mutator/waypoints/module.inc +++ /dev/null @@ -1 +0,0 @@ -#include "waypointsprites.qc" diff --git a/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc b/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc index e99303823b..7e5f79317f 100644 --- a/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc +++ b/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc @@ -1,7 +1,5 @@ #include "waypointsprites.qh" -#ifdef IMPLEMENTATION - REGISTER_MUTATOR(waypointsprites, true); REGISTER_NET_LINKED(waypointsprites) @@ -18,10 +16,12 @@ bool WaypointSprite_SendEntity(entity this, entity to, float sendflags) sendflags |= 0x80; int f = 0; - if(this.currentammo) + if(this.currentammo == 1) f |= 1; // hideable if(this.exteriormodeltoclient == to) f |= 2; // my own + if(this.currentammo == 2) + f |= 2; // radar only MUTATOR_CALLHOOK(SendWaypoint, this, to, sendflags, f); sendflags = M_ARGV(2, int); @@ -1147,4 +1147,3 @@ void WaypointSprite_PlayerGone(entity this) WaypointSprite_DetachCarrier(this); } #endif -#endif diff --git a/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh b/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh index 26e4058d24..6a420b734e 100644 --- a/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh +++ b/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh @@ -1,5 +1,4 @@ -#ifndef WAYPOINTSPRITES_H -#define WAYPOINTSPRITES_H +#pragma once #include "all.qh" @@ -118,6 +117,8 @@ void Draw_WaypointSprite(entity this); #endif #ifdef SVQC +.entity sprite; + float autocvar_sv_waypointsprite_deadlifetime; float autocvar_sv_waypointsprite_deployed_lifetime; float autocvar_sv_waypointsprite_limitedrange; @@ -234,5 +235,3 @@ void WaypointSprite_PlayerDead(entity this); void WaypointSprite_PlayerGone(entity this); #endif - -#endif diff --git a/qcsrc/common/mutators/mutator/weaponarena_random/_mod.inc b/qcsrc/common/mutators/mutator/weaponarena_random/_mod.inc index 742510b883..034b51d117 100644 --- a/qcsrc/common/mutators/mutator/weaponarena_random/_mod.inc +++ b/qcsrc/common/mutators/mutator/weaponarena_random/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/weaponarena_random/_mod.qh b/qcsrc/common/mutators/mutator/weaponarena_random/_mod.qh index 68d6a24c42..05b87c73d3 100644 --- a/qcsrc/common/mutators/mutator/weaponarena_random/_mod.qh +++ b/qcsrc/common/mutators/mutator/weaponarena_random/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/weaponarena_random/module.inc b/qcsrc/common/mutators/mutator/weaponarena_random/module.inc deleted file mode 100644 index b7a5f66901..0000000000 --- a/qcsrc/common/mutators/mutator/weaponarena_random/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "weaponarena_random.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qc b/qcsrc/common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qc new file mode 100644 index 0000000000..7ac4504ec9 --- /dev/null +++ b/qcsrc/common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qc @@ -0,0 +1,14 @@ +#include "sv_weaponarena_random.qh" + +// WEAPONTODO: rename the cvars +REGISTER_MUTATOR(weaponarena_random, true); + +MUTATOR_HOOKFUNCTION(weaponarena_random, PlayerSpawn) +{ + if (!g_weaponarena_random) return; + entity player = M_ARGV(0, entity); + + if (g_weaponarena_random_with_blaster) player.weapons &= ~WEPSET(BLASTER); + W_RandomWeapons(player, g_weaponarena_random); + if (g_weaponarena_random_with_blaster) player.weapons |= WEPSET(BLASTER); +} diff --git a/qcsrc/common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qh b/qcsrc/common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/weaponarena_random/weaponarena_random.qc b/qcsrc/common/mutators/mutator/weaponarena_random/weaponarena_random.qc deleted file mode 100644 index e4d400db70..0000000000 --- a/qcsrc/common/mutators/mutator/weaponarena_random/weaponarena_random.qc +++ /dev/null @@ -1,15 +0,0 @@ -#ifdef IMPLEMENTATION -// WEAPONTODO: rename the cvars -REGISTER_MUTATOR(weaponarena_random, true); - -MUTATOR_HOOKFUNCTION(weaponarena_random, PlayerSpawn) -{ - if (!g_weaponarena_random) return; - entity player = M_ARGV(0, entity); - - if (g_weaponarena_random_with_blaster) player.weapons &= ~WEPSET(BLASTER); - W_RandomWeapons(player, g_weaponarena_random); - if (g_weaponarena_random_with_blaster) player.weapons |= WEPSET(BLASTER); -} - -#endif diff --git a/qcsrc/common/notifications/all.qc b/qcsrc/common/notifications/all.qc index e4f082355b..acd570d0b6 100644 --- a/qcsrc/common/notifications/all.qc +++ b/qcsrc/common/notifications/all.qc @@ -1,3 +1,4 @@ +#include "all.qh" #if defined(CSQC) #include #elif defined(MENUQC) @@ -7,8 +8,7 @@ #include #include #include - #include "all.qh" - #include + #include #endif // ================================================ diff --git a/qcsrc/common/notifications/all.qh b/qcsrc/common/notifications/all.qh index 983b69f3f4..2715925944 100644 --- a/qcsrc/common/notifications/all.qh +++ b/qcsrc/common/notifications/all.qh @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include @@ -159,7 +159,7 @@ GENERIC_COMMAND(dumpnotifs, "Dump all notifications into notifications_dump.txt" { case CMD_REQUEST_COMMAND: { - #ifndef MENUQC + #ifdef GAMEQC string filename = argv(1); bool alsoprint = false; if (filename == "") diff --git a/qcsrc/common/physics/_mod.inc b/qcsrc/common/physics/_mod.inc index 3a61cd4e9a..100aecae29 100644 --- a/qcsrc/common/physics/_mod.inc +++ b/qcsrc/common/physics/_mod.inc @@ -1,3 +1,5 @@ // generated file; do not modify #include #include + +#include diff --git a/qcsrc/common/physics/_mod.qh b/qcsrc/common/physics/_mod.qh index 39dacad8e2..377a7b340d 100644 --- a/qcsrc/common/physics/_mod.qh +++ b/qcsrc/common/physics/_mod.qh @@ -1,3 +1,5 @@ // generated file; do not modify #include #include + +#include diff --git a/qcsrc/common/physics/movetypes/follow.qc b/qcsrc/common/physics/movetypes/follow.qc index 5152089870..3009069052 100644 --- a/qcsrc/common/physics/movetypes/follow.qc +++ b/qcsrc/common/physics/movetypes/follow.qc @@ -1,3 +1,4 @@ +#include "follow.qh" void _Movetype_Physics_Follow(entity this) // SV_Physics_Follow { entity e = this.aiment; diff --git a/qcsrc/common/physics/movetypes/follow.qh b/qcsrc/common/physics/movetypes/follow.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/physics/movetypes/follow.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/physics/movetypes/movetypes.qc b/qcsrc/common/physics/movetypes/movetypes.qc index 1810fb841d..8ac17d883b 100644 --- a/qcsrc/common/physics/movetypes/movetypes.qc +++ b/qcsrc/common/physics/movetypes/movetypes.qc @@ -1,10 +1,10 @@ +#include "movetypes.qh" #include "../player.qh" #if defined(CSQC) #include #include #include - #include "movetypes.qh" #include #include #elif defined(MENUQC) diff --git a/qcsrc/common/physics/movetypes/step.qc b/qcsrc/common/physics/movetypes/step.qc index 7eb553bfaa..a41269f79c 100644 --- a/qcsrc/common/physics/movetypes/step.qc +++ b/qcsrc/common/physics/movetypes/step.qc @@ -1,3 +1,4 @@ +#include "step.qh" void _Movetype_Physics_Step(entity this, float dt) // SV_Physics_Step { if(IS_ONGROUND(this)) diff --git a/qcsrc/common/physics/movetypes/step.qh b/qcsrc/common/physics/movetypes/step.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/physics/movetypes/step.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/physics/movetypes/toss.qc b/qcsrc/common/physics/movetypes/toss.qc index 4821e34f6a..498852135b 100644 --- a/qcsrc/common/physics/movetypes/toss.qc +++ b/qcsrc/common/physics/movetypes/toss.qc @@ -1,3 +1,4 @@ +#include "toss.qh" #include "../player.qh" void _Movetype_Physics_Toss(entity this, float dt) // SV_Physics_Toss diff --git a/qcsrc/common/physics/movetypes/toss.qh b/qcsrc/common/physics/movetypes/toss.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/physics/movetypes/toss.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/physics/movetypes/walk.qc b/qcsrc/common/physics/movetypes/walk.qc index bf58ca5824..c20e82e834 100644 --- a/qcsrc/common/physics/movetypes/walk.qc +++ b/qcsrc/common/physics/movetypes/walk.qc @@ -1,3 +1,4 @@ +#include "walk.qh" void _Movetype_Physics_Walk(entity this, float dt) // SV_WalkMove { vector stepnormal = '0 0 0'; diff --git a/qcsrc/common/physics/movetypes/walk.qh b/qcsrc/common/physics/movetypes/walk.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/physics/movetypes/walk.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/playerstats.qc b/qcsrc/common/playerstats.qc index ad58221269..4dab61164e 100644 --- a/qcsrc/common/playerstats.qc +++ b/qcsrc/common/playerstats.qc @@ -1,12 +1,12 @@ +#include "playerstats.qh" #if defined(CSQC) #elif defined(MENUQC) #elif defined(SVQC) #include "constants.qh" #include "util.qh" - #include + #include #include "../server/weapons/accuracy.qh" #include "../server/defs.qh" - #include "playerstats.qh" #include "../server/scores.qh" #endif diff --git a/qcsrc/common/sounds/all.qc b/qcsrc/common/sounds/all.qc index 9096dddfa8..b3bdf99e91 100644 --- a/qcsrc/common/sounds/all.qc +++ b/qcsrc/common/sounds/all.qc @@ -1,3 +1,4 @@ +#include "all.qh" #ifdef SVQC bool autocvar_bot_sound_monopoly; diff --git a/qcsrc/common/sounds/all.qh b/qcsrc/common/sounds/all.qh index 763aae67cf..b7b296341b 100644 --- a/qcsrc/common/sounds/all.qh +++ b/qcsrc/common/sounds/all.qh @@ -2,7 +2,7 @@ #include "sound.qh" -REGISTRY(Sounds, BITS(8)) +REGISTRY(Sounds, BITS(9)) #define Sounds_from(i) _Sounds_from(i, SND_Null) REGISTER_REGISTRY(Sounds) diff --git a/qcsrc/common/stats.qh b/qcsrc/common/stats.qh index a24243bb6d..d6aa068ea6 100644 --- a/qcsrc/common/stats.qh +++ b/qcsrc/common/stats.qh @@ -1,7 +1,7 @@ #pragma once #ifdef SVQC -#include +#include #endif // Full list of all stat constants, included in a single location for easy reference @@ -121,6 +121,7 @@ REGISTER_STAT(REVIVE_PROGRESS, float) REGISTER_STAT(ROUNDLOST, int) REGISTER_STAT(BUFF_TIME, float) REGISTER_STAT(CTF_FLAGSTATUS, int) +REGISTER_STAT(CAPTURE_PROGRESS, float) REGISTER_STAT(ENTRAP_ORB, float) REGISTER_STAT(ENTRAP_ORB_ALPHA, float) REGISTER_STAT(ITEMSTIME, int, autocvar_sv_itemstime) diff --git a/qcsrc/common/t_items.qc b/qcsrc/common/t_items.qc index bc914d853d..ebc9711414 100644 --- a/qcsrc/common/t_items.qc +++ b/qcsrc/common/t_items.qc @@ -1,12 +1,12 @@ #include "t_items.qh" -#include "items/all.qc" +#include "items/_mod.qh" #if defined(SVQC) #include "../server/bot/api.qh" - #include + #include #include "../server/weapons/common.qh" #include "../server/weapons/selection.qh" @@ -20,12 +20,12 @@ #include - #include + #include #include "../lib/warpzone/util_server.qh" #elif defined(CSQC) #include "physics/movetypes/movetypes.qh" - #include + #include #include "../lib/csqcmodel/cl_model.qh" #include "../lib/csqcmodel/common.qh" #endif diff --git a/qcsrc/common/triggers/_mod.inc b/qcsrc/common/triggers/_mod.inc index c049b10828..9b327de55d 100644 --- a/qcsrc/common/triggers/_mod.inc +++ b/qcsrc/common/triggers/_mod.inc @@ -4,3 +4,8 @@ #include #include #include + +#include +#include +#include +#include diff --git a/qcsrc/common/triggers/_mod.qh b/qcsrc/common/triggers/_mod.qh index 2fba604617..d3bb2dc3ad 100644 --- a/qcsrc/common/triggers/_mod.qh +++ b/qcsrc/common/triggers/_mod.qh @@ -4,3 +4,8 @@ #include #include #include + +#include +#include +#include +#include diff --git a/qcsrc/common/triggers/func/bobbing.qc b/qcsrc/common/triggers/func/bobbing.qc index 094673b5bb..b7034939ae 100644 --- a/qcsrc/common/triggers/func/bobbing.qc +++ b/qcsrc/common/triggers/func/bobbing.qc @@ -1,3 +1,4 @@ +#include "bobbing.qh" #ifdef SVQC .float height; void func_bobbing_controller_think(entity this) diff --git a/qcsrc/common/triggers/func/bobbing.qh b/qcsrc/common/triggers/func/bobbing.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/func/bobbing.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/func/breakable.qc b/qcsrc/common/triggers/func/breakable.qc index 29d6c6a269..2b7a1155f4 100644 --- a/qcsrc/common/triggers/func/breakable.qc +++ b/qcsrc/common/triggers/func/breakable.qc @@ -1,3 +1,4 @@ +#include "breakable.qh" #ifdef SVQC #include diff --git a/qcsrc/common/triggers/func/button.qc b/qcsrc/common/triggers/func/button.qc index b186066e60..916ff8ca1f 100644 --- a/qcsrc/common/triggers/func/button.qc +++ b/qcsrc/common/triggers/func/button.qc @@ -1,3 +1,4 @@ +#include "button.qh" #ifdef SVQC // button and multiple button diff --git a/qcsrc/common/triggers/func/button.qh b/qcsrc/common/triggers/func/button.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/func/button.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/func/conveyor.qc b/qcsrc/common/triggers/func/conveyor.qc index 73ceb63940..4902deea5c 100644 --- a/qcsrc/common/triggers/func/conveyor.qc +++ b/qcsrc/common/triggers/func/conveyor.qc @@ -1,3 +1,4 @@ +#include "conveyor.qh" REGISTER_NET_LINKED(ENT_CLIENT_CONVEYOR) void conveyor_think(entity this) diff --git a/qcsrc/common/triggers/func/conveyor.qh b/qcsrc/common/triggers/func/conveyor.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/func/conveyor.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/func/door.qc b/qcsrc/common/triggers/func/door.qc index 706784eda7..8a82802607 100644 --- a/qcsrc/common/triggers/func/door.qc +++ b/qcsrc/common/triggers/func/door.qc @@ -1,3 +1,4 @@ +#include "door.qh" /* Doors are similar to buttons, but can spawn a fat trigger field around them diff --git a/qcsrc/common/triggers/func/door_rotating.qc b/qcsrc/common/triggers/func/door_rotating.qc index 31171899fc..2c72dc9cf0 100644 --- a/qcsrc/common/triggers/func/door_rotating.qc +++ b/qcsrc/common/triggers/func/door_rotating.qc @@ -1,3 +1,4 @@ +#include "door_rotating.qh" #ifdef SVQC /*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS if two doors touch, they are assumed to be connected and operate as a unit. diff --git a/qcsrc/common/triggers/func/door_rotating.qh b/qcsrc/common/triggers/func/door_rotating.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/func/door_rotating.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/func/door_secret.qc b/qcsrc/common/triggers/func/door_secret.qc index 600949fe5c..efe997c855 100644 --- a/qcsrc/common/triggers/func/door_secret.qc +++ b/qcsrc/common/triggers/func/door_secret.qc @@ -1,3 +1,4 @@ +#include "door_secret.qh" #ifdef SVQC void fd_secret_move1(entity this); void fd_secret_move2(entity this); diff --git a/qcsrc/common/triggers/func/door_secret.qh b/qcsrc/common/triggers/func/door_secret.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/func/door_secret.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/func/fourier.qc b/qcsrc/common/triggers/func/fourier.qc index 52eab115a0..600f393730 100644 --- a/qcsrc/common/triggers/func/fourier.qc +++ b/qcsrc/common/triggers/func/fourier.qc @@ -1,3 +1,4 @@ +#include "fourier.qh" #ifdef SVQC /*QUAKED spawnfunc_func_fourier (0 .5 .8) ? Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions. diff --git a/qcsrc/common/triggers/func/fourier.qh b/qcsrc/common/triggers/func/fourier.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/func/fourier.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/func/ladder.qc b/qcsrc/common/triggers/func/ladder.qc index ae9160dfda..5ff2bdcc24 100644 --- a/qcsrc/common/triggers/func/ladder.qc +++ b/qcsrc/common/triggers/func/ladder.qc @@ -1,3 +1,4 @@ +#include "ladder.qh" REGISTER_NET_LINKED(ENT_CLIENT_LADDER) void func_ladder_touch(entity this, entity toucher) diff --git a/qcsrc/common/triggers/func/pendulum.qc b/qcsrc/common/triggers/func/pendulum.qc index 05f7b75a67..946712509b 100644 --- a/qcsrc/common/triggers/func/pendulum.qc +++ b/qcsrc/common/triggers/func/pendulum.qc @@ -1,3 +1,4 @@ +#include "pendulum.qh" #ifdef SVQC .float freq; void func_pendulum_controller_think(entity this) diff --git a/qcsrc/common/triggers/func/pendulum.qh b/qcsrc/common/triggers/func/pendulum.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/func/pendulum.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/func/plat.qc b/qcsrc/common/triggers/func/plat.qc index 396636e8cd..5d90924daa 100644 --- a/qcsrc/common/triggers/func/plat.qc +++ b/qcsrc/common/triggers/func/plat.qc @@ -1,3 +1,4 @@ +#include "plat.qh" REGISTER_NET_LINKED(ENT_CLIENT_PLAT) #ifdef SVQC diff --git a/qcsrc/common/triggers/func/plat.qh b/qcsrc/common/triggers/func/plat.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/func/plat.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/func/pointparticles.qc b/qcsrc/common/triggers/func/pointparticles.qc index 0cf0615da8..a0773f249a 100644 --- a/qcsrc/common/triggers/func/pointparticles.qc +++ b/qcsrc/common/triggers/func/pointparticles.qc @@ -1,3 +1,4 @@ +#include "pointparticles.qh" REGISTER_NET_LINKED(ENT_CLIENT_POINTPARTICLES) #ifdef SVQC diff --git a/qcsrc/common/triggers/func/pointparticles.qh b/qcsrc/common/triggers/func/pointparticles.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/func/pointparticles.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/func/rainsnow.qc b/qcsrc/common/triggers/func/rainsnow.qc index cc7dc09228..c8b4e29243 100644 --- a/qcsrc/common/triggers/func/rainsnow.qc +++ b/qcsrc/common/triggers/func/rainsnow.qc @@ -1,3 +1,4 @@ +#include "rainsnow.qh" REGISTER_NET_LINKED(ENT_CLIENT_RAINSNOW) #ifdef SVQC diff --git a/qcsrc/common/triggers/func/rainsnow.qh b/qcsrc/common/triggers/func/rainsnow.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/func/rainsnow.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/func/rotating.qc b/qcsrc/common/triggers/func/rotating.qc index 22f3dedea2..1adaea91d3 100644 --- a/qcsrc/common/triggers/func/rotating.qc +++ b/qcsrc/common/triggers/func/rotating.qc @@ -1,3 +1,4 @@ +#include "rotating.qh" #ifdef SVQC void func_rotating_setactive(entity this, int astate) { diff --git a/qcsrc/common/triggers/func/rotating.qh b/qcsrc/common/triggers/func/rotating.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/func/rotating.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/func/stardust.qc b/qcsrc/common/triggers/func/stardust.qc index db5081b61b..9c2fba8ada 100644 --- a/qcsrc/common/triggers/func/stardust.qc +++ b/qcsrc/common/triggers/func/stardust.qc @@ -1,3 +1,4 @@ +#include "stardust.qh" #ifdef SVQC spawnfunc(func_stardust) { diff --git a/qcsrc/common/triggers/func/stardust.qh b/qcsrc/common/triggers/func/stardust.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/func/stardust.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/func/train.qc b/qcsrc/common/triggers/func/train.qc index 247bfb0fc1..dd9eaac860 100644 --- a/qcsrc/common/triggers/func/train.qc +++ b/qcsrc/common/triggers/func/train.qc @@ -1,3 +1,4 @@ +#include "train.qh" .float train_wait_turning; void train_next(entity this); #ifdef SVQC diff --git a/qcsrc/common/triggers/func/vectormamamam.qc b/qcsrc/common/triggers/func/vectormamamam.qc index accdc99835..18d58d6491 100644 --- a/qcsrc/common/triggers/func/vectormamamam.qc +++ b/qcsrc/common/triggers/func/vectormamamam.qc @@ -1,3 +1,4 @@ +#include "vectormamamam.qh" #ifdef SVQC // reusing some fields havocbots declared .entity wp00, wp01, wp02, wp03; diff --git a/qcsrc/common/triggers/func/vectormamamam.qh b/qcsrc/common/triggers/func/vectormamamam.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/func/vectormamamam.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/include.qh b/qcsrc/common/triggers/include.qh index f3f81738a1..87c07c14df 100644 --- a/qcsrc/common/triggers/include.qh +++ b/qcsrc/common/triggers/include.qh @@ -1,7 +1,7 @@ #pragma once // some required common stuff -#ifdef CSQC +#ifdef SVQC #include #endif #include "triggers.qh" diff --git a/qcsrc/common/triggers/misc/corner.qc b/qcsrc/common/triggers/misc/corner.qc index 38772a2955..dcc44710fc 100644 --- a/qcsrc/common/triggers/misc/corner.qc +++ b/qcsrc/common/triggers/misc/corner.qc @@ -1,3 +1,4 @@ +#include "corner.qh" REGISTER_NET_LINKED(ENT_CLIENT_CORNER) #ifdef SVQC diff --git a/qcsrc/common/triggers/misc/corner.qh b/qcsrc/common/triggers/misc/corner.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/misc/corner.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/misc/follow.qc b/qcsrc/common/triggers/misc/follow.qc index d19d9da085..63db2c18fa 100644 --- a/qcsrc/common/triggers/misc/follow.qc +++ b/qcsrc/common/triggers/misc/follow.qc @@ -1,3 +1,4 @@ +#include "follow.qh" // the way this entity works makes it no use to CSQC, as it removes itself instantly #ifdef SVQC diff --git a/qcsrc/common/triggers/misc/follow.qh b/qcsrc/common/triggers/misc/follow.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/misc/follow.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/misc/include.qc b/qcsrc/common/triggers/misc/include.qc index 8965c62906..bbe5ce03d6 100644 --- a/qcsrc/common/triggers/misc/include.qc +++ b/qcsrc/common/triggers/misc/include.qc @@ -1,3 +1,4 @@ +#include "include.qh" #include "corner.qc" #include "follow.qc" #include "laser.qc" diff --git a/qcsrc/common/triggers/misc/include.qh b/qcsrc/common/triggers/misc/include.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/misc/include.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/misc/laser.qc b/qcsrc/common/triggers/misc/laser.qc index d10ff11280..2059a8126d 100644 --- a/qcsrc/common/triggers/misc/laser.qc +++ b/qcsrc/common/triggers/misc/laser.qc @@ -1,3 +1,4 @@ +#include "laser.qh" #if defined(CSQC) #include #include diff --git a/qcsrc/common/triggers/misc/laser.qh b/qcsrc/common/triggers/misc/laser.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/misc/laser.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/misc/teleport_dest.qc b/qcsrc/common/triggers/misc/teleport_dest.qc index ab15a68919..fc3cec863a 100644 --- a/qcsrc/common/triggers/misc/teleport_dest.qc +++ b/qcsrc/common/triggers/misc/teleport_dest.qc @@ -1,3 +1,4 @@ +#include "teleport_dest.qh" REGISTER_NET_LINKED(ENT_CLIENT_TELEPORT_DEST) #ifdef SVQC diff --git a/qcsrc/common/triggers/misc/teleport_dest.qh b/qcsrc/common/triggers/misc/teleport_dest.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/misc/teleport_dest.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/platforms.qc b/qcsrc/common/triggers/platforms.qc index 7b9ffd9c50..10af3e1944 100644 --- a/qcsrc/common/triggers/platforms.qc +++ b/qcsrc/common/triggers/platforms.qc @@ -1,3 +1,4 @@ +#include "platforms.qh" void generic_plat_blocked(entity this, entity blocker) { #ifdef SVQC diff --git a/qcsrc/common/triggers/subs.qc b/qcsrc/common/triggers/subs.qc index 66f0253d95..67eb18a678 100644 --- a/qcsrc/common/triggers/subs.qc +++ b/qcsrc/common/triggers/subs.qc @@ -1,3 +1,4 @@ +#include "subs.qh" void SUB_NullThink(entity this) { } void SUB_CalcMoveDone(entity this); diff --git a/qcsrc/common/triggers/target/changelevel.qc b/qcsrc/common/triggers/target/changelevel.qc index d4bc850de1..6c006d42a9 100644 --- a/qcsrc/common/triggers/target/changelevel.qc +++ b/qcsrc/common/triggers/target/changelevel.qc @@ -1,3 +1,4 @@ +#include "changelevel.qh" #ifdef SVQC .string chmap, gametype; .entity chlevel_targ; diff --git a/qcsrc/common/triggers/target/changelevel.qh b/qcsrc/common/triggers/target/changelevel.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/target/changelevel.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/target/location.qc b/qcsrc/common/triggers/target/location.qc index 5a24d2c5ed..5259276e43 100644 --- a/qcsrc/common/triggers/target/location.qc +++ b/qcsrc/common/triggers/target/location.qc @@ -1,3 +1,4 @@ +#include "location.qh" #ifdef SVQC void target_push_init(entity this); diff --git a/qcsrc/common/triggers/target/location.qh b/qcsrc/common/triggers/target/location.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/target/location.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/target/music.qc b/qcsrc/common/triggers/target/music.qc index ff195f03f9..c8e3539283 100644 --- a/qcsrc/common/triggers/target/music.qc +++ b/qcsrc/common/triggers/target/music.qc @@ -1,3 +1,4 @@ +#include "music.qh" #if defined(CSQC) #elif defined(MENUQC) #elif defined(SVQC) diff --git a/qcsrc/common/triggers/target/spawn.qc b/qcsrc/common/triggers/target/spawn.qc index bc5271040c..4eed8ef345 100644 --- a/qcsrc/common/triggers/target/spawn.qc +++ b/qcsrc/common/triggers/target/spawn.qc @@ -1,3 +1,4 @@ +#include "spawn.qh" #if defined(CSQC) #elif defined(MENUQC) #elif defined(SVQC) diff --git a/qcsrc/common/triggers/target/spawn.qh b/qcsrc/common/triggers/target/spawn.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/target/spawn.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/target/speaker.qc b/qcsrc/common/triggers/target/speaker.qc index f8f1c0ace5..af327b443b 100644 --- a/qcsrc/common/triggers/target/speaker.qc +++ b/qcsrc/common/triggers/target/speaker.qc @@ -1,3 +1,4 @@ +#include "speaker.qh" #ifdef SVQC // TODO add a way to do looped sounds with sound(); then complete this entity void target_speaker_use_off(entity this, entity actor, entity trigger); diff --git a/qcsrc/common/triggers/target/speaker.qh b/qcsrc/common/triggers/target/speaker.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/target/speaker.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/target/voicescript.qc b/qcsrc/common/triggers/target/voicescript.qc index bdf1e0f1fb..fe7155cc54 100644 --- a/qcsrc/common/triggers/target/voicescript.qc +++ b/qcsrc/common/triggers/target/voicescript.qc @@ -1,3 +1,4 @@ +#include "voicescript.qh" #ifdef SVQC .entity voicescript; // attached voice script .float voicescript_index; // index of next voice, or -1 to use the randomized ones diff --git a/qcsrc/common/triggers/target/voicescript.qh b/qcsrc/common/triggers/target/voicescript.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/target/voicescript.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/counter.qc b/qcsrc/common/triggers/trigger/counter.qc index a4f850ba9a..8246aed7c3 100644 --- a/qcsrc/common/triggers/trigger/counter.qc +++ b/qcsrc/common/triggers/trigger/counter.qc @@ -1,3 +1,4 @@ +#include "counter.qh" #ifdef SVQC void counter_use(entity this, entity actor, entity trigger) { diff --git a/qcsrc/common/triggers/trigger/counter.qh b/qcsrc/common/triggers/trigger/counter.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/counter.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/delay.qc b/qcsrc/common/triggers/trigger/delay.qc index d6742fed32..c5049da393 100644 --- a/qcsrc/common/triggers/trigger/delay.qc +++ b/qcsrc/common/triggers/trigger/delay.qc @@ -1,3 +1,4 @@ +#include "delay.qh" #ifdef SVQC void delay_use(entity this, entity actor, entity trigger) { diff --git a/qcsrc/common/triggers/trigger/delay.qh b/qcsrc/common/triggers/trigger/delay.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/delay.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/disablerelay.qc b/qcsrc/common/triggers/trigger/disablerelay.qc index 1d30db0e08..6154f6bf0a 100644 --- a/qcsrc/common/triggers/trigger/disablerelay.qc +++ b/qcsrc/common/triggers/trigger/disablerelay.qc @@ -1,3 +1,4 @@ +#include "disablerelay.qh" #ifdef SVQC void trigger_disablerelay_use(entity this, entity actor, entity trigger) { diff --git a/qcsrc/common/triggers/trigger/disablerelay.qh b/qcsrc/common/triggers/trigger/disablerelay.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/disablerelay.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/flipflop.qc b/qcsrc/common/triggers/trigger/flipflop.qc index e4923bdf54..af212ff5a4 100644 --- a/qcsrc/common/triggers/trigger/flipflop.qc +++ b/qcsrc/common/triggers/trigger/flipflop.qc @@ -1,3 +1,4 @@ +#include "flipflop.qh" #ifdef SVQC /*QUAKED spawnfunc_trigger_flipflop (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ENABLED "Flip-flop" trigger gate... lets only every second trigger event through diff --git a/qcsrc/common/triggers/trigger/flipflop.qh b/qcsrc/common/triggers/trigger/flipflop.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/flipflop.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/gamestart.qc b/qcsrc/common/triggers/trigger/gamestart.qc index 62b21e66f4..72d76d1833 100644 --- a/qcsrc/common/triggers/trigger/gamestart.qc +++ b/qcsrc/common/triggers/trigger/gamestart.qc @@ -1,3 +1,4 @@ +#include "gamestart.qh" #ifdef SVQC void gamestart_use(entity this, entity actor, entity trigger) { diff --git a/qcsrc/common/triggers/trigger/gamestart.qh b/qcsrc/common/triggers/trigger/gamestart.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/gamestart.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/gravity.qc b/qcsrc/common/triggers/trigger/gravity.qc index 88b73c4dfc..3ea1562f08 100644 --- a/qcsrc/common/triggers/trigger/gravity.qc +++ b/qcsrc/common/triggers/trigger/gravity.qc @@ -1,3 +1,4 @@ +#include "gravity.qh" #ifdef SVQC .entity trigger_gravity_check; void trigger_gravity_remove(entity own) diff --git a/qcsrc/common/triggers/trigger/gravity.qh b/qcsrc/common/triggers/trigger/gravity.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/gravity.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/heal.qc b/qcsrc/common/triggers/trigger/heal.qc index 5a2bc78de8..e7b3090628 100644 --- a/qcsrc/common/triggers/trigger/heal.qc +++ b/qcsrc/common/triggers/trigger/heal.qc @@ -1,3 +1,4 @@ +#include "heal.qh" #ifdef SVQC .float triggerhealtime; void trigger_heal_touch(entity this, entity toucher) diff --git a/qcsrc/common/triggers/trigger/heal.qh b/qcsrc/common/triggers/trigger/heal.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/heal.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/hurt.qc b/qcsrc/common/triggers/trigger/hurt.qc index 4579fd7524..d0ba4ebd19 100644 --- a/qcsrc/common/triggers/trigger/hurt.qc +++ b/qcsrc/common/triggers/trigger/hurt.qc @@ -1,3 +1,4 @@ +#include "hurt.qh" #ifdef SVQC void trigger_hurt_use(entity this, entity actor, entity trigger) { diff --git a/qcsrc/common/triggers/trigger/hurt.qh b/qcsrc/common/triggers/trigger/hurt.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/hurt.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/impulse.qc b/qcsrc/common/triggers/trigger/impulse.qc index 43bac947e1..cb9c2d2935 100644 --- a/qcsrc/common/triggers/trigger/impulse.qc +++ b/qcsrc/common/triggers/trigger/impulse.qc @@ -1,3 +1,4 @@ +#include "impulse.qh" // targeted (directional) mode void trigger_impulse_touch1(entity this, entity toucher) { diff --git a/qcsrc/common/triggers/trigger/jumppads.qc b/qcsrc/common/triggers/trigger/jumppads.qc index 519ba26915..ef13dd5bec 100644 --- a/qcsrc/common/triggers/trigger/jumppads.qc +++ b/qcsrc/common/triggers/trigger/jumppads.qc @@ -1,3 +1,4 @@ +#include "jumppads.qh" // TODO: split target_push and put it in the target folder #ifdef SVQC #include "jumppads.qh" diff --git a/qcsrc/common/triggers/trigger/keylock.qc b/qcsrc/common/triggers/trigger/keylock.qc index eabb84f57a..bf20d1e973 100644 --- a/qcsrc/common/triggers/trigger/keylock.qc +++ b/qcsrc/common/triggers/trigger/keylock.qc @@ -1,3 +1,4 @@ +#include "keylock.qh" /** * trigger given targets */ diff --git a/qcsrc/common/triggers/trigger/magicear.qc b/qcsrc/common/triggers/trigger/magicear.qc index 53fda30e42..065d8c932a 100644 --- a/qcsrc/common/triggers/trigger/magicear.qc +++ b/qcsrc/common/triggers/trigger/magicear.qc @@ -1,3 +1,4 @@ +#include "magicear.qh" #ifdef SVQC float magicear_matched; float W_Tuba_HasPlayed(entity pl, string melody, float instrument, float ignorepitch, float mintempo, float maxtempo); diff --git a/qcsrc/common/triggers/trigger/magicear.qh b/qcsrc/common/triggers/trigger/magicear.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/magicear.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/monoflop.qc b/qcsrc/common/triggers/trigger/monoflop.qc index 018e20884b..a67baca16a 100644 --- a/qcsrc/common/triggers/trigger/monoflop.qc +++ b/qcsrc/common/triggers/trigger/monoflop.qc @@ -1,3 +1,4 @@ +#include "monoflop.qh" #ifdef SVQC /*QUAKED spawnfunc_trigger_monoflop (.5 .5 .5) (-8 -8 -8) (8 8 8) "Mono-flop" trigger gate... turns one trigger event into one "on" and one "off" event, separated by a delay of "wait" diff --git a/qcsrc/common/triggers/trigger/monoflop.qh b/qcsrc/common/triggers/trigger/monoflop.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/monoflop.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/multi.qc b/qcsrc/common/triggers/trigger/multi.qc index b801e9f4f3..7aa13c13eb 100644 --- a/qcsrc/common/triggers/trigger/multi.qc +++ b/qcsrc/common/triggers/trigger/multi.qc @@ -1,3 +1,4 @@ +#include "multi.qh" // NOTE: also contains trigger_once at bottom #ifdef SVQC diff --git a/qcsrc/common/triggers/trigger/multivibrator.qc b/qcsrc/common/triggers/trigger/multivibrator.qc index 1a1850537d..d946efe5f1 100644 --- a/qcsrc/common/triggers/trigger/multivibrator.qc +++ b/qcsrc/common/triggers/trigger/multivibrator.qc @@ -1,3 +1,4 @@ +#include "multivibrator.qh" #ifdef SVQC void multivibrator_send(entity this) { diff --git a/qcsrc/common/triggers/trigger/multivibrator.qh b/qcsrc/common/triggers/trigger/multivibrator.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/multivibrator.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/relay.qc b/qcsrc/common/triggers/trigger/relay.qc index 1df446ba32..794f4dc112 100644 --- a/qcsrc/common/triggers/trigger/relay.qc +++ b/qcsrc/common/triggers/trigger/relay.qc @@ -1,3 +1,4 @@ +#include "relay.qh" #ifdef SVQC /*QUAKED spawnfunc_trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8) This fixed size trigger cannot be touched, it can only be fired by other events. It can contain killtargets, targets, delays, and messages. diff --git a/qcsrc/common/triggers/trigger/relay.qh b/qcsrc/common/triggers/trigger/relay.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/relay.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/relay_activators.qc b/qcsrc/common/triggers/trigger/relay_activators.qc index bbb49d0e5c..d713a05837 100644 --- a/qcsrc/common/triggers/trigger/relay_activators.qc +++ b/qcsrc/common/triggers/trigger/relay_activators.qc @@ -1,3 +1,4 @@ +#include "relay_activators.qh" #ifdef SVQC void relay_activators_use(entity this, entity actor, entity trigger) { diff --git a/qcsrc/common/triggers/trigger/relay_activators.qh b/qcsrc/common/triggers/trigger/relay_activators.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/relay_activators.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/relay_if.qc b/qcsrc/common/triggers/trigger/relay_if.qc index ea90a06cb7..728252c704 100644 --- a/qcsrc/common/triggers/trigger/relay_if.qc +++ b/qcsrc/common/triggers/trigger/relay_if.qc @@ -1,3 +1,4 @@ +#include "relay_if.qh" #ifdef SVQC void trigger_relay_if_use(entity this, entity actor, entity trigger) { diff --git a/qcsrc/common/triggers/trigger/relay_if.qh b/qcsrc/common/triggers/trigger/relay_if.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/relay_if.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/relay_teamcheck.qc b/qcsrc/common/triggers/trigger/relay_teamcheck.qc index 2972d32bbf..4f9dab7fd1 100644 --- a/qcsrc/common/triggers/trigger/relay_teamcheck.qc +++ b/qcsrc/common/triggers/trigger/relay_teamcheck.qc @@ -1,3 +1,4 @@ +#include "relay_teamcheck.qh" #ifdef SVQC void trigger_relay_teamcheck_use(entity this, entity actor, entity trigger) { diff --git a/qcsrc/common/triggers/trigger/relay_teamcheck.qh b/qcsrc/common/triggers/trigger/relay_teamcheck.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/relay_teamcheck.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/secret.qc b/qcsrc/common/triggers/trigger/secret.qc index 9260c01ac2..e6e35c295a 100644 --- a/qcsrc/common/triggers/trigger/secret.qc +++ b/qcsrc/common/triggers/trigger/secret.qc @@ -1,9 +1,9 @@ +#include "secret.qh" #if defined(CSQC) #elif defined(MENUQC) #elif defined(SVQC) #include #include - #include "secret.qh" #endif #ifdef SVQC diff --git a/qcsrc/common/triggers/trigger/swamp.qc b/qcsrc/common/triggers/trigger/swamp.qc index 99eb846c73..71c5247c74 100644 --- a/qcsrc/common/triggers/trigger/swamp.qc +++ b/qcsrc/common/triggers/trigger/swamp.qc @@ -1,8 +1,9 @@ +#include "swamp.qh" #if defined(CSQC) #elif defined(MENUQC) #elif defined(SVQC) #include - #include + #include #include #include #endif diff --git a/qcsrc/common/triggers/trigger/teleport.qc b/qcsrc/common/triggers/trigger/teleport.qc index 69e2c49c1d..05bb13c710 100644 --- a/qcsrc/common/triggers/trigger/teleport.qc +++ b/qcsrc/common/triggers/trigger/teleport.qc @@ -1,3 +1,4 @@ +#include "teleport.qh" REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_TELEPORT) #ifdef SVQC diff --git a/qcsrc/common/triggers/trigger/teleport.qh b/qcsrc/common/triggers/trigger/teleport.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/teleport.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/viewloc.qc b/qcsrc/common/triggers/trigger/viewloc.qc index f2a0e86cc3..d53ac5dca6 100644 --- a/qcsrc/common/triggers/trigger/viewloc.qc +++ b/qcsrc/common/triggers/trigger/viewloc.qc @@ -1,3 +1,4 @@ +#include "viewloc.qh" #if defined(CSQC) #elif defined(MENUQC) #elif defined(SVQC) diff --git a/qcsrc/common/triggers/triggers.qc b/qcsrc/common/triggers/triggers.qc index 849d3e8acb..54e1e09919 100644 --- a/qcsrc/common/triggers/triggers.qc +++ b/qcsrc/common/triggers/triggers.qc @@ -1,3 +1,4 @@ +#include "triggers.qh" void SUB_DontUseTargets(entity this, entity actor, entity trigger) { } void SUB_UseTargets(entity this, entity actor, entity trigger); @@ -163,6 +164,7 @@ void trigger_remove_generic(entity this) } #endif + /* ============================== SUB_UseTargets @@ -182,7 +184,8 @@ match (string)this.target and call their .use function ============================== */ -void SUB_UseTargets(entity this, entity actor, entity trigger) + +void SUB_UseTargets_Ex(entity this, entity actor, entity trigger, bool preventReuse) { // // check for a delay @@ -253,7 +256,7 @@ void SUB_UseTargets(entity this, entity actor, entity trigger) int aw_flag = this.antiwall_flag; for(entity t = NULL; (t = find(t, targetname, s)); ) { - if(t.use) + if(t.use && (t.sub_target_used != time || !preventReuse)) { if(this.target_random) { @@ -265,6 +268,8 @@ void SUB_UseTargets(entity this, entity actor, entity trigger) t.antiwall_flag = aw_flag; t.use(t, actor, this); + if(preventReuse) + t.sub_target_used = time; } } } @@ -272,9 +277,16 @@ void SUB_UseTargets(entity this, entity actor, entity trigger) } if(this.target_random && RandomSelection_chosen_ent) + { RandomSelection_chosen_ent.use(RandomSelection_chosen_ent, actor, this); + if(preventReuse) + RandomSelection_chosen_ent.sub_target_used = time; + } } +void SUB_UseTargets(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, false); } +void SUB_UseTargets_PreventReuse(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, true); } + void SUB_UseTargets_self(entity this) { SUB_UseTargets(this, NULL, NULL); diff --git a/qcsrc/common/triggers/triggers.qh b/qcsrc/common/triggers/triggers.qh index 8a8eb566c1..2b8274f4b8 100644 --- a/qcsrc/common/triggers/triggers.qh +++ b/qcsrc/common/triggers/triggers.qh @@ -27,8 +27,12 @@ string trigger_magicear_processmessage_forallears(entity source, float teamsay, void target_voicescript_next(entity pl); void target_voicescript_clear(entity pl); + +void SUB_UseTargets_PreventReuse(entity this, entity actor, entity trigger); #endif +.float sub_target_used; + .float volume, atten; .vector dest; diff --git a/qcsrc/common/turrets/_all.inc b/qcsrc/common/turrets/_all.inc new file mode 100644 index 0000000000..8bc63f720a --- /dev/null +++ b/qcsrc/common/turrets/_all.inc @@ -0,0 +1,2 @@ +#include "_all.qh" +#include "_mod.inc" diff --git a/qcsrc/common/turrets/_all.qh b/qcsrc/common/turrets/_all.qh new file mode 100644 index 0000000000..947026dd59 --- /dev/null +++ b/qcsrc/common/turrets/_all.qh @@ -0,0 +1,2 @@ +#pragma once +#include "_mod.qh" diff --git a/qcsrc/common/turrets/_mod.inc b/qcsrc/common/turrets/_mod.inc index 40c3114bab..8fff9535c5 100644 --- a/qcsrc/common/turrets/_mod.inc +++ b/qcsrc/common/turrets/_mod.inc @@ -1,8 +1,13 @@ // generated file; do not modify #include #include -#include #include -#include #include +#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif #include diff --git a/qcsrc/common/turrets/_mod.qh b/qcsrc/common/turrets/_mod.qh index 6da539e852..06978f1d41 100644 --- a/qcsrc/common/turrets/_mod.qh +++ b/qcsrc/common/turrets/_mod.qh @@ -1,8 +1,13 @@ // generated file; do not modify #include #include -#include #include -#include #include +#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif #include diff --git a/qcsrc/common/turrets/all.qh b/qcsrc/common/turrets/all.qh index 476da2d18e..1a77e98912 100644 --- a/qcsrc/common/turrets/all.qh +++ b/qcsrc/common/turrets/all.qh @@ -1,6 +1,6 @@ #pragma once -#include +#include #include "config.qh" #include "turret.qh" diff --git a/qcsrc/common/turrets/checkpoint.qc b/qcsrc/common/turrets/checkpoint.qc index fb56d3ecc9..6c246a75b8 100644 --- a/qcsrc/common/turrets/checkpoint.qc +++ b/qcsrc/common/turrets/checkpoint.qc @@ -1,3 +1,4 @@ +#include "checkpoint.qh" /** turret_checkpoint **/ diff --git a/qcsrc/common/turrets/checkpoint.qh b/qcsrc/common/turrets/checkpoint.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/turrets/checkpoint.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/turrets/cl_turrets.qc b/qcsrc/common/turrets/cl_turrets.qc index 29658b56b4..a893b3e8a7 100644 --- a/qcsrc/common/turrets/cl_turrets.qc +++ b/qcsrc/common/turrets/cl_turrets.qc @@ -1,3 +1,4 @@ +#include "cl_turrets.qh" void turret_remove(entity this) { delete(this.tur_head); diff --git a/qcsrc/common/turrets/cl_turrets.qh b/qcsrc/common/turrets/cl_turrets.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/turrets/cl_turrets.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/turrets/config.qc b/qcsrc/common/turrets/config.qc index f5989b48d1..2b1e00c8a7 100644 --- a/qcsrc/common/turrets/config.qc +++ b/qcsrc/common/turrets/config.qc @@ -1,3 +1,4 @@ +#include "config.qh" // ========================== // Turret Config Generator // ========================== diff --git a/qcsrc/common/turrets/sv_turrets.qc b/qcsrc/common/turrets/sv_turrets.qc index a387fefc47..dabb7ee35d 100644 --- a/qcsrc/common/turrets/sv_turrets.qc +++ b/qcsrc/common/turrets/sv_turrets.qc @@ -1,3 +1,4 @@ +#include "sv_turrets.qh" #ifdef SVQC #include diff --git a/qcsrc/common/turrets/targettrigger.qc b/qcsrc/common/turrets/targettrigger.qc index ca94f2b4e1..f2a97d4a12 100644 --- a/qcsrc/common/turrets/targettrigger.qc +++ b/qcsrc/common/turrets/targettrigger.qc @@ -1,3 +1,4 @@ +#include "targettrigger.qh" spawnfunc(turret_targettrigger); void turret_targettrigger_touch(entity this, entity toucher); diff --git a/qcsrc/common/turrets/targettrigger.qh b/qcsrc/common/turrets/targettrigger.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/turrets/targettrigger.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/turrets/turret.qh b/qcsrc/common/turrets/turret.qh index 649fd51ae6..8e52397028 100644 --- a/qcsrc/common/turrets/turret.qh +++ b/qcsrc/common/turrets/turret.qh @@ -1,6 +1,6 @@ #pragma once -#include +#include CLASS(Turret, Object) ATTRIB(Turret, m_id, int, 0); diff --git a/qcsrc/common/turrets/turret/ewheel.qc b/qcsrc/common/turrets/turret/ewheel.qc index a86acdb274..b5716f7a98 100644 --- a/qcsrc/common/turrets/turret/ewheel.qc +++ b/qcsrc/common/turrets/turret/ewheel.qc @@ -1,24 +1,4 @@ -#ifndef TURRET_EWHEEL_H -#define TURRET_EWHEEL_H - -//#define EWHEEL_FANCYPATH - -#include "ewheel_weapon.qh" - -CLASS(EWheel, Turret) -/* spawnflags */ ATTRIB(EWheel, spawnflags, int, TUR_FLAG_PLAYER | TUR_FLAG_MOVE | TUR_FLAG_ROAM); -/* mins */ ATTRIB(EWheel, mins, vector, '-32 -32 0'); -/* maxs */ ATTRIB(EWheel, maxs, vector, '32 32 48'); -/* modelname */ ATTRIB(EWheel, mdl, string, "ewheel-base2.md3"); -/* model */ ATTRIB_STRZONE(EWheel, model, string, strcat("models/turrets/", this.mdl)); -/* head_model */ ATTRIB_STRZONE(EWheel, head_model, string, strcat("models/turrets/", "ewheel-gun1.md3")); -/* netname */ ATTRIB(EWheel, netname, string, "ewheel"); -/* fullname */ ATTRIB(EWheel, turret_name, string, _("eWheel Turret")); - ATTRIB(EWheel, m_weapon, Weapon, WEP_EWHEEL); -ENDCLASS(EWheel) -REGISTER_TURRET(EWHEEL, NEW(EWheel)); - -#endif +#include "ewheel.qh" #ifdef IMPLEMENTATION diff --git a/qcsrc/common/turrets/turret/ewheel.qh b/qcsrc/common/turrets/turret/ewheel.qh new file mode 100644 index 0000000000..b34adb2fe3 --- /dev/null +++ b/qcsrc/common/turrets/turret/ewheel.qh @@ -0,0 +1,18 @@ +#pragma once + +//#define EWHEEL_FANCYPATH + +#include "ewheel_weapon.qh" + +CLASS(EWheel, Turret) +/* spawnflags */ ATTRIB(EWheel, spawnflags, int, TUR_FLAG_PLAYER | TUR_FLAG_MOVE | TUR_FLAG_ROAM); +/* mins */ ATTRIB(EWheel, mins, vector, '-32 -32 0'); +/* maxs */ ATTRIB(EWheel, maxs, vector, '32 32 48'); +/* modelname */ ATTRIB(EWheel, mdl, string, "ewheel-base2.md3"); +/* model */ ATTRIB_STRZONE(EWheel, model, string, strcat("models/turrets/", this.mdl)); +/* head_model */ ATTRIB_STRZONE(EWheel, head_model, string, strcat("models/turrets/", "ewheel-gun1.md3")); +/* netname */ ATTRIB(EWheel, netname, string, "ewheel"); +/* fullname */ ATTRIB(EWheel, turret_name, string, _("eWheel Turret")); + ATTRIB(EWheel, m_weapon, Weapon, WEP_EWHEEL); +ENDCLASS(EWheel) +REGISTER_TURRET(EWHEEL, NEW(EWheel)); diff --git a/qcsrc/common/turrets/turret/flac.qc b/qcsrc/common/turrets/turret/flac.qc index 20eeb77595..ab6e5f5cb6 100644 --- a/qcsrc/common/turrets/turret/flac.qc +++ b/qcsrc/common/turrets/turret/flac.qc @@ -1,22 +1,4 @@ -#ifndef TURRET_FLAC_H -#define TURRET_FLAC_H - -#include "flac_weapon.qh" - -CLASS(Flac, Turret) -/* spawnflags */ ATTRIB(Flac, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_FASTPROJ | TUR_FLAG_MISSILE); -/* mins */ ATTRIB(Flac, mins, vector, '-32 -32 0'); -/* maxs */ ATTRIB(Flac, maxs, vector, '32 32 64'); -/* modelname */ ATTRIB(Flac, mdl, string, "base.md3"); -/* model */ ATTRIB_STRZONE(Flac, model, string, strcat("models/turrets/", this.mdl)); -/* head_model */ ATTRIB_STRZONE(Flac, head_model, string, strcat("models/turrets/", "flac.md3")); -/* netname */ ATTRIB(Flac, netname, string, "flac"); -/* fullname */ ATTRIB(Flac, turret_name, string, _("FLAC Cannon")); - ATTRIB(Flac, m_weapon, Weapon, WEP_FLAC); -ENDCLASS(Flac) -REGISTER_TURRET(FLAC, NEW(Flac)); - -#endif +#include "flac.qh" #ifdef IMPLEMENTATION diff --git a/qcsrc/common/turrets/turret/flac.qh b/qcsrc/common/turrets/turret/flac.qh new file mode 100644 index 0000000000..d53422c602 --- /dev/null +++ b/qcsrc/common/turrets/turret/flac.qh @@ -0,0 +1,16 @@ +#pragma once + +#include "flac_weapon.qh" + +CLASS(Flac, Turret) +/* spawnflags */ ATTRIB(Flac, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_FASTPROJ | TUR_FLAG_MISSILE); +/* mins */ ATTRIB(Flac, mins, vector, '-32 -32 0'); +/* maxs */ ATTRIB(Flac, maxs, vector, '32 32 64'); +/* modelname */ ATTRIB(Flac, mdl, string, "base.md3"); +/* model */ ATTRIB_STRZONE(Flac, model, string, strcat("models/turrets/", this.mdl)); +/* head_model */ ATTRIB_STRZONE(Flac, head_model, string, strcat("models/turrets/", "flac.md3")); +/* netname */ ATTRIB(Flac, netname, string, "flac"); +/* fullname */ ATTRIB(Flac, turret_name, string, _("FLAC Cannon")); + ATTRIB(Flac, m_weapon, Weapon, WEP_FLAC); +ENDCLASS(Flac) +REGISTER_TURRET(FLAC, NEW(Flac)); diff --git a/qcsrc/common/turrets/turret/fusionreactor.qc b/qcsrc/common/turrets/turret/fusionreactor.qc index cd7dbec72d..163bffb9d7 100644 --- a/qcsrc/common/turrets/turret/fusionreactor.qc +++ b/qcsrc/common/turrets/turret/fusionreactor.qc @@ -1,19 +1,4 @@ -#ifndef TURRET_FUSIONREACTOR_H -#define TURRET_FUSIONREACTOR_H - -CLASS(FusionReactor, Turret) -/* spawnflags */ ATTRIB(FusionReactor, spawnflags, int, TUR_FLAG_SUPPORT | TUR_FLAG_AMMOSOURCE); -/* mins */ ATTRIB(FusionReactor, mins, vector, '-34 -34 0'); -/* maxs */ ATTRIB(FusionReactor, maxs, vector, '34 34 90'); -/* modelname */ ATTRIB(FusionReactor, mdl, string, "base.md3"); -/* model */ ATTRIB_STRZONE(FusionReactor, model, string, strcat("models/turrets/", this.mdl)); -/* head_model */ ATTRIB_STRZONE(FusionReactor, head_model, string, strcat("models/turrets/", "reactor.md3")); -/* netname */ ATTRIB(FusionReactor, netname, string, "fusionreactor"); -/* fullname */ ATTRIB(FusionReactor, turret_name, string, _("Fusion Reactor")); -ENDCLASS(FusionReactor) -REGISTER_TURRET(FUSIONREACTOR, NEW(FusionReactor)); - -#endif +#include "fusionreactor.qh" #ifdef IMPLEMENTATION #ifdef SVQC diff --git a/qcsrc/common/turrets/turret/fusionreactor.qh b/qcsrc/common/turrets/turret/fusionreactor.qh new file mode 100644 index 0000000000..134b805e9f --- /dev/null +++ b/qcsrc/common/turrets/turret/fusionreactor.qh @@ -0,0 +1,13 @@ +#pragma once + +CLASS(FusionReactor, Turret) +/* spawnflags */ ATTRIB(FusionReactor, spawnflags, int, TUR_FLAG_SUPPORT | TUR_FLAG_AMMOSOURCE); +/* mins */ ATTRIB(FusionReactor, mins, vector, '-34 -34 0'); +/* maxs */ ATTRIB(FusionReactor, maxs, vector, '34 34 90'); +/* modelname */ ATTRIB(FusionReactor, mdl, string, "base.md3"); +/* model */ ATTRIB_STRZONE(FusionReactor, model, string, strcat("models/turrets/", this.mdl)); +/* head_model */ ATTRIB_STRZONE(FusionReactor, head_model, string, strcat("models/turrets/", "reactor.md3")); +/* netname */ ATTRIB(FusionReactor, netname, string, "fusreac"); +/* fullname */ ATTRIB(FusionReactor, turret_name, string, _("Fusion Reactor")); +ENDCLASS(FusionReactor) +REGISTER_TURRET(FUSIONREACTOR, NEW(FusionReactor)); diff --git a/qcsrc/common/turrets/turret/hellion.qc b/qcsrc/common/turrets/turret/hellion.qc index 61203ddf75..88a0170ea6 100644 --- a/qcsrc/common/turrets/turret/hellion.qc +++ b/qcsrc/common/turrets/turret/hellion.qc @@ -1,22 +1,4 @@ -#ifndef TURRET_HELLION_H -#define TURRET_HELLION_H - -#include "hellion_weapon.qh" - -CLASS(Hellion, Turret) -/* spawnflags */ ATTRIB(Hellion, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_FASTPROJ | TUR_FLAG_PLAYER | TUR_FLAG_MISSILE); -/* mins */ ATTRIB(Hellion, mins, vector, '-32 -32 0'); -/* maxs */ ATTRIB(Hellion, maxs, vector, '32 32 64'); -/* modelname */ ATTRIB(Hellion, mdl, string, "base.md3"); -/* model */ ATTRIB_STRZONE(Hellion, model, string, strcat("models/turrets/", this.mdl)); -/* head_model */ ATTRIB_STRZONE(Hellion, head_model, string, strcat("models/turrets/", "hellion.md3")); -/* netname */ ATTRIB(Hellion, netname, string, "hellion"); -/* fullname */ ATTRIB(Hellion, turret_name, string, _("Hellion Missile Turret")); - ATTRIB(Hellion, m_weapon, Weapon, WEP_HELLION); -ENDCLASS(Hellion) -REGISTER_TURRET(HELLION, NEW(Hellion)); - -#endif +#include "hellion.qh" #ifdef IMPLEMENTATION diff --git a/qcsrc/common/turrets/turret/hellion.qh b/qcsrc/common/turrets/turret/hellion.qh new file mode 100644 index 0000000000..642645b85f --- /dev/null +++ b/qcsrc/common/turrets/turret/hellion.qh @@ -0,0 +1,16 @@ +#pragma once + +#include "hellion_weapon.qh" + +CLASS(Hellion, Turret) +/* spawnflags */ ATTRIB(Hellion, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_FASTPROJ | TUR_FLAG_PLAYER | TUR_FLAG_MISSILE); +/* mins */ ATTRIB(Hellion, mins, vector, '-32 -32 0'); +/* maxs */ ATTRIB(Hellion, maxs, vector, '32 32 64'); +/* modelname */ ATTRIB(Hellion, mdl, string, "base.md3"); +/* model */ ATTRIB_STRZONE(Hellion, model, string, strcat("models/turrets/", this.mdl)); +/* head_model */ ATTRIB_STRZONE(Hellion, head_model, string, strcat("models/turrets/", "hellion.md3")); +/* netname */ ATTRIB(Hellion, netname, string, "hellion"); +/* fullname */ ATTRIB(Hellion, turret_name, string, _("Hellion Missile Turret")); + ATTRIB(Hellion, m_weapon, Weapon, WEP_HELLION); +ENDCLASS(Hellion) +REGISTER_TURRET(HELLION, NEW(Hellion)); diff --git a/qcsrc/common/turrets/turret/hk.qc b/qcsrc/common/turrets/turret/hk.qc index 9cb60df4c6..255821e1c3 100644 --- a/qcsrc/common/turrets/turret/hk.qc +++ b/qcsrc/common/turrets/turret/hk.qc @@ -1,24 +1,4 @@ -#ifndef TURRET_HK_H -#define TURRET_HK_H - -//#define TURRET_DEBUG_HK - -#include "hk_weapon.qh" - -CLASS(HunterKiller, Turret) -/* spawnflags */ ATTRIB(HunterKiller, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER | TUR_FLAG_RECIEVETARGETS); -/* mins */ ATTRIB(HunterKiller, mins, vector, '-32 -32 0'); -/* maxs */ ATTRIB(HunterKiller, maxs, vector, '32 32 64'); -/* modelname */ ATTRIB(HunterKiller, mdl, string, "base.md3"); -/* model */ ATTRIB_STRZONE(HunterKiller, model, string, strcat("models/turrets/", this.mdl)); -/* head_model */ ATTRIB_STRZONE(HunterKiller, head_model, string, strcat("models/turrets/", "hk.md3")); -/* netname */ ATTRIB(HunterKiller, netname, string, "hk"); -/* fullname */ ATTRIB(HunterKiller, turret_name, string, _("Hunter-Killer Turret")); - ATTRIB(HunterKiller, m_weapon, Weapon, WEP_HK); -ENDCLASS(HunterKiller) -REGISTER_TURRET(HK, NEW(HunterKiller)); - -#endif +#include "hk.qh" #ifdef IMPLEMENTATION diff --git a/qcsrc/common/turrets/turret/hk.qh b/qcsrc/common/turrets/turret/hk.qh new file mode 100644 index 0000000000..d7c9cfbbe8 --- /dev/null +++ b/qcsrc/common/turrets/turret/hk.qh @@ -0,0 +1,18 @@ +#pragma once + +//#define TURRET_DEBUG_HK + +#include "hk_weapon.qh" + +CLASS(HunterKiller, Turret) +/* spawnflags */ ATTRIB(HunterKiller, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER | TUR_FLAG_RECIEVETARGETS); +/* mins */ ATTRIB(HunterKiller, mins, vector, '-32 -32 0'); +/* maxs */ ATTRIB(HunterKiller, maxs, vector, '32 32 64'); +/* modelname */ ATTRIB(HunterKiller, mdl, string, "base.md3"); +/* model */ ATTRIB_STRZONE(HunterKiller, model, string, strcat("models/turrets/", this.mdl)); +/* head_model */ ATTRIB_STRZONE(HunterKiller, head_model, string, strcat("models/turrets/", "hk.md3")); +/* netname */ ATTRIB(HunterKiller, netname, string, "hk"); +/* fullname */ ATTRIB(HunterKiller, turret_name, string, _("Hunter-Killer Turret")); + ATTRIB(HunterKiller, m_weapon, Weapon, WEP_HK); +ENDCLASS(HunterKiller) +REGISTER_TURRET(HK, NEW(HunterKiller)); diff --git a/qcsrc/common/turrets/turret/machinegun.qc b/qcsrc/common/turrets/turret/machinegun.qc index 8addd95428..db3cb47bf2 100644 --- a/qcsrc/common/turrets/turret/machinegun.qc +++ b/qcsrc/common/turrets/turret/machinegun.qc @@ -1,22 +1,4 @@ -#ifndef TURRET_MACHINEGUN_H -#define TURRET_MACHINEGUN_H - -#include "machinegun_weapon.qh" - -CLASS(MachineGunTurret, Turret) -/* spawnflags */ ATTRIB(MachineGunTurret, spawnflags, int, TUR_FLAG_PLAYER); -/* mins */ ATTRIB(MachineGunTurret, mins, vector, '-32 -32 0'); -/* maxs */ ATTRIB(MachineGunTurret, maxs, vector, '32 32 64'); -/* modelname */ ATTRIB(MachineGunTurret, mdl, string, "base.md3"); -/* model */ ATTRIB_STRZONE(MachineGunTurret, model, string, strcat("models/turrets/", this.mdl)); -/* head_model */ ATTRIB_STRZONE(MachineGunTurret, head_model, string, strcat("models/turrets/", "machinegun.md3")); -/* netname */ ATTRIB(MachineGunTurret, netname, string, "machinegun"); -/* fullname */ ATTRIB(MachineGunTurret, turret_name, string, _("Machinegun Turret")); - ATTRIB(MachineGunTurret, m_weapon, Weapon, WEP_TUR_MACHINEGUN); -ENDCLASS(MachineGunTurret) -REGISTER_TURRET(MACHINEGUN, NEW(MachineGunTurret)); - -#endif +#include "machinegun.qh" #ifdef IMPLEMENTATION diff --git a/qcsrc/common/turrets/turret/machinegun.qh b/qcsrc/common/turrets/turret/machinegun.qh new file mode 100644 index 0000000000..92a8fbaa4e --- /dev/null +++ b/qcsrc/common/turrets/turret/machinegun.qh @@ -0,0 +1,16 @@ +#pragma once + +#include "machinegun_weapon.qh" + +CLASS(MachineGunTurret, Turret) +/* spawnflags */ ATTRIB(MachineGunTurret, spawnflags, int, TUR_FLAG_PLAYER); +/* mins */ ATTRIB(MachineGunTurret, mins, vector, '-32 -32 0'); +/* maxs */ ATTRIB(MachineGunTurret, maxs, vector, '32 32 64'); +/* modelname */ ATTRIB(MachineGunTurret, mdl, string, "base.md3"); +/* model */ ATTRIB_STRZONE(MachineGunTurret, model, string, strcat("models/turrets/", this.mdl)); +/* head_model */ ATTRIB_STRZONE(MachineGunTurret, head_model, string, strcat("models/turrets/", "machinegun.md3")); +/* netname */ ATTRIB(MachineGunTurret, netname, string, "machinegun"); +/* fullname */ ATTRIB(MachineGunTurret, turret_name, string, _("Machinegun Turret")); + ATTRIB(MachineGunTurret, m_weapon, Weapon, WEP_TUR_MACHINEGUN); +ENDCLASS(MachineGunTurret) +REGISTER_TURRET(MACHINEGUN, NEW(MachineGunTurret)); diff --git a/qcsrc/common/turrets/turret/mlrs.qc b/qcsrc/common/turrets/turret/mlrs.qc index 316d3b9a05..472a0cb09a 100644 --- a/qcsrc/common/turrets/turret/mlrs.qc +++ b/qcsrc/common/turrets/turret/mlrs.qc @@ -1,22 +1,4 @@ -#ifndef TURRET_MLRS_H -#define TURRET_MLRS_H - -#include "mlrs_weapon.qh" - -CLASS(MLRSTurret, Turret) -/* spawnflags */ ATTRIB(MLRSTurret, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER); -/* mins */ ATTRIB(MLRSTurret, mins, vector, '-32 -32 0'); -/* maxs */ ATTRIB(MLRSTurret, maxs, vector, '32 32 64'); -/* modelname */ ATTRIB(MLRSTurret, mdl, string, "base.md3"); -/* model */ ATTRIB_STRZONE(MLRSTurret, model, string, strcat("models/turrets/", this.mdl)); -/* head_model */ ATTRIB_STRZONE(MLRSTurret, head_model, string, strcat("models/turrets/", "mlrs.md3")); -/* netname */ ATTRIB(MLRSTurret, netname, string, "mlrs"); -/* fullname */ ATTRIB(MLRSTurret, turret_name, string, _("MLRS Turret")); - ATTRIB(MLRSTurret, m_weapon, Weapon, WEP_TUR_MLRS); -ENDCLASS(MLRSTurret) -REGISTER_TURRET(MLRS, NEW(MLRSTurret)); - -#endif +#include "mlrs.qh" #ifdef IMPLEMENTATION diff --git a/qcsrc/common/turrets/turret/mlrs.qh b/qcsrc/common/turrets/turret/mlrs.qh new file mode 100644 index 0000000000..b2a6a5c43d --- /dev/null +++ b/qcsrc/common/turrets/turret/mlrs.qh @@ -0,0 +1,16 @@ +#pragma once + +#include "mlrs_weapon.qh" + +CLASS(MLRSTurret, Turret) +/* spawnflags */ ATTRIB(MLRSTurret, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER); +/* mins */ ATTRIB(MLRSTurret, mins, vector, '-32 -32 0'); +/* maxs */ ATTRIB(MLRSTurret, maxs, vector, '32 32 64'); +/* modelname */ ATTRIB(MLRSTurret, mdl, string, "base.md3"); +/* model */ ATTRIB_STRZONE(MLRSTurret, model, string, strcat("models/turrets/", this.mdl)); +/* head_model */ ATTRIB_STRZONE(MLRSTurret, head_model, string, strcat("models/turrets/", "mlrs.md3")); +/* netname */ ATTRIB(MLRSTurret, netname, string, "mlrs"); +/* fullname */ ATTRIB(MLRSTurret, turret_name, string, _("MLRS Turret")); + ATTRIB(MLRSTurret, m_weapon, Weapon, WEP_TUR_MLRS); +ENDCLASS(MLRSTurret) +REGISTER_TURRET(MLRS, NEW(MLRSTurret)); diff --git a/qcsrc/common/turrets/turret/phaser.qc b/qcsrc/common/turrets/turret/phaser.qc index 7c5d336212..31ece9cb2b 100644 --- a/qcsrc/common/turrets/turret/phaser.qc +++ b/qcsrc/common/turrets/turret/phaser.qc @@ -1,22 +1,4 @@ -#ifndef TURRET_PHASER_H -#define TURRET_PHASER_H - -#include "phaser_weapon.qh" - -CLASS(PhaserTurret, Turret) -/* spawnflags */ ATTRIB(PhaserTurret, spawnflags, int, TUR_FLAG_SNIPER | TUR_FLAG_HITSCAN | TUR_FLAG_PLAYER); -/* mins */ ATTRIB(PhaserTurret, mins, vector, '-32 -32 0'); -/* maxs */ ATTRIB(PhaserTurret, maxs, vector, '32 32 64'); -/* modelname */ ATTRIB(PhaserTurret, mdl, string, "base.md3"); -/* model */ ATTRIB_STRZONE(PhaserTurret, model, string, strcat("models/turrets/", this.mdl)); -/* head_model */ ATTRIB_STRZONE(PhaserTurret, head_model, string, strcat("models/turrets/", "phaser.md3")); -/* netname */ ATTRIB(PhaserTurret, netname, string, "phaser"); -/* fullname */ ATTRIB(PhaserTurret, turret_name, string, _("Phaser Cannon")); - ATTRIB(PhaserTurret, m_weapon, Weapon, WEP_PHASER); -ENDCLASS(PhaserTurret) -REGISTER_TURRET(PHASER, NEW(PhaserTurret)); - -#endif +#include "phaser.qh" #ifdef IMPLEMENTATION diff --git a/qcsrc/common/turrets/turret/phaser.qh b/qcsrc/common/turrets/turret/phaser.qh new file mode 100644 index 0000000000..fedbe66cba --- /dev/null +++ b/qcsrc/common/turrets/turret/phaser.qh @@ -0,0 +1,16 @@ +#pragma once + +#include "phaser_weapon.qh" + +CLASS(PhaserTurret, Turret) +/* spawnflags */ ATTRIB(PhaserTurret, spawnflags, int, TUR_FLAG_SNIPER | TUR_FLAG_HITSCAN | TUR_FLAG_PLAYER); +/* mins */ ATTRIB(PhaserTurret, mins, vector, '-32 -32 0'); +/* maxs */ ATTRIB(PhaserTurret, maxs, vector, '32 32 64'); +/* modelname */ ATTRIB(PhaserTurret, mdl, string, "base.md3"); +/* model */ ATTRIB_STRZONE(PhaserTurret, model, string, strcat("models/turrets/", this.mdl)); +/* head_model */ ATTRIB_STRZONE(PhaserTurret, head_model, string, strcat("models/turrets/", "phaser.md3")); +/* netname */ ATTRIB(PhaserTurret, netname, string, "phaser"); +/* fullname */ ATTRIB(PhaserTurret, turret_name, string, _("Phaser Cannon")); + ATTRIB(PhaserTurret, m_weapon, Weapon, WEP_PHASER); +ENDCLASS(PhaserTurret) +REGISTER_TURRET(PHASER, NEW(PhaserTurret)); diff --git a/qcsrc/common/turrets/turret/plasma.qc b/qcsrc/common/turrets/turret/plasma.qc index 82aa1abe67..d161436ab0 100644 --- a/qcsrc/common/turrets/turret/plasma.qc +++ b/qcsrc/common/turrets/turret/plasma.qc @@ -1,22 +1,4 @@ -#ifndef TURRET_PLASMA_H -#define TURRET_PLASMA_H - -#include "plasma_weapon.qh" - -CLASS(PlasmaTurret, Turret) -/* spawnflags */ ATTRIB(PlasmaTurret, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER); -/* mins */ ATTRIB(PlasmaTurret, mins, vector, '-32 -32 0'); -/* maxs */ ATTRIB(PlasmaTurret, maxs, vector, '32 32 64'); -/* modelname */ ATTRIB(PlasmaTurret, mdl, string, "base.md3"); -/* model */ ATTRIB_STRZONE(PlasmaTurret, model, string, strcat("models/turrets/", this.mdl)); -/* head_model */ ATTRIB_STRZONE(PlasmaTurret, head_model, string, strcat("models/turrets/", "plasma.md3")); -/* netname */ ATTRIB(PlasmaTurret, netname, string, "plasma"); -/* fullname */ ATTRIB(PlasmaTurret, turret_name, string, _("Plasma Cannon")); - ATTRIB(PlasmaTurret, m_weapon, Weapon, WEP_PLASMA); -ENDCLASS(PlasmaTurret) -REGISTER_TURRET(PLASMA, NEW(PlasmaTurret)); - -#endif +#include "plasma.qh" #ifdef IMPLEMENTATION diff --git a/qcsrc/common/turrets/turret/plasma.qh b/qcsrc/common/turrets/turret/plasma.qh new file mode 100644 index 0000000000..fc2a96de75 --- /dev/null +++ b/qcsrc/common/turrets/turret/plasma.qh @@ -0,0 +1,16 @@ +#pragma once + +#include "plasma_weapon.qh" + +CLASS(PlasmaTurret, Turret) +/* spawnflags */ ATTRIB(PlasmaTurret, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER); +/* mins */ ATTRIB(PlasmaTurret, mins, vector, '-32 -32 0'); +/* maxs */ ATTRIB(PlasmaTurret, maxs, vector, '32 32 64'); +/* modelname */ ATTRIB(PlasmaTurret, mdl, string, "base.md3"); +/* model */ ATTRIB_STRZONE(PlasmaTurret, model, string, strcat("models/turrets/", this.mdl)); +/* head_model */ ATTRIB_STRZONE(PlasmaTurret, head_model, string, strcat("models/turrets/", "plasma.md3")); +/* netname */ ATTRIB(PlasmaTurret, netname, string, "plasma"); +/* fullname */ ATTRIB(PlasmaTurret, turret_name, string, _("Plasma Cannon")); + ATTRIB(PlasmaTurret, m_weapon, Weapon, WEP_PLASMA); +ENDCLASS(PlasmaTurret) +REGISTER_TURRET(PLASMA, NEW(PlasmaTurret)); diff --git a/qcsrc/common/turrets/turret/plasma_dual.qc b/qcsrc/common/turrets/turret/plasma_dual.qc index 9430bc6921..9e6d80b2fd 100644 --- a/qcsrc/common/turrets/turret/plasma_dual.qc +++ b/qcsrc/common/turrets/turret/plasma_dual.qc @@ -1,28 +1,4 @@ -#ifndef TURRET_PLASMA_DUAL_H -#define TURRET_PLASMA_DUAL_H - -#include "plasma_weapon.qh" - -CLASS(PlasmaDualAttack, PlasmaAttack) -/* refname */ ATTRIB(PlasmaDualAttack, netname, string, "turret_plasma_dual"); -/* wepname */ ATTRIB(PlasmaDualAttack, m_name, string, _("Dual plasma")); -ENDCLASS(PlasmaDualAttack) -REGISTER_WEAPON(PLASMA_DUAL, NEW(PlasmaDualAttack)); - -CLASS(DualPlasmaTurret, PlasmaTurret) -/* spawnflags */ ATTRIB(DualPlasmaTurret, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER); -/* mins */ ATTRIB(DualPlasmaTurret, mins, vector, '-32 -32 0'); -/* maxs */ ATTRIB(DualPlasmaTurret, maxs, vector, '32 32 64'); -/* modelname */ ATTRIB(DualPlasmaTurret, mdl, string, "base.md3"); -/* model */ ATTRIB_STRZONE(DualPlasmaTurret, model, string, strcat("models/turrets/", this.mdl)); -/* head_model */ ATTRIB_STRZONE(DualPlasmaTurret, head_model, string, strcat("models/turrets/", "plasmad.md3")); -/* netname */ ATTRIB(DualPlasmaTurret, netname, string, "plasma_dual"); -/* fullname */ ATTRIB(DualPlasmaTurret, turret_name, string, _("Dual Plasma Cannon")); - ATTRIB(DualPlasmaTurret, m_weapon, Weapon, WEP_PLASMA_DUAL); -ENDCLASS(DualPlasmaTurret) -REGISTER_TURRET(PLASMA_DUAL, NEW(DualPlasmaTurret)); - -#endif +#include "plasma_dual.qh" #ifdef IMPLEMENTATION diff --git a/qcsrc/common/turrets/turret/plasma_dual.qh b/qcsrc/common/turrets/turret/plasma_dual.qh new file mode 100644 index 0000000000..e4c7b0e7df --- /dev/null +++ b/qcsrc/common/turrets/turret/plasma_dual.qh @@ -0,0 +1,22 @@ +#pragma once + +#include "plasma_weapon.qh" + +CLASS(PlasmaDualAttack, PlasmaAttack) +/* refname */ ATTRIB(PlasmaDualAttack, netname, string, "turret_plasma_dual"); +/* wepname */ ATTRIB(PlasmaDualAttack, m_name, string, _("Dual plasma")); +ENDCLASS(PlasmaDualAttack) +REGISTER_WEAPON(PLASMA_DUAL, NEW(PlasmaDualAttack)); + +CLASS(DualPlasmaTurret, PlasmaTurret) +/* spawnflags */ ATTRIB(DualPlasmaTurret, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER); +/* mins */ ATTRIB(DualPlasmaTurret, mins, vector, '-32 -32 0'); +/* maxs */ ATTRIB(DualPlasmaTurret, maxs, vector, '32 32 64'); +/* modelname */ ATTRIB(DualPlasmaTurret, mdl, string, "base.md3"); +/* model */ ATTRIB_STRZONE(DualPlasmaTurret, model, string, strcat("models/turrets/", this.mdl)); +/* head_model */ ATTRIB_STRZONE(DualPlasmaTurret, head_model, string, strcat("models/turrets/", "plasmad.md3")); +/* netname */ ATTRIB(DualPlasmaTurret, netname, string, "plasma_dual"); +/* fullname */ ATTRIB(DualPlasmaTurret, turret_name, string, _("Dual Plasma Cannon")); + ATTRIB(DualPlasmaTurret, m_weapon, Weapon, WEP_PLASMA_DUAL); +ENDCLASS(DualPlasmaTurret) +REGISTER_TURRET(PLASMA_DUAL, NEW(DualPlasmaTurret)); diff --git a/qcsrc/common/turrets/turret/tesla.qc b/qcsrc/common/turrets/turret/tesla.qc index b0755f32b5..249fe18eb9 100644 --- a/qcsrc/common/turrets/turret/tesla.qc +++ b/qcsrc/common/turrets/turret/tesla.qc @@ -1,22 +1,4 @@ -#ifndef TURRET_TESLA_H -#define TURRET_TESLA_H - -#include "tesla_weapon.qh" - -CLASS(TeslaCoil, Turret) -/* spawnflags */ ATTRIB(TeslaCoil, spawnflags, int, TUR_FLAG_HITSCAN | TUR_FLAG_PLAYER | TUR_FLAG_MISSILE); -/* mins */ ATTRIB(TeslaCoil, mins, vector, '-60 -60 0'); -/* maxs */ ATTRIB(TeslaCoil, maxs, vector, '60 60 128'); -/* modelname */ ATTRIB(TeslaCoil, mdl, string, "tesla_base.md3"); -/* model */ ATTRIB_STRZONE(TeslaCoil, model, string, strcat("models/turrets/", this.mdl)); -/* head_model */ ATTRIB_STRZONE(TeslaCoil, head_model, string, strcat("models/turrets/", "tesla_head.md3")); -/* netname */ ATTRIB(TeslaCoil, netname, string, "tesla"); -/* fullname */ ATTRIB(TeslaCoil, turret_name, string, _("Tesla Coil")); - ATTRIB(TeslaCoil, m_weapon, Weapon, WEP_TESLA); -ENDCLASS(TeslaCoil) -REGISTER_TURRET(TESLA, NEW(TeslaCoil)); - -#endif +#include "tesla.qh" #ifdef IMPLEMENTATION diff --git a/qcsrc/common/turrets/turret/tesla.qh b/qcsrc/common/turrets/turret/tesla.qh new file mode 100644 index 0000000000..c5f67b1b9d --- /dev/null +++ b/qcsrc/common/turrets/turret/tesla.qh @@ -0,0 +1,16 @@ +#pragma once + +#include "tesla_weapon.qh" + +CLASS(TeslaCoil, Turret) +/* spawnflags */ ATTRIB(TeslaCoil, spawnflags, int, TUR_FLAG_HITSCAN | TUR_FLAG_PLAYER | TUR_FLAG_MISSILE); +/* mins */ ATTRIB(TeslaCoil, mins, vector, '-60 -60 0'); +/* maxs */ ATTRIB(TeslaCoil, maxs, vector, '60 60 128'); +/* modelname */ ATTRIB(TeslaCoil, mdl, string, "tesla_base.md3"); +/* model */ ATTRIB_STRZONE(TeslaCoil, model, string, strcat("models/turrets/", this.mdl)); +/* head_model */ ATTRIB_STRZONE(TeslaCoil, head_model, string, strcat("models/turrets/", "tesla_head.md3")); +/* netname */ ATTRIB(TeslaCoil, netname, string, "tesla"); +/* fullname */ ATTRIB(TeslaCoil, turret_name, string, _("Tesla Coil")); + ATTRIB(TeslaCoil, m_weapon, Weapon, WEP_TESLA); +ENDCLASS(TeslaCoil) +REGISTER_TURRET(TESLA, NEW(TeslaCoil)); diff --git a/qcsrc/common/turrets/turret/walker.qc b/qcsrc/common/turrets/turret/walker.qc index 727da27f94..94ecdd332a 100644 --- a/qcsrc/common/turrets/turret/walker.qc +++ b/qcsrc/common/turrets/turret/walker.qc @@ -1,24 +1,4 @@ -#ifndef TURRET_WALKER_H -#define TURRET_WALKER_H - -//#define WALKER_FANCYPATHING - -#include "walker_weapon.qh" - -CLASS(WalkerTurret, Turret) -/* spawnflags */ ATTRIB(WalkerTurret, spawnflags, int, TUR_FLAG_PLAYER | TUR_FLAG_MOVE); -/* mins */ ATTRIB(WalkerTurret, mins, vector, '-70 -70 0'); -/* maxs */ ATTRIB(WalkerTurret, maxs, vector, '70 70 95'); -/* modelname */ ATTRIB(WalkerTurret, mdl, string, "walker_body.md3"); -/* model */ ATTRIB_STRZONE(WalkerTurret, model, string, strcat("models/turrets/", this.mdl)); -/* head_model */ ATTRIB_STRZONE(WalkerTurret, head_model, string, strcat("models/turrets/", "walker_head_minigun.md3")); -/* netname */ ATTRIB(WalkerTurret, netname, string, "walker"); -/* fullname */ ATTRIB(WalkerTurret, turret_name, string, _("Walker Turret")); - ATTRIB(WalkerTurret, m_weapon, Weapon, WEP_WALKER); -ENDCLASS(WalkerTurret) -REGISTER_TURRET(WALKER, NEW(WalkerTurret)); - -#endif +#include "walker.qh" #ifdef IMPLEMENTATION diff --git a/qcsrc/common/turrets/turret/walker.qh b/qcsrc/common/turrets/turret/walker.qh new file mode 100644 index 0000000000..54a908bd1c --- /dev/null +++ b/qcsrc/common/turrets/turret/walker.qh @@ -0,0 +1,18 @@ +#pragma once + +//#define WALKER_FANCYPATHING + +#include "walker_weapon.qh" + +CLASS(WalkerTurret, Turret) +/* spawnflags */ ATTRIB(WalkerTurret, spawnflags, int, TUR_FLAG_PLAYER | TUR_FLAG_MOVE); +/* mins */ ATTRIB(WalkerTurret, mins, vector, '-70 -70 0'); +/* maxs */ ATTRIB(WalkerTurret, maxs, vector, '70 70 95'); +/* modelname */ ATTRIB(WalkerTurret, mdl, string, "walker_body.md3"); +/* model */ ATTRIB_STRZONE(WalkerTurret, model, string, strcat("models/turrets/", this.mdl)); +/* head_model */ ATTRIB_STRZONE(WalkerTurret, head_model, string, strcat("models/turrets/", "walker_head_minigun.md3")); +/* netname */ ATTRIB(WalkerTurret, netname, string, "walker"); +/* fullname */ ATTRIB(WalkerTurret, turret_name, string, _("Walker Turret")); + ATTRIB(WalkerTurret, m_weapon, Weapon, WEP_WALKER); +ENDCLASS(WalkerTurret) +REGISTER_TURRET(WALKER, NEW(WalkerTurret)); diff --git a/qcsrc/common/turrets/turrets.qc b/qcsrc/common/turrets/turrets.qc new file mode 100644 index 0000000000..a53f859524 --- /dev/null +++ b/qcsrc/common/turrets/turrets.qc @@ -0,0 +1 @@ +#include "turrets.qh" diff --git a/qcsrc/common/turrets/turrets.qh b/qcsrc/common/turrets/turrets.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/turrets/turrets.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/turrets/util.qc b/qcsrc/common/turrets/util.qc index 8a6a5ee87b..5fef364580 100644 --- a/qcsrc/common/turrets/util.qc +++ b/qcsrc/common/turrets/util.qc @@ -1,3 +1,4 @@ +#include "util.qh" /* * Update this.tur_shotorg by getting up2date bone info * NOTICE this func overwrites the global v_forward, v_right and v_up vectors. diff --git a/qcsrc/common/util.qc b/qcsrc/common/util.qc index 9cf439f52b..716abaf988 100644 --- a/qcsrc/common/util.qc +++ b/qcsrc/common/util.qc @@ -18,7 +18,7 @@ #include "mapinfo.qh" #endif -#ifndef MENUQC +#ifdef GAMEQC /* * Get "real" origin, in worldspace, even if ent is attached to something else. */ @@ -55,8 +55,7 @@ string wordwrap(string s, float l) return r; } -#ifndef MENUQC -#ifndef CSQC +#ifdef SVQC entity _wordwrap_buffer_sprint_ent; void wordwrap_buffer_sprint(string s) { @@ -80,7 +79,6 @@ void wordwrap_sprint(entity to, string s, float l) return; } #endif -#endif #ifndef SVQC string draw_UseSkinFor(string pic) @@ -352,7 +350,7 @@ STATIC_INIT(compressShortVector) } } -#ifndef MENUQC +#ifdef GAMEQC float CheckWireframeBox(entity forent, vector v0, vector dvx, vector dvy, vector dvz) { traceline(v0, v0 + dvx, true, forent); if(trace_fraction < 1) return 0; @@ -448,7 +446,7 @@ string swapInPriorityList(string order, float i, float j) return order; } -#ifndef MENUQC +#ifdef GAMEQC void get_mi_min_max(float mode) { vector mi, ma; @@ -1113,7 +1111,7 @@ vector decompressShotOrigin(int f) return v; } -#ifndef MENUQC +#ifdef GAMEQC vector healtharmor_maxdamage(float h, float a, float armorblock, int deathtype) { // NOTE: we'll always choose the SMALLER value... @@ -1228,7 +1226,7 @@ float get_model_parameters(string m, float sk) } get_model_parameters_fixbone = 0; -#ifndef MENUQC +#ifdef GAMEQC MUTATOR_CALLHOOK(ClearModelParams); #endif @@ -1293,7 +1291,7 @@ float get_model_parameters(string m, float sk) get_model_parameters_bone_upperbody = s; if(c == "bone_weapon") get_model_parameters_bone_weapon = s; - #ifndef MENUQC + #ifdef GAMEQC MUTATOR_CALLHOOK(GetModelParams, c, s); #endif for(int i = 0; i < MAX_AIM_BONES; ++i) @@ -1386,7 +1384,7 @@ void m_shutdown() cvar_settemp_restore(); // this must be done LAST, but in any case } -#ifndef MENUQC +#ifdef GAMEQC .float skeleton_bones_index; void Skeleton_SetBones(entity e) { @@ -1464,7 +1462,7 @@ void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t queue_start.FindConnectedComponent_processing = 0; } -#ifndef MENUQC +#ifdef GAMEQC vector animfixfps(entity e, vector a, vector b) { // multi-frame anim: keep as-is @@ -1483,7 +1481,7 @@ vector animfixfps(entity e, vector a, vector b) } #endif -#ifndef MENUQC +#ifdef GAMEQC Notification Announcer_PickNumber(int type, int num) { return = NULL; @@ -1595,7 +1593,7 @@ Notification Announcer_PickNumber(int type, int num) } #endif -#ifndef MENUQC +#ifdef GAMEQC int Mod_Q1BSP_SuperContentsFromNativeContents(int nativecontents) { switch(nativecontents) diff --git a/qcsrc/common/util.qh b/qcsrc/common/util.qh index 599dd4d43c..9e1d5a75be 100644 --- a/qcsrc/common/util.qh +++ b/qcsrc/common/util.qh @@ -1,6 +1,6 @@ #pragma once -#ifndef MENUQC +#ifdef GAMEQC vector real_origin(entity ent); #endif @@ -8,11 +8,9 @@ vector real_origin(entity ent); // this returns a tempstring containing a copy of s with additional \n newlines added, it also replaces \n in the text with a real newline // NOTE: s IS allowed to be a tempstring string wordwrap(string s, float l); -#ifndef MENUQC -#ifndef CSQC +#ifdef SVQC void wordwrap_sprint(entity to, string s, float l); #endif -#endif void wordwrap_cb(string s, float l, void(string) callback); #ifndef SVQC @@ -66,7 +64,7 @@ string ScoreString(float vflags, float value); vector decompressShortVector(float data); float compressShortVector(vector vec); -#ifndef MENUQC +#ifdef GAMEQC float CheckWireframeBox(entity forent, vector v0, vector dvx, vector dvy, vector dvz); #endif @@ -79,7 +77,7 @@ float cvar_value_issafe(string s); float cvar_settemp(string pKey, string pValue); float cvar_settemp_restore(); -#ifndef MENUQC +#ifdef GAMEQC // modes: 0 = trust q3map2 (_mini images) // 1 = trust tracebox (_radar images) // in both modes, mapinfo's "size" overrides @@ -148,14 +146,14 @@ string rankings_reply, ladder_reply, lsmaps_reply, maplist_reply, monsterlist_re string records_reply[10]; #endif -#ifndef MENUQC +#ifdef GAMEQC vector healtharmor_maxdamage(float h, float a, float armorblock, int deathtype); // returns vector: maxdamage, armorideal, 1 if fully armored vector healtharmor_applydamage(float a, float armorblock, int deathtype, float damage); // returns vector: take, save, 0 #endif string getcurrentmod(); -#ifndef MENUQC +#ifdef GAMEQC #ifdef CSQC int ReadInt24_t(); #else @@ -190,7 +188,7 @@ float get_model_parameters_fixbone; string get_model_parameters_desc; float get_model_parameters(string mod, float skn); // call with string_null to clear; skin -1 means mod is the filename of the txt file and is to be split -#ifndef MENUQC +#ifdef GAMEQC vector NearestPointOnBox(entity box, vector org); #endif @@ -205,7 +203,7 @@ const float XENCODE_LEN = 5; string xencode(float f); float xdecode(string s); -#ifndef MENUQC +#ifdef GAMEQC string strtolower(string s); #endif @@ -214,7 +212,7 @@ string MakeConsoleSafe(string input); // generic shutdown handler void Shutdown(); -#ifndef MENUQC +#ifdef GAMEQC .float skeleton_bones; void Skeleton_SetBones(entity e); // loops through the tags of model v using counter tagnum @@ -256,7 +254,7 @@ void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t string CCR(string input); -#ifndef MENUQC +#ifdef GAMEQC #ifdef CSQC #define GENTLE (autocvar_cl_gentle || autocvar_cl_gentle_messages) #else @@ -265,11 +263,11 @@ string CCR(string input); #define normal_or_gentle(normal, gentle) (GENTLE ? ((gentle != "") ? gentle : normal) : normal) #endif -#ifndef MENUQC +#ifdef GAMEQC vector animfixfps(entity e, vector a, vector b); #endif -#ifndef MENUQC +#ifdef GAMEQC const float CNT_NORMAL = 1; const float CNT_GAMESTART = 2; const float CNT_IDLE = 3; @@ -279,7 +277,7 @@ const float CNT_ROUNDSTART = 6; entity Announcer_PickNumber(float type, float num); #endif -#ifndef MENUQC +#ifdef GAMEQC int Mod_Q1BSP_SuperContentsFromNativeContents(int nativecontents); int Mod_Q1BSP_NativeContentsFromSuperContents(int supercontents); #endif diff --git a/qcsrc/common/vehicles/_all.inc b/qcsrc/common/vehicles/_all.inc new file mode 100644 index 0000000000..8bc63f720a --- /dev/null +++ b/qcsrc/common/vehicles/_all.inc @@ -0,0 +1,2 @@ +#include "_all.qh" +#include "_mod.inc" diff --git a/qcsrc/common/vehicles/_all.qh b/qcsrc/common/vehicles/_all.qh new file mode 100644 index 0000000000..947026dd59 --- /dev/null +++ b/qcsrc/common/vehicles/_all.qh @@ -0,0 +1,2 @@ +#pragma once +#include "_mod.qh" diff --git a/qcsrc/common/vehicles/_mod.inc b/qcsrc/common/vehicles/_mod.inc index 269858f847..ed26659daa 100644 --- a/qcsrc/common/vehicles/_mod.inc +++ b/qcsrc/common/vehicles/_mod.inc @@ -1,4 +1,9 @@ // generated file; do not modify #include -#include -#include +#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/vehicles/_mod.qh b/qcsrc/common/vehicles/_mod.qh index d21e829f7a..4892b0f317 100644 --- a/qcsrc/common/vehicles/_mod.qh +++ b/qcsrc/common/vehicles/_mod.qh @@ -1,4 +1,9 @@ // generated file; do not modify #include -#include -#include +#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/vehicles/all.qc b/qcsrc/common/vehicles/all.qc index 251df22839..4aef11cad5 100644 --- a/qcsrc/common/vehicles/all.qc +++ b/qcsrc/common/vehicles/all.qc @@ -1,8 +1,7 @@ +#include "all.qh" #ifndef VEHICLES_ALL_C #define VEHICLES_ALL_C -#include "all.qh" - REGISTER_NET_LINKED(ENT_CLIENT_AUXILIARYXHAIR) #if defined(SVQC) diff --git a/qcsrc/common/vehicles/cl_vehicles.qc b/qcsrc/common/vehicles/cl_vehicles.qc index e08490976d..afb48c918b 100644 --- a/qcsrc/common/vehicles/cl_vehicles.qc +++ b/qcsrc/common/vehicles/cl_vehicles.qc @@ -1,3 +1,4 @@ +#include "cl_vehicles.qh" const string vCROSS_BURST = "gfx/vehicles/crosshair_burst.tga"; const string vCROSS_DROP = "gfx/vehicles/crosshair_drop.tga"; const string vCROSS_GUIDE = "gfx/vehicles/crosshair_guide.tga"; diff --git a/qcsrc/common/vehicles/vehicle/bumblebee.qc b/qcsrc/common/vehicles/vehicle/bumblebee.qc index 9f718e34b9..403c9de9dd 100644 --- a/qcsrc/common/vehicles/vehicle/bumblebee.qc +++ b/qcsrc/common/vehicles/vehicle/bumblebee.qc @@ -1,34 +1,5 @@ -#ifndef VEHICLE_BUMBLEBEE -#define VEHICLE_BUMBLEBEE #include "bumblebee.qh" -#include "bumblebee_weapons.qh" - -CLASS(Bumblebee, Vehicle) -/* spawnflags */ ATTRIB(Bumblebee, spawnflags, int, VHF_DMGSHAKE); -/* mins */ ATTRIB(Bumblebee, mins, vector, '-245 -130 -130'); -/* maxs */ ATTRIB(Bumblebee, maxs, vector, '230 130 130'); -/* view offset*/ ATTRIB(Bumblebee, view_ofs, vector, '0 0 300'); -/* view dist */ ATTRIB(Bumblebee, height, float, 450); -/* model */ ATTRIB(Bumblebee, mdl, string, "models/vehicles/bumblebee_body.dpm"); -/* model */ ATTRIB(Bumblebee, model, string, "models/vehicles/bumblebee_body.dpm"); -/* head_model */ ATTRIB(Bumblebee, head_model, string, ""); -/* hud_model */ ATTRIB(Bumblebee, hud_model, string, "models/vehicles/spiderbot_cockpit.dpm"); -/* tags */ ATTRIB(Bumblebee, tag_head, string, ""); -/* tags */ ATTRIB(Bumblebee, tag_hud, string, ""); -/* tags */ ATTRIB(Bumblebee, tag_view, string, "tag_viewport"); -/* netname */ ATTRIB(Bumblebee, netname, string, "bumblebee"); -/* fullname */ ATTRIB(Bumblebee, vehicle_name, string, _("Bumblebee")); -/* icon */ ATTRIB(Bumblebee, m_icon, string, "vehicle_bumble"); -ENDCLASS(Bumblebee) -REGISTER_VEHICLE(BUMBLEBEE, NEW(Bumblebee)); - -#ifndef MENUQC - MODEL(VEH_BUMBLEBEE_GUNCOCKPIT, "models/vehicles/wakizashi_cockpit.dpm"); -#endif - -#endif - #ifdef IMPLEMENTATION const float BRG_SETUP = 2; diff --git a/qcsrc/common/vehicles/vehicle/bumblebee.qh b/qcsrc/common/vehicles/vehicle/bumblebee.qh index b675185d49..2c90b7c40b 100644 --- a/qcsrc/common/vehicles/vehicle/bumblebee.qh +++ b/qcsrc/common/vehicles/vehicle/bumblebee.qh @@ -1,6 +1,30 @@ #pragma once -#ifdef CSQC +#include "bumblebee_weapons.qh" + +CLASS(Bumblebee, Vehicle) +/* spawnflags */ ATTRIB(Bumblebee, spawnflags, int, VHF_DMGSHAKE); +/* mins */ ATTRIB(Bumblebee, mins, vector, '-245 -130 -130'); +/* maxs */ ATTRIB(Bumblebee, maxs, vector, '230 130 130'); +/* view offset*/ ATTRIB(Bumblebee, view_ofs, vector, '0 0 300'); +/* view dist */ ATTRIB(Bumblebee, height, float, 450); +/* model */ ATTRIB(Bumblebee, mdl, string, "models/vehicles/bumblebee_body.dpm"); +/* model */ ATTRIB(Bumblebee, model, string, "models/vehicles/bumblebee_body.dpm"); +/* head_model */ ATTRIB(Bumblebee, head_model, string, ""); +/* hud_model */ ATTRIB(Bumblebee, hud_model, string, "models/vehicles/spiderbot_cockpit.dpm"); +/* tags */ ATTRIB(Bumblebee, tag_head, string, ""); +/* tags */ ATTRIB(Bumblebee, tag_hud, string, ""); +/* tags */ ATTRIB(Bumblebee, tag_view, string, "tag_viewport"); +/* netname */ ATTRIB(Bumblebee, netname, string, "bumblebee"); +/* fullname */ ATTRIB(Bumblebee, vehicle_name, string, _("Bumblebee")); +/* icon */ ATTRIB(Bumblebee, m_icon, string, "vehicle_bumble"); +ENDCLASS(Bumblebee) +REGISTER_VEHICLE(BUMBLEBEE, NEW(Bumblebee)); +#ifdef GAMEQC + MODEL(VEH_BUMBLEBEE_GUNCOCKPIT, "models/vehicles/wakizashi_cockpit.dpm"); +#endif + +#ifdef CSQC void CSQC_BUMBLE_GUN_HUD(); #endif diff --git a/qcsrc/common/vehicles/vehicle/bumblebee_weapons.qh b/qcsrc/common/vehicles/vehicle/bumblebee_weapons.qh index 73fed55e1c..d4ed9505b9 100644 --- a/qcsrc/common/vehicles/vehicle/bumblebee_weapons.qh +++ b/qcsrc/common/vehicles/vehicle/bumblebee_weapons.qh @@ -1,6 +1,6 @@ #pragma once -#include +#include float autocvar_g_vehicle_bumblebee_cannon_cost = 2; float autocvar_g_vehicle_bumblebee_cannon_damage = 60; diff --git a/qcsrc/common/vehicles/vehicle/racer.qc b/qcsrc/common/vehicles/vehicle/racer.qc index 2bccec21a0..c8f8cf84ea 100644 --- a/qcsrc/common/vehicles/vehicle/racer.qc +++ b/qcsrc/common/vehicles/vehicle/racer.qc @@ -1,28 +1,4 @@ -#ifndef VEHICLE_RACER -#define VEHICLE_RACER - -#include "racer_weapon.qh" - -CLASS(Racer, Vehicle) -/* spawnflags */ ATTRIB(Racer, spawnflags, int, VHF_DMGSHAKE | VHF_DMGROLL); -/* mins */ ATTRIB(Racer, mins, vector, '-120 -120 -40' * 0.5); -/* maxs */ ATTRIB(Racer, maxs, vector, '120 120 40' * 0.5); -/* view offset*/ ATTRIB(Racer, view_ofs, vector, '0 0 50'); -/* view dist */ ATTRIB(Racer, height, float, 200); -/* model */ ATTRIB(Racer, mdl, string, "models/vehicles/wakizashi.dpm"); -/* model */ ATTRIB(Racer, model, string, "models/vehicles/wakizashi.dpm"); -/* head_model */ ATTRIB(Racer, head_model, string, "null"); -/* hud_model */ ATTRIB(Racer, hud_model, string, "models/vehicles/wakizashi_cockpit.dpm"); -/* tags */ ATTRIB(Racer, tag_head, string, ""); -/* tags */ ATTRIB(Racer, tag_hud, string, ""); -/* tags */ ATTRIB(Racer, tag_view, string, "tag_viewport"); -/* netname */ ATTRIB(Racer, netname, string, "racer"); -/* fullname */ ATTRIB(Racer, vehicle_name, string, _("Racer")); -/* icon */ ATTRIB(Racer, m_icon, string, "vehicle_racer"); -ENDCLASS(Racer) -REGISTER_VEHICLE(RACER, NEW(Racer)); - -#endif +#include "racer.qh" #ifdef IMPLEMENTATION diff --git a/qcsrc/common/vehicles/vehicle/racer.qh b/qcsrc/common/vehicles/vehicle/racer.qh new file mode 100644 index 0000000000..dd14144132 --- /dev/null +++ b/qcsrc/common/vehicles/vehicle/racer.qh @@ -0,0 +1,22 @@ +#pragma once + +#include "racer_weapon.qh" + +CLASS(Racer, Vehicle) +/* spawnflags */ ATTRIB(Racer, spawnflags, int, VHF_DMGSHAKE | VHF_DMGROLL); +/* mins */ ATTRIB(Racer, mins, vector, '-120 -120 -40' * 0.5); +/* maxs */ ATTRIB(Racer, maxs, vector, '120 120 40' * 0.5); +/* view offset*/ ATTRIB(Racer, view_ofs, vector, '0 0 50'); +/* view dist */ ATTRIB(Racer, height, float, 200); +/* model */ ATTRIB(Racer, mdl, string, "models/vehicles/wakizashi.dpm"); +/* model */ ATTRIB(Racer, model, string, "models/vehicles/wakizashi.dpm"); +/* head_model */ ATTRIB(Racer, head_model, string, "null"); +/* hud_model */ ATTRIB(Racer, hud_model, string, "models/vehicles/wakizashi_cockpit.dpm"); +/* tags */ ATTRIB(Racer, tag_head, string, ""); +/* tags */ ATTRIB(Racer, tag_hud, string, ""); +/* tags */ ATTRIB(Racer, tag_view, string, "tag_viewport"); +/* netname */ ATTRIB(Racer, netname, string, "racer"); +/* fullname */ ATTRIB(Racer, vehicle_name, string, _("Racer")); +/* icon */ ATTRIB(Racer, m_icon, string, "vehicle_racer"); +ENDCLASS(Racer) +REGISTER_VEHICLE(RACER, NEW(Racer)); diff --git a/qcsrc/common/vehicles/vehicle/racer_weapon.qh b/qcsrc/common/vehicles/vehicle/racer_weapon.qh index fc9e352542..51c20ef9d0 100644 --- a/qcsrc/common/vehicles/vehicle/racer_weapon.qh +++ b/qcsrc/common/vehicles/vehicle/racer_weapon.qh @@ -1,6 +1,6 @@ #pragma once -#include +#include CLASS(RacerAttack, PortoLaunch) /* flags */ ATTRIB(RacerAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); diff --git a/qcsrc/common/vehicles/vehicle/raptor.qc b/qcsrc/common/vehicles/vehicle/raptor.qc index 510f63ba1f..a03c936bd7 100644 --- a/qcsrc/common/vehicles/vehicle/raptor.qc +++ b/qcsrc/common/vehicles/vehicle/raptor.qc @@ -1,30 +1,5 @@ -#ifndef VEHICLE_RAPTOR -#define VEHICLE_RAPTOR #include "raptor.qh" -#include "raptor_weapons.qh" - -CLASS(Raptor, Vehicle) -/* spawnflags */ ATTRIB(Raptor, spawnflags, int, VHF_DMGSHAKE | VHF_DMGROLL); -/* mins */ ATTRIB(Raptor, mins, vector, '-80 -80 0'); -/* maxs */ ATTRIB(Raptor, maxs, vector, '80 80 70'); -/* view offset*/ ATTRIB(Raptor, view_ofs, vector, '0 0 160'); -/* view dist */ ATTRIB(Raptor, height, float, 200); -/* model */ ATTRIB(Raptor, mdl, string, "models/vehicles/raptor.dpm"); -/* model */ ATTRIB(Raptor, model, string, "models/vehicles/raptor.dpm"); -/* head_model */ ATTRIB(Raptor, head_model, string, ""); -/* hud_model */ ATTRIB(Raptor, hud_model, string, "models/vehicles/raptor_cockpit.dpm"); -/* tags */ ATTRIB(Raptor, tag_head, string, ""); -/* tags */ ATTRIB(Raptor, tag_hud, string, "tag_hud"); -/* tags */ ATTRIB(Raptor, tag_view, string, "tag_camera"); -/* netname */ ATTRIB(Raptor, netname, string, "raptor"); -/* fullname */ ATTRIB(Raptor, vehicle_name, string, _("Raptor")); -/* icon */ ATTRIB(Raptor, m_icon, string, "vehicle_raptor"); -ENDCLASS(Raptor) -REGISTER_VEHICLE(RAPTOR, NEW(Raptor)); - -#endif - #ifdef IMPLEMENTATION #ifdef SVQC diff --git a/qcsrc/common/vehicles/vehicle/raptor.qh b/qcsrc/common/vehicles/vehicle/raptor.qh index fd9a7de790..12666523c7 100644 --- a/qcsrc/common/vehicles/vehicle/raptor.qh +++ b/qcsrc/common/vehicles/vehicle/raptor.qh @@ -1,5 +1,26 @@ #pragma once +#include "raptor_weapons.qh" + +CLASS(Raptor, Vehicle) +/* spawnflags */ ATTRIB(Raptor, spawnflags, int, VHF_DMGSHAKE | VHF_DMGROLL); +/* mins */ ATTRIB(Raptor, mins, vector, '-80 -80 0'); +/* maxs */ ATTRIB(Raptor, maxs, vector, '80 80 70'); +/* view offset*/ ATTRIB(Raptor, view_ofs, vector, '0 0 160'); +/* view dist */ ATTRIB(Raptor, height, float, 200); +/* model */ ATTRIB(Raptor, mdl, string, "models/vehicles/raptor.dpm"); +/* model */ ATTRIB(Raptor, model, string, "models/vehicles/raptor.dpm"); +/* head_model */ ATTRIB(Raptor, head_model, string, ""); +/* hud_model */ ATTRIB(Raptor, hud_model, string, "models/vehicles/raptor_cockpit.dpm"); +/* tags */ ATTRIB(Raptor, tag_head, string, ""); +/* tags */ ATTRIB(Raptor, tag_hud, string, "tag_hud"); +/* tags */ ATTRIB(Raptor, tag_view, string, "tag_camera"); +/* netname */ ATTRIB(Raptor, netname, string, "raptor"); +/* fullname */ ATTRIB(Raptor, vehicle_name, string, _("Raptor")); +/* icon */ ATTRIB(Raptor, m_icon, string, "vehicle_raptor"); +ENDCLASS(Raptor) +REGISTER_VEHICLE(RAPTOR, NEW(Raptor)); + const int RSM_FIRST = 1; const int RSM_BOMB = 1; const int RSM_FLARE = 2; diff --git a/qcsrc/common/vehicles/vehicle/raptor_weapons.qh b/qcsrc/common/vehicles/vehicle/raptor_weapons.qh index 0b3af4169d..4260d4292e 100644 --- a/qcsrc/common/vehicles/vehicle/raptor_weapons.qh +++ b/qcsrc/common/vehicles/vehicle/raptor_weapons.qh @@ -1,6 +1,6 @@ #pragma once -#include +#include CLASS(RaptorCannon, PortoLaunch) /* flags */ ATTRIB(RaptorCannon, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); diff --git a/qcsrc/common/vehicles/vehicle/spiderbot.qc b/qcsrc/common/vehicles/vehicle/spiderbot.qc index 3365266f80..fc8a4f190b 100644 --- a/qcsrc/common/vehicles/vehicle/spiderbot.qc +++ b/qcsrc/common/vehicles/vehicle/spiderbot.qc @@ -1,29 +1,4 @@ -#ifndef VEHICLE_SPIDERBOT -#define VEHICLE_SPIDERBOT - -#include "spiderbot_weapons.qh" - -CLASS(Spiderbot, Vehicle) -/* spawnflags */ ATTRIB(Spiderbot, spawnflags, int, VHF_DMGSHAKE); -/* mins */ ATTRIB(Spiderbot, mins, vector, '-75 -75 10'); -/* maxs */ ATTRIB(Spiderbot, maxs, vector, '75 75 125'); -/* view offset*/ ATTRIB(Spiderbot, view_ofs, vector, '0 0 70'); -/* view dist */ ATTRIB(Spiderbot, height, float, 170); -/* model */ ATTRIB(Spiderbot, mdl, string, "models/vehicles/spiderbot.dpm"); -/* model */ ATTRIB(Spiderbot, model, string, "models/vehicles/spiderbot.dpm"); -/* head_model */ ATTRIB(Spiderbot, head_model, string, "models/vehicles/spiderbot_top.dpm"); -/* hud_model */ ATTRIB(Spiderbot, hud_model, string, "models/vehicles/spiderbot_cockpit.dpm"); -/* tags */ ATTRIB(Spiderbot, tag_head, string, "tag_head"); -/* tags */ ATTRIB(Spiderbot, tag_hud, string, "tag_hud"); -/* tags */ ATTRIB(Spiderbot, tag_view, string, ""); -/* netname */ ATTRIB(Spiderbot, netname, string, "spiderbot"); -/* fullname */ ATTRIB(Spiderbot, vehicle_name, string, _("Spiderbot")); -/* icon */ ATTRIB(Spiderbot, m_icon, string, "vehicle_spider"); -ENDCLASS(Spiderbot) - -REGISTER_VEHICLE(SPIDERBOT, NEW(Spiderbot)); - -#endif +#include "spiderbot.qh" #ifdef IMPLEMENTATION diff --git a/qcsrc/common/vehicles/vehicle/spiderbot.qh b/qcsrc/common/vehicles/vehicle/spiderbot.qh new file mode 100644 index 0000000000..a594ace048 --- /dev/null +++ b/qcsrc/common/vehicles/vehicle/spiderbot.qh @@ -0,0 +1,23 @@ +#pragma once + +#include "spiderbot_weapons.qh" + +CLASS(Spiderbot, Vehicle) +/* spawnflags */ ATTRIB(Spiderbot, spawnflags, int, VHF_DMGSHAKE); +/* mins */ ATTRIB(Spiderbot, mins, vector, '-75 -75 10'); +/* maxs */ ATTRIB(Spiderbot, maxs, vector, '75 75 125'); +/* view offset*/ ATTRIB(Spiderbot, view_ofs, vector, '0 0 70'); +/* view dist */ ATTRIB(Spiderbot, height, float, 170); +/* model */ ATTRIB(Spiderbot, mdl, string, "models/vehicles/spiderbot.dpm"); +/* model */ ATTRIB(Spiderbot, model, string, "models/vehicles/spiderbot.dpm"); +/* head_model */ ATTRIB(Spiderbot, head_model, string, "models/vehicles/spiderbot_top.dpm"); +/* hud_model */ ATTRIB(Spiderbot, hud_model, string, "models/vehicles/spiderbot_cockpit.dpm"); +/* tags */ ATTRIB(Spiderbot, tag_head, string, "tag_head"); +/* tags */ ATTRIB(Spiderbot, tag_hud, string, "tag_hud"); +/* tags */ ATTRIB(Spiderbot, tag_view, string, ""); +/* netname */ ATTRIB(Spiderbot, netname, string, "spiderbot"); +/* fullname */ ATTRIB(Spiderbot, vehicle_name, string, _("Spiderbot")); +/* icon */ ATTRIB(Spiderbot, m_icon, string, "vehicle_spider"); +ENDCLASS(Spiderbot) + +REGISTER_VEHICLE(SPIDERBOT, NEW(Spiderbot)); diff --git a/qcsrc/common/vehicles/vehicle/spiderbot_weapons.qh b/qcsrc/common/vehicles/vehicle/spiderbot_weapons.qh index 61e2b02501..a1523045a6 100644 --- a/qcsrc/common/vehicles/vehicle/spiderbot_weapons.qh +++ b/qcsrc/common/vehicles/vehicle/spiderbot_weapons.qh @@ -1,6 +1,6 @@ #pragma once -#include +#include #ifdef SVQC void spiderbot_rocket_do(entity this); diff --git a/qcsrc/common/vehicles/vehicles.qc b/qcsrc/common/vehicles/vehicles.qc new file mode 100644 index 0000000000..5e7736501b --- /dev/null +++ b/qcsrc/common/vehicles/vehicles.qc @@ -0,0 +1 @@ +#include "vehicles.qh" diff --git a/qcsrc/common/vehicles/vehicles.qh b/qcsrc/common/vehicles/vehicles.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/vehicles/vehicles.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/viewloc.qc b/qcsrc/common/viewloc.qc index 4b6a6997a4..7d6a3818e0 100644 --- a/qcsrc/common/viewloc.qc +++ b/qcsrc/common/viewloc.qc @@ -1,3 +1,4 @@ +#include "viewloc.qh" #include "util.qh" #if defined(CSQC) diff --git a/qcsrc/common/weapons/_all.inc b/qcsrc/common/weapons/_all.inc new file mode 100644 index 0000000000..213c39c621 --- /dev/null +++ b/qcsrc/common/weapons/_all.inc @@ -0,0 +1,2 @@ +#include "_all.qh" +#include "all.qc" diff --git a/qcsrc/common/weapons/_all.qh b/qcsrc/common/weapons/_all.qh new file mode 100644 index 0000000000..671dde06df --- /dev/null +++ b/qcsrc/common/weapons/_all.qh @@ -0,0 +1,2 @@ +#pragma once +#include "all.qh" diff --git a/qcsrc/common/weapons/all.qc b/qcsrc/common/weapons/all.qc index 2b75ec0313..dfdbd76566 100644 --- a/qcsrc/common/weapons/all.qc +++ b/qcsrc/common/weapons/all.qc @@ -1,8 +1,7 @@ +#include "all.qh" #ifndef WEAPONS_ALL_C #define WEAPONS_ALL_C -#include "all.qh" - #if defined(CSQC) #include #include "../constants.qh" @@ -19,6 +18,7 @@ #include #elif defined(MENUQC) #elif defined(SVQC) + #include #include #include #include @@ -37,14 +37,14 @@ #include #include "../notifications/all.qh" #include "../deathtypes/all.qh" - #include + #include #include "../mapinfo.qh" - #include + #include #include #include #include #endif -#ifndef MENUQC +#ifdef GAMEQC #include "calculations.qc" #endif #ifdef SVQC @@ -275,7 +275,7 @@ string W_Model(string w_mdl) return M_ARGV(1, string); } -#ifndef MENUQC +#ifdef GAMEQC vector shotorg_adjustfromclient(vector vecs, float y_is_right, float algn) { switch (algn) @@ -547,7 +547,7 @@ void CL_WeaponEntity_SetModel(entity this, string name, bool _anim) } #endif -#ifndef MENUQC +#ifdef GAMEQC REGISTER_NET_TEMP(wframe) #ifdef CSQC diff --git a/qcsrc/common/weapons/all.qh b/qcsrc/common/weapons/all.qh index a3f7eb4d0f..32b03a16be 100644 --- a/qcsrc/common/weapons/all.qh +++ b/qcsrc/common/weapons/all.qh @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include "config.qh" @@ -18,7 +18,7 @@ WepSet ReadWepSet(); #include "weapon.qh" -#ifndef MENUQC +#ifdef GAMEQC #include "calculations.qh" #include #endif @@ -322,7 +322,7 @@ STATIC_INIT(register_weapons_done) weaponorder_byid = strzone(substring(weaponorder_byid, 1, -1)); } -#ifndef MENUQC +#ifdef GAMEQC .entity weaponchild; .entity exteriorweaponentity; diff --git a/qcsrc/common/weapons/calculations.qc b/qcsrc/common/weapons/calculations.qc index 73d9b938ef..c35b1930c4 100644 --- a/qcsrc/common/weapons/calculations.qc +++ b/qcsrc/common/weapons/calculations.qc @@ -1,3 +1,5 @@ +#include "calculations.qh" + // ============================= // Explosion Force Calculation // ============================= diff --git a/qcsrc/common/weapons/calculations.qh b/qcsrc/common/weapons/calculations.qh index 05eb9d9baf..c349eeca4f 100644 --- a/qcsrc/common/weapons/calculations.qh +++ b/qcsrc/common/weapons/calculations.qh @@ -3,3 +3,4 @@ vector damage_explosion_calcpush(vector explosion_f, vector target_v, float speedfactor); vector W_CalculateSpread(vector forward, float spread, float spreadfactor, float spreadstyle); int W_GetGunAlignment(entity player); +float explosion_calcpush_getmultiplier(vector explosion_v, vector target_v); diff --git a/qcsrc/common/weapons/config.qc b/qcsrc/common/weapons/config.qc index 26a05d10ac..4f6177b478 100644 --- a/qcsrc/common/weapons/config.qc +++ b/qcsrc/common/weapons/config.qc @@ -1,8 +1,8 @@ +#include "config.qh" #if defined(CSQC) #elif defined(MENUQC) #elif defined(SVQC) #include "../util.qh" - #include "config.qh" #include "all.qh" #endif diff --git a/qcsrc/common/weapons/weapon.qh b/qcsrc/common/weapons/weapon.qh index a08be2e61d..7b2f4b5b7a 100644 --- a/qcsrc/common/weapons/weapon.qh +++ b/qcsrc/common/weapons/weapon.qh @@ -130,11 +130,11 @@ CLASS(Weapon, Object) } ENDCLASS(Weapon) -#include +#include CLASS(WeaponPickup, Pickup) ATTRIB(WeaponPickup, m_weapon, Weapon); ATTRIB(WeaponPickup, m_name, string); -#ifndef MENUQC +#ifdef GAMEQC ATTRIB(WeaponPickup, m_sound, Sound, SND_WEAPONPICKUP); #endif #ifdef SVQC @@ -146,7 +146,7 @@ CLASS(WeaponPickup, Pickup) CONSTRUCT(WeaponPickup); this.m_weapon = w; this.m_name = w.m_name; -#ifndef MENUQC +#ifdef GAMEQC this.m_model = w.m_model; #endif #ifdef SVQC @@ -175,11 +175,6 @@ ENDCLASS(OffhandWeapon) const int MAX_SHOT_DISTANCE = 32768; -// weapon pickup ratings for bot logic -const int BOT_PICKUP_RATING_LOW = 2500; -const int BOT_PICKUP_RATING_MID = 5000; -const int BOT_PICKUP_RATING_HIGH = 10000; - // weapon flags const int WEP_TYPE_OTHER = 0x00; // not for damaging people const int WEP_TYPE_SPLASH = 0x01; // splash damage diff --git a/qcsrc/common/weapons/weapon/arc.qc b/qcsrc/common/weapons/weapon/arc.qc index c0d85831ae..1f8b8e7f08 100644 --- a/qcsrc/common/weapons/weapon/arc.qc +++ b/qcsrc/common/weapons/weapon/arc.qc @@ -1,3 +1,4 @@ +#include "arc.qh" #ifndef IMPLEMENTATION CLASS(Arc, Weapon) /* ammotype */ ATTRIB(Arc, ammo_field, .int, ammo_cells); @@ -6,7 +7,7 @@ CLASS(Arc, Weapon) /* rating */ ATTRIB(Arc, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH); /* color */ ATTRIB(Arc, wpcolor, vector, '1 1 1'); /* modelname */ ATTRIB(Arc, mdl, string, "arc"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Arc, m_model, Model, MDL_ARC_ITEM); #endif /* crosshair */ ATTRIB(Arc, w_crosshair, string, "gfx/crosshairhlac"); @@ -74,7 +75,7 @@ ENDCLASS(Arc) REGISTER_WEAPON(ARC, arc, NEW(Arc)); -#ifndef MENUQC +#ifdef GAMEQC const float ARC_MAX_SEGMENTS = 20; vector arc_shotorigin[4]; .vector beam_start; @@ -1397,7 +1398,7 @@ NET_HANDLE(ENT_CLIENT_ARC_BEAM, bool isnew) { this.beam_type = ReadByte(); - vector beamcolor = ((autocvar_cl_arcbeam_teamcolor) ? colormapPaletteColor(stof(getplayerkeyvalue(this.sv_entnum - 1, "colors")) & 0x0F, true) : '1 1 1'); + vector beamcolor = ((autocvar_cl_arcbeam_teamcolor) ? colormapPaletteColor(entcs_GetClientColors(this.sv_entnum - 1) & 0x0F, true) : '1 1 1'); switch(this.beam_type) { case ARC_BT_MISS: diff --git a/qcsrc/common/weapons/weapon/arc.qh b/qcsrc/common/weapons/weapon/arc.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/arc.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/blaster.qc b/qcsrc/common/weapons/weapon/blaster.qc index 791b574817..2f24e9e254 100644 --- a/qcsrc/common/weapons/weapon/blaster.qc +++ b/qcsrc/common/weapons/weapon/blaster.qc @@ -1,3 +1,4 @@ +#include "blaster.qh" #ifndef IMPLEMENTATION CLASS(Blaster, Weapon) /* ammotype */ //ATTRIB(Blaster, ammo_field, .int, ammo_none); @@ -6,7 +7,7 @@ CLASS(Blaster, Weapon) /* rating */ ATTRIB(Blaster, bot_pickupbasevalue, float, 0); /* color */ ATTRIB(Blaster, wpcolor, vector, '1 0.5 0.5'); /* modelname */ ATTRIB(Blaster, mdl, string, "laser"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Blaster, m_model, Model, MDL_BLASTER_ITEM); #endif /* crosshair */ ATTRIB(Blaster, w_crosshair, string, "gfx/crosshairlaser"); diff --git a/qcsrc/common/weapons/weapon/blaster.qh b/qcsrc/common/weapons/weapon/blaster.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/blaster.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/crylink.qc b/qcsrc/common/weapons/weapon/crylink.qc index 8865dbd02f..be5b2ec28f 100644 --- a/qcsrc/common/weapons/weapon/crylink.qc +++ b/qcsrc/common/weapons/weapon/crylink.qc @@ -1,3 +1,4 @@ +#include "crylink.qh" #ifndef IMPLEMENTATION CLASS(Crylink, Weapon) /* ammotype */ ATTRIB(Crylink, ammo_field, .int, ammo_cells); @@ -6,7 +7,7 @@ CLASS(Crylink, Weapon) /* rating */ ATTRIB(Crylink, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID); /* color */ ATTRIB(Crylink, wpcolor, vector, '1 0.5 1'); /* modelname */ ATTRIB(Crylink, mdl, string, "crylink"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Crylink, m_model, Model, MDL_CRYLINK_ITEM); #endif /* crosshair */ ATTRIB(Crylink, w_crosshair, string, "gfx/crosshaircrylink"); diff --git a/qcsrc/common/weapons/weapon/crylink.qh b/qcsrc/common/weapons/weapon/crylink.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/crylink.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/devastator.qc b/qcsrc/common/weapons/weapon/devastator.qc index e02a474c2d..5ffe08bad9 100644 --- a/qcsrc/common/weapons/weapon/devastator.qc +++ b/qcsrc/common/weapons/weapon/devastator.qc @@ -1,3 +1,4 @@ +#include "devastator.qh" #ifndef IMPLEMENTATION CLASS(Devastator, Weapon) /* ammotype */ ATTRIB(Devastator, ammo_field, .int, ammo_rockets); @@ -6,7 +7,7 @@ CLASS(Devastator, Weapon) /* rating */ ATTRIB(Devastator, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH); /* color */ ATTRIB(Devastator, wpcolor, vector, '1 1 0'); /* modelname */ ATTRIB(Devastator, mdl, string, "rl"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Devastator, m_model, Model, MDL_DEVASTATOR_ITEM); #endif /* crosshair */ ATTRIB(Devastator, w_crosshair, string, "gfx/crosshairrocketlauncher"); diff --git a/qcsrc/common/weapons/weapon/devastator.qh b/qcsrc/common/weapons/weapon/devastator.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/devastator.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/electro.qc b/qcsrc/common/weapons/weapon/electro.qc index 2c74b3b451..bd6fb89694 100644 --- a/qcsrc/common/weapons/weapon/electro.qc +++ b/qcsrc/common/weapons/weapon/electro.qc @@ -1,3 +1,4 @@ +#include "electro.qh" #ifndef IMPLEMENTATION CLASS(Electro, Weapon) /* ammotype */ ATTRIB(Electro, ammo_field, .int, ammo_cells); @@ -6,7 +7,7 @@ CLASS(Electro, Weapon) /* rating */ ATTRIB(Electro, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID); /* color */ ATTRIB(Electro, wpcolor, vector, '0 0.5 1'); /* modelname */ ATTRIB(Electro, mdl, string, "electro"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Electro, m_model, Model, MDL_ELECTRO_ITEM); #endif /* crosshair */ ATTRIB(Electro, w_crosshair, string, "gfx/crosshairelectro"); diff --git a/qcsrc/common/weapons/weapon/electro.qh b/qcsrc/common/weapons/weapon/electro.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/electro.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/fireball.qc b/qcsrc/common/weapons/weapon/fireball.qc index 8d17a247d4..db87ee445f 100644 --- a/qcsrc/common/weapons/weapon/fireball.qc +++ b/qcsrc/common/weapons/weapon/fireball.qc @@ -1,3 +1,4 @@ +#include "fireball.qh" #ifndef IMPLEMENTATION CLASS(Fireball, Weapon) /* ammotype */ //ATTRIB(Fireball, ammo_field, .int, ammo_none); @@ -6,7 +7,7 @@ CLASS(Fireball, Weapon) /* rating */ ATTRIB(Fireball, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID); /* color */ ATTRIB(Fireball, wpcolor, vector, '1 0.5 0'); /* modelname */ ATTRIB(Fireball, mdl, string, "fireball"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Fireball, m_model, Model, MDL_FIREBALL_ITEM); #endif /* crosshair */ ATTRIB(Fireball, w_crosshair, string, "gfx/crosshairfireball"); diff --git a/qcsrc/common/weapons/weapon/fireball.qh b/qcsrc/common/weapons/weapon/fireball.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/fireball.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/hagar.qc b/qcsrc/common/weapons/weapon/hagar.qc index c83203272d..ece4b35895 100644 --- a/qcsrc/common/weapons/weapon/hagar.qc +++ b/qcsrc/common/weapons/weapon/hagar.qc @@ -1,3 +1,4 @@ +#include "hagar.qh" #ifndef IMPLEMENTATION CLASS(Hagar, Weapon) /* ammotype */ ATTRIB(Hagar, ammo_field, .int, ammo_rockets); @@ -6,7 +7,7 @@ CLASS(Hagar, Weapon) /* rating */ ATTRIB(Hagar, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID); /* color */ ATTRIB(Hagar, wpcolor, vector, '1 1 0.5'); /* modelname */ ATTRIB(Hagar, mdl, string, "hagar"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Hagar, m_model, Model, MDL_HAGAR_ITEM); #endif /* crosshair */ ATTRIB(Hagar, w_crosshair, string, "gfx/crosshairhagar"); diff --git a/qcsrc/common/weapons/weapon/hagar.qh b/qcsrc/common/weapons/weapon/hagar.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/hagar.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/hlac.qc b/qcsrc/common/weapons/weapon/hlac.qc index b545f6ce1e..c77ccc98e4 100644 --- a/qcsrc/common/weapons/weapon/hlac.qc +++ b/qcsrc/common/weapons/weapon/hlac.qc @@ -1,3 +1,4 @@ +#include "hlac.qh" #ifndef IMPLEMENTATION CLASS(HLAC, Weapon) /* ammotype */ ATTRIB(HLAC, ammo_field, .int, ammo_cells); @@ -6,7 +7,7 @@ CLASS(HLAC, Weapon) /* rating */ ATTRIB(HLAC, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID); /* color */ ATTRIB(HLAC, wpcolor, vector, '0 1 0'); /* modelname */ ATTRIB(HLAC, mdl, string, "hlac"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(HLAC, m_model, Model, MDL_HLAC_ITEM); #endif /* crosshair */ ATTRIB(HLAC, w_crosshair, string, "gfx/crosshairhlac"); diff --git a/qcsrc/common/weapons/weapon/hlac.qh b/qcsrc/common/weapons/weapon/hlac.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/hlac.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/hook.qc b/qcsrc/common/weapons/weapon/hook.qc index 5e8119e6a6..b2c3174599 100644 --- a/qcsrc/common/weapons/weapon/hook.qc +++ b/qcsrc/common/weapons/weapon/hook.qc @@ -1,3 +1,4 @@ +#include "hook.qh" #ifndef IMPLEMENTATION CLASS(Hook, Weapon) /* ammotype */ ATTRIB(Hook, ammo_field, .int, ammo_fuel); @@ -6,7 +7,7 @@ CLASS(Hook, Weapon) /* rating */ ATTRIB(Hook, bot_pickupbasevalue, float, 0); /* color */ ATTRIB(Hook, wpcolor, vector, '0 0.5 0'); /* modelname */ ATTRIB(Hook, mdl, string, "hookgun"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Hook, m_model, Model, MDL_HOOK_ITEM); #endif /* crosshair */ ATTRIB(Hook, w_crosshair, string, "gfx/crosshairhook"); diff --git a/qcsrc/common/weapons/weapon/hook.qh b/qcsrc/common/weapons/weapon/hook.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/hook.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/machinegun.qc b/qcsrc/common/weapons/weapon/machinegun.qc index e3d1da2fea..81da28ddb0 100644 --- a/qcsrc/common/weapons/weapon/machinegun.qc +++ b/qcsrc/common/weapons/weapon/machinegun.qc @@ -1,3 +1,4 @@ +#include "machinegun.qh" #ifndef IMPLEMENTATION CLASS(MachineGun, Weapon) /* ammotype */ ATTRIB(MachineGun, ammo_field, .int, ammo_nails); @@ -6,7 +7,7 @@ CLASS(MachineGun, Weapon) /* rating */ ATTRIB(MachineGun, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID); /* color */ ATTRIB(MachineGun, wpcolor, vector, '1 1 0'); /* modelname */ ATTRIB(MachineGun, mdl, string, "uzi"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(MachineGun, m_model, Model, MDL_MACHINEGUN_ITEM); #endif /* crosshair */ ATTRIB(MachineGun, w_crosshair, string, "gfx/crosshairuzi"); diff --git a/qcsrc/common/weapons/weapon/machinegun.qh b/qcsrc/common/weapons/weapon/machinegun.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/machinegun.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/minelayer.qc b/qcsrc/common/weapons/weapon/minelayer.qc index 3021f47454..a3b356954a 100644 --- a/qcsrc/common/weapons/weapon/minelayer.qc +++ b/qcsrc/common/weapons/weapon/minelayer.qc @@ -1,3 +1,4 @@ +#include "minelayer.qh" #ifndef IMPLEMENTATION CLASS(MineLayer, Weapon) /* ammotype */ ATTRIB(MineLayer, ammo_field, .int, ammo_rockets); @@ -6,7 +7,7 @@ CLASS(MineLayer, Weapon) /* rating */ ATTRIB(MineLayer, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH); /* color */ ATTRIB(MineLayer, wpcolor, vector, '0.75 1 0'); /* modelname */ ATTRIB(MineLayer, mdl, string, "minelayer"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(MineLayer, m_model, Model, MDL_MINELAYER_ITEM); #endif /* crosshair */ ATTRIB(MineLayer, w_crosshair, string, "gfx/crosshairminelayer"); diff --git a/qcsrc/common/weapons/weapon/minelayer.qh b/qcsrc/common/weapons/weapon/minelayer.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/minelayer.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/mortar.qc b/qcsrc/common/weapons/weapon/mortar.qc index 0a457f1a6b..81be156541 100644 --- a/qcsrc/common/weapons/weapon/mortar.qc +++ b/qcsrc/common/weapons/weapon/mortar.qc @@ -1,3 +1,4 @@ +#include "mortar.qh" #ifndef IMPLEMENTATION CLASS(Mortar, Weapon) /* ammotype */ ATTRIB(Mortar, ammo_field, .int, ammo_rockets); @@ -6,7 +7,7 @@ CLASS(Mortar, Weapon) /* rating */ ATTRIB(Mortar, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID); /* color */ ATTRIB(Mortar, wpcolor, vector, '1 0 0'); /* modelname */ ATTRIB(Mortar, mdl, string, "gl"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Mortar, m_model, Model, MDL_MORTAR_ITEM); #endif /* crosshair */ ATTRIB(Mortar, w_crosshair, string, "gfx/crosshairgrenadelauncher"); diff --git a/qcsrc/common/weapons/weapon/mortar.qh b/qcsrc/common/weapons/weapon/mortar.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/mortar.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/porto.qc b/qcsrc/common/weapons/weapon/porto.qc index b5bc00353e..8a689e0510 100644 --- a/qcsrc/common/weapons/weapon/porto.qc +++ b/qcsrc/common/weapons/weapon/porto.qc @@ -1,3 +1,4 @@ +#include "porto.qh" #ifndef IMPLEMENTATION CLASS(PortoLaunch, Weapon) /* ammotype */ ATTRIB(PortoLaunch, ammo_field, .int, ammo_none); @@ -6,7 +7,7 @@ CLASS(PortoLaunch, Weapon) /* rating */ ATTRIB(PortoLaunch, bot_pickupbasevalue, float, 0); /* color */ ATTRIB(PortoLaunch, wpcolor, vector, '0.5 0.5 0.5'); /* modelname */ ATTRIB(PortoLaunch, mdl, string, "porto"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(PortoLaunch, m_model, Model, MDL_PORTO_ITEM); #endif /* crosshair */ ATTRIB(PortoLaunch, w_crosshair, string, "gfx/crosshairporto"); diff --git a/qcsrc/common/weapons/weapon/porto.qh b/qcsrc/common/weapons/weapon/porto.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/porto.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/rifle.qc b/qcsrc/common/weapons/weapon/rifle.qc index 1b6faee786..478a315253 100644 --- a/qcsrc/common/weapons/weapon/rifle.qc +++ b/qcsrc/common/weapons/weapon/rifle.qc @@ -1,3 +1,4 @@ +#include "rifle.qh" #ifndef IMPLEMENTATION CLASS(Rifle, Weapon) /* ammotype */ ATTRIB(Rifle, ammo_field, .int, ammo_nails); @@ -6,7 +7,7 @@ CLASS(Rifle, Weapon) /* rating */ ATTRIB(Rifle, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID); /* color */ ATTRIB(Rifle, wpcolor, vector, '0.5 1 0'); /* modelname */ ATTRIB(Rifle, mdl, string, "campingrifle"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Rifle, m_model, Model, MDL_RIFLE_ITEM); #endif /* crosshair */ ATTRIB(Rifle, w_crosshair, string, "gfx/crosshairrifle"); diff --git a/qcsrc/common/weapons/weapon/rifle.qh b/qcsrc/common/weapons/weapon/rifle.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/rifle.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/seeker.qc b/qcsrc/common/weapons/weapon/seeker.qc index 9f0a326ff2..850659069a 100644 --- a/qcsrc/common/weapons/weapon/seeker.qc +++ b/qcsrc/common/weapons/weapon/seeker.qc @@ -1,3 +1,4 @@ +#include "seeker.qh" #ifndef IMPLEMENTATION CLASS(Seeker, Weapon) /* ammotype */ ATTRIB(Seeker, ammo_field, .int, ammo_rockets); @@ -6,7 +7,7 @@ CLASS(Seeker, Weapon) /* rating */ ATTRIB(Seeker, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH); /* color */ ATTRIB(Seeker, wpcolor, vector, '0.5 1 0'); /* modelname */ ATTRIB(Seeker, mdl, string, "seeker"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Seeker, m_model, Model, MDL_SEEKER_ITEM); #endif /* crosshair */ ATTRIB(Seeker, w_crosshair, string, "gfx/crosshairseeker"); diff --git a/qcsrc/common/weapons/weapon/seeker.qh b/qcsrc/common/weapons/weapon/seeker.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/seeker.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/shockwave.qc b/qcsrc/common/weapons/weapon/shockwave.qc index de7726f24f..28cbe69bbe 100644 --- a/qcsrc/common/weapons/weapon/shockwave.qc +++ b/qcsrc/common/weapons/weapon/shockwave.qc @@ -1,3 +1,4 @@ +#include "shockwave.qh" #ifndef IMPLEMENTATION CLASS(Shockwave, Weapon) /* ammotype */ //ATTRIB(Shockwave, ammo_field, .int, ammo_none); @@ -6,7 +7,7 @@ CLASS(Shockwave, Weapon) /* rating */ ATTRIB(Shockwave, bot_pickupbasevalue, float, BOT_PICKUP_RATING_LOW); /* color */ ATTRIB(Shockwave, wpcolor, vector, '0.5 0.25 0'); /* modelname */ ATTRIB(Shockwave, mdl, string, "shotgun"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Shockwave, m_model, Model, MDL_SHOCKWAVE_ITEM); #endif /* crosshair */ ATTRIB(Shockwave, w_crosshair, string, "gfx/crosshairshotgun"); diff --git a/qcsrc/common/weapons/weapon/shockwave.qh b/qcsrc/common/weapons/weapon/shockwave.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/shockwave.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/shotgun.qc b/qcsrc/common/weapons/weapon/shotgun.qc index 9c7aff3aca..9ae0c6069a 100644 --- a/qcsrc/common/weapons/weapon/shotgun.qc +++ b/qcsrc/common/weapons/weapon/shotgun.qc @@ -1,3 +1,4 @@ +#include "shotgun.qh" #ifndef IMPLEMENTATION CLASS(Shotgun, Weapon) /* ammotype */ ATTRIB(Shotgun, ammo_field, .int, ammo_shells); @@ -6,7 +7,7 @@ CLASS(Shotgun, Weapon) /* rating */ ATTRIB(Shotgun, bot_pickupbasevalue, float, BOT_PICKUP_RATING_LOW); /* color */ ATTRIB(Shotgun, wpcolor, vector, '0.5 0.25 0'); /* modelname */ ATTRIB(Shotgun, mdl, string, "shotgun"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Shotgun, m_model, Model, MDL_SHOTGUN_ITEM); #endif /* crosshair */ ATTRIB(Shotgun, w_crosshair, string, "gfx/crosshairshotgun"); diff --git a/qcsrc/common/weapons/weapon/shotgun.qh b/qcsrc/common/weapons/weapon/shotgun.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/shotgun.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/tuba.qc b/qcsrc/common/weapons/weapon/tuba.qc index 31d2bf7a62..546e59f241 100644 --- a/qcsrc/common/weapons/weapon/tuba.qc +++ b/qcsrc/common/weapons/weapon/tuba.qc @@ -1,3 +1,4 @@ +#include "tuba.qh" #ifndef IMPLEMENTATION CLASS(Tuba, Weapon) /* impulse */ ATTRIB(Tuba, impulse, int, 1); @@ -5,7 +6,7 @@ CLASS(Tuba, Weapon) /* rating */ ATTRIB(Tuba, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID); /* color */ ATTRIB(Tuba, wpcolor, vector, '0 1 0'); /* modelname */ ATTRIB(Tuba, mdl, string, "tuba"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Tuba, m_model, Model, MDL_TUBA_ITEM); #endif /* crosshair */ ATTRIB(Tuba, w_crosshair, string, "gfx/crosshairtuba"); diff --git a/qcsrc/common/weapons/weapon/tuba.qh b/qcsrc/common/weapons/weapon/tuba.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/tuba.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/vaporizer.qc b/qcsrc/common/weapons/weapon/vaporizer.qc index 8fa43c1d8c..d9c215e24b 100644 --- a/qcsrc/common/weapons/weapon/vaporizer.qc +++ b/qcsrc/common/weapons/weapon/vaporizer.qc @@ -1,3 +1,4 @@ +#include "vaporizer.qh" #ifndef IMPLEMENTATION CLASS(Vaporizer, Weapon) /* ammotype */ ATTRIB(Vaporizer, ammo_field, .int, ammo_cells); @@ -6,7 +7,7 @@ CLASS(Vaporizer, Weapon) /* rating */ ATTRIB(Vaporizer, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH); /* color */ ATTRIB(Vaporizer, wpcolor, vector, '0.5 1 1'); /* modelname */ ATTRIB(Vaporizer, mdl, string, "minstanex"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Vaporizer, m_model, Model, MDL_VAPORIZER_ITEM); #endif /* crosshair */ ATTRIB(Vaporizer, w_crosshair, string, "gfx/crosshairminstanex"); @@ -111,7 +112,7 @@ void VaporizerBeam_Draw(entity this) //entity e = CSQCModel_server2csqc(this.sv_entnum - 1); //if (e == NULL) //{ - rgb = colormapPaletteColor(stof(getplayerkeyvalue(this.sv_entnum - 1, "colors")) & 0x0F, true); + rgb = colormapPaletteColor(entcs_GetClientColors(this.sv_entnum - 1) & 0x0F, true); //rgb = '1 1 1'; //} //else diff --git a/qcsrc/common/weapons/weapon/vaporizer.qh b/qcsrc/common/weapons/weapon/vaporizer.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/vaporizer.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/vortex.qc b/qcsrc/common/weapons/weapon/vortex.qc index 0ca635b3ee..3e2c1a094e 100644 --- a/qcsrc/common/weapons/weapon/vortex.qc +++ b/qcsrc/common/weapons/weapon/vortex.qc @@ -1,3 +1,4 @@ +#include "vortex.qh" #ifndef IMPLEMENTATION CLASS(Vortex, Weapon) /* ammotype */ ATTRIB(Vortex, ammo_field, .int, ammo_cells); @@ -6,7 +7,7 @@ CLASS(Vortex, Weapon) /* rating */ ATTRIB(Vortex, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH); /* color */ ATTRIB(Vortex, wpcolor, vector, '0.5 1 1'); /* modelname */ ATTRIB(Vortex, mdl, string, "nex"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Vortex, m_model, Model, MDL_VORTEX_ITEM); #endif /* crosshair */ ATTRIB(Vortex, w_crosshair, string, "gfx/crosshairnex"); diff --git a/qcsrc/common/weapons/weapon/vortex.qh b/qcsrc/common/weapons/weapon/vortex.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/vortex.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/dpdefs/post.qh b/qcsrc/dpdefs/post.qh index 9419dceea1..16fd934503 100644 --- a/qcsrc/dpdefs/post.qh +++ b/qcsrc/dpdefs/post.qh @@ -8,6 +8,7 @@ #undef objerror #undef remove #undef walkmove +#undef setcolor #ifdef MENUQC #define NULL (0, null_entity) diff --git a/qcsrc/dpdefs/pre.qh b/qcsrc/dpdefs/pre.qh index 801b8731bc..63cebbc1ac 100644 --- a/qcsrc/dpdefs/pre.qh +++ b/qcsrc/dpdefs/pre.qh @@ -8,3 +8,4 @@ #define objerror builtin_objerror #define remove builtin_remove #define walkmove builtin_walkmove +#define setcolor builtin_setcolor diff --git a/qcsrc/dpdefs/upstream/dpextensions.qc b/qcsrc/dpdefs/upstream/dpextensions.qc index 21060c82ef..2f49d97bb8 100644 --- a/qcsrc/dpdefs/upstream/dpextensions.qc +++ b/qcsrc/dpdefs/upstream/dpextensions.qc @@ -2599,3 +2599,12 @@ void coverage() = #642; // Reports a coverage event. The engine counts for each float(string url, float id, string content_type, string delim, float buf, float keyid) crypto_uri_postbuf = #513; //description: //use -1 as buffer handle to justs end delim as postdata + +//DP_USERMOVETYPES +//idea: divVerent +//darkplaces implementation: Mario +//movetype definitions: +float MOVETYPE_USER_FIRST = 128; +float MOVETYPE_USER_LAST = 191; +//description: +//user defined movetypes can be added between the start and end points, without producing unknown movetype warnings diff --git a/qcsrc/ecs/_lib.inc b/qcsrc/ecs/_lib.inc deleted file mode 100644 index 726f693e9e..0000000000 --- a/qcsrc/ecs/_lib.inc +++ /dev/null @@ -1,6 +0,0 @@ -#include "_lib.qh" - -#include "_mod.inc" -#include "components/_mod.inc" -#include "events/_mod.inc" -#include "systems/_mod.inc" diff --git a/qcsrc/ecs/_lib.qh b/qcsrc/ecs/_lib.qh deleted file mode 100644 index a617c73b09..0000000000 --- a/qcsrc/ecs/_lib.qh +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -/** Components always interpolate from the previous state */ -#define COMPONENT(com) \ - void com_##com##_interpolate(entity it, float a); \ - .bool com_##com - -#define FOREACH_COMPONENT(com, body) FOREACH_ENTITY_FLOAT(com_##com, true, body) - - -#define EVENT(T, args) .bool evt_##T##_listener; .void args evt_##T - -#define emit(T, ...) \ - MACRO_BEGIN \ - FOREACH_ENTITY_FLOAT_ORDERED(evt_##T##_listener, true, it.evt_##T(__VA_ARGS__)); \ - MACRO_END - -#define subscribe(listener, T, fn) \ - MACRO_BEGIN \ - listener.evt_##T = (fn); \ - listener.evt_##T##_listener = true; \ - MACRO_END - - -/** - * framelimit 0 is no limit, interpolation does not apply - * framerate below minfps will result in less than 100% speed - */ -#define SYSTEM(sys, frameLimit, minfps) \ - void sys_##sys##_update(entity this, float dt); \ - float autocvar_xon_sys_##sys##_dt = ((frameLimit) ? (1 / (frameLimit)) : 0); \ - float autocvar_xon_sys_##sys##_minfps = (1 / (1 / (minfps))) - -#define SYSTEM_UPDATE(sys) \ - MACRO_BEGIN \ - static float t = 0; \ - float dt = autocvar_xon_sys_##sys##_dt; \ - float minfps = autocvar_xon_sys_##sys##_minfps; \ - static float accumulator = 0; \ - float a = 0; \ - if (dt) { \ - accumulator += min(frametime, 1 / (minfps)); \ - } else { \ - accumulator += frametime; \ - dt = accumulator; \ - a = 1; \ - } \ - while (accumulator >= dt) \ - { \ - time = t; \ - FOREACH_COMPONENT(sys, sys_##sys##_update(it, dt)); \ - t += dt; \ - accumulator -= dt; \ - } \ - if (!a) a = accumulator / dt; \ - FOREACH_COMPONENT(sys, com_##sys##_interpolate(it, a)); \ - MACRO_END diff --git a/qcsrc/ecs/_mod.inc b/qcsrc/ecs/_mod.inc index 683c3a91d5..48b7069b24 100644 --- a/qcsrc/ecs/_mod.inc +++ b/qcsrc/ecs/_mod.inc @@ -1,2 +1,6 @@ // generated file; do not modify #include + +#include +#include +#include diff --git a/qcsrc/ecs/_mod.qh b/qcsrc/ecs/_mod.qh index 4d8bc34da3..0d1ff4482f 100644 --- a/qcsrc/ecs/_mod.qh +++ b/qcsrc/ecs/_mod.qh @@ -1,2 +1,6 @@ // generated file; do not modify #include + +#include +#include +#include diff --git a/qcsrc/ecs/lib.qh b/qcsrc/ecs/lib.qh new file mode 100644 index 0000000000..a617c73b09 --- /dev/null +++ b/qcsrc/ecs/lib.qh @@ -0,0 +1,57 @@ +#pragma once + +/** Components always interpolate from the previous state */ +#define COMPONENT(com) \ + void com_##com##_interpolate(entity it, float a); \ + .bool com_##com + +#define FOREACH_COMPONENT(com, body) FOREACH_ENTITY_FLOAT(com_##com, true, body) + + +#define EVENT(T, args) .bool evt_##T##_listener; .void args evt_##T + +#define emit(T, ...) \ + MACRO_BEGIN \ + FOREACH_ENTITY_FLOAT_ORDERED(evt_##T##_listener, true, it.evt_##T(__VA_ARGS__)); \ + MACRO_END + +#define subscribe(listener, T, fn) \ + MACRO_BEGIN \ + listener.evt_##T = (fn); \ + listener.evt_##T##_listener = true; \ + MACRO_END + + +/** + * framelimit 0 is no limit, interpolation does not apply + * framerate below minfps will result in less than 100% speed + */ +#define SYSTEM(sys, frameLimit, minfps) \ + void sys_##sys##_update(entity this, float dt); \ + float autocvar_xon_sys_##sys##_dt = ((frameLimit) ? (1 / (frameLimit)) : 0); \ + float autocvar_xon_sys_##sys##_minfps = (1 / (1 / (minfps))) + +#define SYSTEM_UPDATE(sys) \ + MACRO_BEGIN \ + static float t = 0; \ + float dt = autocvar_xon_sys_##sys##_dt; \ + float minfps = autocvar_xon_sys_##sys##_minfps; \ + static float accumulator = 0; \ + float a = 0; \ + if (dt) { \ + accumulator += min(frametime, 1 / (minfps)); \ + } else { \ + accumulator += frametime; \ + dt = accumulator; \ + a = 1; \ + } \ + while (accumulator >= dt) \ + { \ + time = t; \ + FOREACH_COMPONENT(sys, sys_##sys##_update(it, dt)); \ + t += dt; \ + accumulator -= dt; \ + } \ + if (!a) a = accumulator / dt; \ + FOREACH_COMPONENT(sys, com_##sys##_interpolate(it, a)); \ + MACRO_END diff --git a/qcsrc/ecs/main.qh b/qcsrc/ecs/main.qh index 9b7bf35238..724cb1ef89 100644 --- a/qcsrc/ecs/main.qh +++ b/qcsrc/ecs/main.qh @@ -1,3 +1,5 @@ #pragma once +#include "lib.qh" + void systems_update(); diff --git a/qcsrc/lib/_all.inc b/qcsrc/lib/_all.inc index 83a0ebf34a..b6c3c9e01b 100644 --- a/qcsrc/lib/_all.inc +++ b/qcsrc/lib/_all.inc @@ -266,7 +266,6 @@ void make_safe_for_remove(entity this); #endif #undef ENGINE_EVENT -#ifndef MENUQC - #include - #include +#ifdef GAMEQC + #include #endif diff --git a/qcsrc/lib/angle.qh b/qcsrc/lib/angle.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/lib/angle.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/lib/csqcmodel/_mod.inc b/qcsrc/lib/csqcmodel/_mod.inc index a2012c8fa2..cd9569e501 100644 --- a/qcsrc/lib/csqcmodel/_mod.inc +++ b/qcsrc/lib/csqcmodel/_mod.inc @@ -1,5 +1,13 @@ // generated file; do not modify -#include -#include #include -#include +#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif +#include +#ifdef CSQC + #include +#endif diff --git a/qcsrc/lib/csqcmodel/_mod.qh b/qcsrc/lib/csqcmodel/_mod.qh index de3102fa85..1b05351928 100644 --- a/qcsrc/lib/csqcmodel/_mod.qh +++ b/qcsrc/lib/csqcmodel/_mod.qh @@ -1,5 +1,13 @@ // generated file; do not modify -#include -#include #include -#include +#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif +#include +#ifdef CSQC + #include +#endif diff --git a/qcsrc/lib/csqcmodel/cl_model.qc b/qcsrc/lib/csqcmodel/cl_model.qc index 6b86092967..7f55a3c10f 100644 --- a/qcsrc/lib/csqcmodel/cl_model.qc +++ b/qcsrc/lib/csqcmodel/cl_model.qc @@ -194,6 +194,14 @@ void CSQCModel_Draw(entity this) CSQCModel_Hook_PreDraw(this, isplayer); + if(isplayer) + { + if(this.entnum == player_localentnum) + this.renderflags |= RF_EXTERNALMODEL; + else + this.renderflags &= ~RF_EXTERNALMODEL; + } + // inherit draw flags easily entity root = this; while(root.tag_entity) diff --git a/qcsrc/lib/csqcmodel/model.qc b/qcsrc/lib/csqcmodel/model.qc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/qcsrc/lib/csqcmodel/model.qh b/qcsrc/lib/csqcmodel/model.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/lib/csqcmodel/model.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/lib/csqcmodel/player.qc b/qcsrc/lib/csqcmodel/player.qc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/qcsrc/lib/csqcmodel/player.qh b/qcsrc/lib/csqcmodel/player.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/lib/csqcmodel/player.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/lib/defer.qh b/qcsrc/lib/defer.qh index c20c534407..4f34bb4853 100644 --- a/qcsrc/lib/defer.qh +++ b/qcsrc/lib/defer.qh @@ -1,6 +1,6 @@ #pragma once -#ifndef MENUQC +#ifdef GAMEQC #include "oo.qh" #include "self.qh" diff --git a/qcsrc/lib/intrusivelist.qh b/qcsrc/lib/intrusivelist.qh index 2927a62fe6..9d3ec54c8e 100644 --- a/qcsrc/lib/intrusivelist.qh +++ b/qcsrc/lib/intrusivelist.qh @@ -147,7 +147,7 @@ void IL_REMOVE(IntrusiveList this, entity it) /** * Delete the list */ -#define IL_DELETE(this, dtor) \ +#define IL_DELETE(this) \ MACRO_BEGIN \ { \ delete(this); \ diff --git a/qcsrc/lib/iter.qh b/qcsrc/lib/iter.qh index 7183e90fea..e3cf7410fb 100644 --- a/qcsrc/lib/iter.qh +++ b/qcsrc/lib/iter.qh @@ -159,7 +159,7 @@ MACRO_END .entity _FOREACH_ENTITY_FIND_flags_next; noref string _FOREACH_ENTITY_FIND_flags_mutex; #define FOREACH_ENTITY_FLAGS_UNORDERED(fld, match, body) _FOREACH_ENTITY_FIND_UNORDERED(, flags, fld, match, true, body) -#ifndef MENUQC +#ifdef GAMEQC entity(vector org, float rad, .entity tofield) _findchainradius_tofield = #22; #define FOREACH_ENTITY_RADIUS(org, dist, cond, body) ORDERED(FOREACH_ENTITY_RADIUS)(org, dist, cond, body) .entity _FOREACH_ENTITY_FIND_radius_next; noref string _FOREACH_ENTITY_FIND_radius_mutex; diff --git a/qcsrc/lib/json.qh b/qcsrc/lib/json.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/lib/json.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/lib/matrix/command.qc b/qcsrc/lib/matrix/command.qc index 27c7ec959c..449aa373b3 100644 --- a/qcsrc/lib/matrix/command.qc +++ b/qcsrc/lib/matrix/command.qc @@ -1,6 +1,6 @@ #include "command.qh" -#include +#include GENERIC_COMMAND(mx, "Send a matrix command") { switch (argv(1)) { diff --git a/qcsrc/lib/net.qh b/qcsrc/lib/net.qh index 106f00998c..86c15ec473 100644 --- a/qcsrc/lib/net.qh +++ b/qcsrc/lib/net.qh @@ -287,7 +287,7 @@ USING(Stream, int); #define Read_string() ReadString() #define Write_string(to, f) WriteString(to, f) -#ifndef MENUQC +#ifdef GAMEQC const float APPROXPASTTIME_ACCURACY_REQUIREMENT = 0.05; #define APPROXPASTTIME_MAX (16384 * APPROXPASTTIME_ACCURACY_REQUIREMENT) #define APPROXPASTTIME_RANGE (64 * APPROXPASTTIME_ACCURACY_REQUIREMENT) diff --git a/qcsrc/lib/replicate.qh b/qcsrc/lib/replicate.qh index f69b6072be..a36466d2e0 100644 --- a/qcsrc/lib/replicate.qh +++ b/qcsrc/lib/replicate.qh @@ -1,6 +1,6 @@ #pragma once -#ifndef MENUQC +#ifdef GAMEQC /** * Replicate a client cvar into a server field diff --git a/qcsrc/lib/self.qh b/qcsrc/lib/self.qh index bc545b0d0f..43bd12314e 100644 --- a/qcsrc/lib/self.qh +++ b/qcsrc/lib/self.qh @@ -66,13 +66,13 @@ SELFWRAP(think, void, (), (entity this), (this)) #define setthink(e, f) SELFWRAP_SET(think, e, f) #define getthink(e) SELFWRAP_GET(think, e) -#ifndef MENUQC +#ifdef GAMEQC SELFWRAP(touch, void, (), (entity this, entity toucher), (this, other)) #define settouch(e, f) SELFWRAP_SET(touch, e, f) #define gettouch(e) SELFWRAP_GET(touch, e) #endif -#ifndef MENUQC +#ifdef GAMEQC SELFWRAP(blocked, void, (), (entity this, entity blocker), (this, other)) #define setblocked(e, f) SELFWRAP_SET(blocked, e, f) #define blocked stopusingthis @@ -81,7 +81,7 @@ SELFWRAP(blocked, void, (), (entity this, entity blocker), (this, other)) SELFWRAP(predraw, void, (), (entity this), (this)) #define setpredraw(e, f) SELFWRAP_SET(predraw, e, f) -#ifndef MENUQC +#ifdef GAMEQC SELFWRAP(customizeentityforclient, bool, (), (entity this, entity client), (this, other)) #define setcefc(e, f) SELFWRAP_SET(customizeentityforclient, e, f) #define getcefc(e) SELFWRAP_GET(customizeentityforclient, e) @@ -104,6 +104,6 @@ SELFWRAP(SendEntity, bool, (entity to, int sendflags), (entity this, entity to, #define movetogoal(e, ...) (__self = (e), builtin_movetogoal(__VA_ARGS__)) #define walkmove(e, ...) (__self = (e), builtin_walkmove(__VA_ARGS__)) -#ifndef MENUQC +#ifdef GAMEQC void adaptor_think2use(entity this) { if (this.use) this.use(this, NULL, NULL); } #endif diff --git a/qcsrc/lib/vector.qh b/qcsrc/lib/vector.qh index 10e8ed8c43..bb363c12ef 100644 --- a/qcsrc/lib/vector.qh +++ b/qcsrc/lib/vector.qh @@ -142,7 +142,7 @@ vector vec_epsilon(vector this, float eps) #define ClipVelocity(in, normal, out, overbounce) \ (out = vec_epsilon(vec_reflect(in, normal, (overbounce) - 1), 0.1)) -#ifndef MENUQC +#ifdef GAMEQC vector get_corner_position(entity box, int corner) { switch (corner) diff --git a/qcsrc/lib/warpzone/common.qc b/qcsrc/lib/warpzone/common.qc index cff5be7589..90e3cd76c9 100644 --- a/qcsrc/lib/warpzone/common.qc +++ b/qcsrc/lib/warpzone/common.qc @@ -4,7 +4,7 @@ #include #elif defined(MENUQC) #elif defined(SVQC) - #include + #include #endif void WarpZone_Accumulator_Clear(entity acc) diff --git a/qcsrc/lib/warpzone/server.qc b/qcsrc/lib/warpzone/server.qc index de692ee819..34ea2610de 100644 --- a/qcsrc/lib/warpzone/server.qc +++ b/qcsrc/lib/warpzone/server.qc @@ -7,7 +7,7 @@ #include #include #include - #include + #include #include #include #endif diff --git a/qcsrc/menu/_all.inc b/qcsrc/menu/_all.inc new file mode 100644 index 0000000000..e5198f5916 --- /dev/null +++ b/qcsrc/menu/_all.inc @@ -0,0 +1,10 @@ +#include +#include "_mod.inc" + +#include "anim/_mod.inc" +#include "command/_mod.inc" +#include "item/_mod.inc" +#include "mutators/_mod.inc" +#include "xonotic/_mod.inc" + +#include diff --git a/qcsrc/menu/auto-super.pl b/qcsrc/menu/auto-super.pl deleted file mode 100644 index 00926d0617..0000000000 --- a/qcsrc/menu/auto-super.pl +++ /dev/null @@ -1,101 +0,0 @@ -my %classoffile = (); -my %classes = (); -my %baseclass = (); -my %methods = (); -my %attrs = (); -my %methodnames = (); -my %old2new = (); - -print STDERR "Scanning...\n"; -for my $f(@ARGV) -{ - open my $fh, '<', $f; - while(<$fh>) - { - if(/^CLASS\(([^)]*)\)(?:\s*EXTENDS\(([^)]*)\))?/) - { - $classes{$1} = defined($2) ? $2 : "Object"; - $classoffile{$f} = $1; - } - if(/^\s*METHOD\(([^),]*),\s*([^),]*)/) - { - $methods{$1}{$2} = $1; - $methodnames{"$1"."_"."$2"} = $f; - $old2new{"$2$1"} = "$1"."_"."$2"; - } - if(/^\s*ATTRIB(?:ARRAY)?\(([^),]*),\s*([^),]*)/) - { - $attrs{$1}{$2} = $1; - } - } - close $fh; -} - -# propagate down methods etc. -print STDERR "Propagating...\n"; -for my $class(keys %classes) -{ - print STDERR "$class"; - my $base = $class; - for(;;) - { - $base = $classes{$base}; - last if not defined $base; - print STDERR " -> $base"; - while(my ($method, $definingclass) = each %{$methods{$base}}) - { - $methods{$class}{$method} = $definingclass - if not defined $methods{$class}{$method}; - } - while(my ($attr, $definingclass) = each %{$attrs{$base}}) - { - $attrs{$class}{$attr} = $definingclass - if not defined $attrs{$class}{$attr}; - } - } - print STDERR "\n"; -} - -# change all calls to base method to super, complain about skipping -print STDERR "Fixing...\n"; -for my $f(@ARGV) -{ - open my $fh, '<', $f; - my $s = do { undef local $/; <$fh>; }; - my $s0 = $s; - close $fh; - - my $class = $classoffile{$f}; - my $base = $classes{$class}; - next if not defined $base; - - for(keys %old2new) - { - $s =~ s/\b$_\b/$old2new{$_}/g; - } - - my @methods_super = map { [ $methods{$base}{$_} . "_" . $_, "SUPER($class).$_" ]; } keys %{$methods{$base}}; - for(@methods_super) - { - my ($search, $replace) = @$_; - my $n = ($s =~ s/\b$search\b/$replace/g); - print STDERR "[$f] $search -> $replace... $n replacements\n" - if $n; - } - - for(grep { $methodnames{$_} ne $f } keys %methodnames) - { - if($s =~ /\b$_\b/) - { - print STDERR "[$f] calls non-super external method directly: $_\n"; - } - } - - if($s ne $s0) - { - print STDERR "Rewriting $f...\n"; - open my $fh, '>', $f; - print $fh $s; - close $fh; - } -} diff --git a/qcsrc/menu/command/_mod.inc b/qcsrc/menu/command/_mod.inc index e721f357a3..0bcef50de7 100644 --- a/qcsrc/menu/command/_mod.inc +++ b/qcsrc/menu/command/_mod.inc @@ -1,3 +1,2 @@ // generated file; do not modify -#include #include diff --git a/qcsrc/menu/command/_mod.qh b/qcsrc/menu/command/_mod.qh index 5e66557301..91c0a8f35e 100644 --- a/qcsrc/menu/command/_mod.qh +++ b/qcsrc/menu/command/_mod.qh @@ -1,3 +1,2 @@ // generated file; do not modify -#include #include diff --git a/qcsrc/menu/command/all.qc b/qcsrc/menu/command/all.qc deleted file mode 100644 index 2f8df96b6b..0000000000 --- a/qcsrc/menu/command/all.qc +++ /dev/null @@ -1,3 +0,0 @@ -#include "all.qh" - -#include diff --git a/qcsrc/menu/command/all.qh b/qcsrc/menu/command/all.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/menu/command/all.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/menu/command/menu_cmd.qc b/qcsrc/menu/command/menu_cmd.qc index 0f87cbb095..72aadf6247 100644 --- a/qcsrc/menu/command/menu_cmd.qc +++ b/qcsrc/menu/command/menu_cmd.qc @@ -5,7 +5,7 @@ #include "../mutators/events.qh" -#include +#include .entity firstChild, nextSibling; diff --git a/qcsrc/menu/menu.qc b/qcsrc/menu/menu.qc index 22934ca908..aaa33b137b 100644 --- a/qcsrc/menu/menu.qc +++ b/qcsrc/menu/menu.qc @@ -17,8 +17,8 @@ #include "xonotic/util.qh" -#include "../common/items/all.qh" -#include +#include "../common/items/_mod.qh" +#include #include "../common/mapinfo.qh" #include "../common/mutators/base.qh" diff --git a/qcsrc/menu/progs.inc b/qcsrc/menu/progs.inc index ad08af93dd..404c6f2e5a 100644 --- a/qcsrc/menu/progs.inc +++ b/qcsrc/menu/progs.inc @@ -1,18 +1,9 @@ #include #if XONOTIC - -#include "../menu/_mod.inc" -#include "anim/_mod.inc" -#include "command/_mod.inc" -#include "item/_mod.inc" -#include "mutators/_mod.inc" -#include "xonotic/_mod.inc" - -#include - +#include #endif -#if BUILD_MOD -#include "../../mod/menu/progs.inc" +#ifdef BUILD_MOD +#include #endif diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc b/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc index f2f7f5c8e4..07fe09c100 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc +++ b/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc @@ -1,5 +1,5 @@ #include "dialog_multiplayer_create_mutators.qh" -#include +#include #include "weaponarenacheckbox.qh" #include "checkbox.qh" diff --git a/qcsrc/menu/xonotic/keybinder.qc b/qcsrc/menu/xonotic/keybinder.qc index 27e488164b..2d093a2ed5 100644 --- a/qcsrc/menu/xonotic/keybinder.qc +++ b/qcsrc/menu/xonotic/keybinder.qc @@ -1,6 +1,6 @@ #include "keybinder.qh" -#include +#include .int flags; #include "button.qh" diff --git a/qcsrc/menu/xonotic/util.qc b/qcsrc/menu/xonotic/util.qc index 9cc4da8c38..ec76d389e6 100644 --- a/qcsrc/menu/xonotic/util.qc +++ b/qcsrc/menu/xonotic/util.qc @@ -7,7 +7,7 @@ #include #include #include -#include +#include float GL_CheckExtension(string ext) { diff --git a/qcsrc/menu/xonotic/weaponslist.qc b/qcsrc/menu/xonotic/weaponslist.qc index ed74631dea..7b3d7375b7 100644 --- a/qcsrc/menu/xonotic/weaponslist.qc +++ b/qcsrc/menu/xonotic/weaponslist.qc @@ -1,6 +1,6 @@ #include "weaponslist.qh" -#include +#include .bool disabled; diff --git a/qcsrc/server/_all.inc b/qcsrc/server/_all.inc new file mode 100644 index 0000000000..3359f13b12 --- /dev/null +++ b/qcsrc/server/_all.inc @@ -0,0 +1,18 @@ +#include +#include "_mod.inc" + +#include "bot/_mod.inc" +#include "command/_mod.inc" +#include "mutators/_mod.inc" +#include "pathlib/_mod.inc" +#include "weapons/_mod.inc" + +#include +#include + +#include + +#include +#include +#include +#include diff --git a/qcsrc/server/_mod.inc b/qcsrc/server/_mod.inc index f22742f1dd..4ecc610c7d 100644 --- a/qcsrc/server/_mod.inc +++ b/qcsrc/server/_mod.inc @@ -3,20 +3,20 @@ #include #include #include -#include -#include -#include +#include #include #include #include #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -25,7 +25,9 @@ #include #include #include -#include +#ifdef SVQC + #include +#endif #include #include #include diff --git a/qcsrc/server/_mod.qh b/qcsrc/server/_mod.qh index 17e0a829d4..8162606c78 100644 --- a/qcsrc/server/_mod.qh +++ b/qcsrc/server/_mod.qh @@ -3,20 +3,20 @@ #include #include #include -#include -#include -#include +#include #include #include #include #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -25,7 +25,9 @@ #include #include #include -#include +#ifdef SVQC + #include +#endif #include #include #include diff --git a/qcsrc/server/bot/_mod.inc b/qcsrc/server/bot/_mod.inc index 8f0672e262..7c0c2882e9 100644 --- a/qcsrc/server/bot/_mod.inc +++ b/qcsrc/server/bot/_mod.inc @@ -1,2 +1,5 @@ // generated file; do not modify #include + +#include +#include diff --git a/qcsrc/server/bot/_mod.qh b/qcsrc/server/bot/_mod.qh index 33f0b02291..3678fc69f1 100644 --- a/qcsrc/server/bot/_mod.qh +++ b/qcsrc/server/bot/_mod.qh @@ -1,2 +1,5 @@ // generated file; do not modify #include + +#include +#include diff --git a/qcsrc/server/bot/api.qc b/qcsrc/server/bot/api.qc index 85b0e46568..274b034033 100644 --- a/qcsrc/server/bot/api.qc +++ b/qcsrc/server/bot/api.qc @@ -1,49 +1 @@ #include "api.qh" - -#if 1 - -#include "default/_mod.inc" -#include "default/havocbot/_mod.inc" - -#else - -bool bot_aim(entity this, float shotspeed, float shotspeedupward, float maxshottime, float applygravity) { return false; } -void bot_clientconnect(entity this) { } -void bot_clientdisconnect(entity this) { } -void bot_cmdhelp(string scmd) { } -void bot_endgame() { } -bool bot_fixcount() { return true; } -void bot_list_commands() { } -void bot_queuecommand(entity bot, string cmdstring) { } -void bot_relinkplayerlist() { } -void bot_resetqueues() { } -void bot_serverframe() { } -bool bot_shouldattack(entity this, entity e) { return false; } -void bot_think(entity this) { } - -entity find_bot_by_name(string name) { return NULL; } -entity find_bot_by_number(float number) { return NULL; } - -void havocbot_goalrating_controlpoints(entity this, float ratingscale, vector org, float sradius) { } -void havocbot_goalrating_enemyplayers(entity this, float ratingscale, vector org, float sradius) { } -void havocbot_goalrating_items(entity this, float ratingscale, vector org, float sradius) { } - -entity navigation_findnearestwaypoint(entity ent, float walkfromwp) { return NULL; } -void navigation_goalrating_end(entity this) { } -void navigation_goalrating_start(entity this) { } -void navigation_markroutes(entity this, entity fixed_source_waypoint) { } -void navigation_markroutes_inverted(entity fixed_source_waypoint) { } -void navigation_routerating(entity this, entity e, float f, float rangebias) { } - -bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float movemode) { return false; } - -void waypoint_remove(entity e) { } -void waypoint_saveall() { } -void waypoint_schedulerelinkall() { } -void waypoint_schedulerelink(entity wp) { } -void waypoint_spawnforitem(entity e) { } -void waypoint_spawnforitem_force(entity e, vector org) { } -void waypoint_spawnforteleporter(entity e, vector destination, float timetaken) { } -void waypoint_spawnforteleporter_v(entity e, vector org, vector destination, float timetaken) { } -entity waypoint_spawn(vector m1, vector m2, float f) { return NULL; } -#endif diff --git a/qcsrc/server/bot/api.qh b/qcsrc/server/bot/api.qh index 9c525892f9..59af78cb08 100644 --- a/qcsrc/server/bot/api.qh +++ b/qcsrc/server/bot/api.qh @@ -1,6 +1,6 @@ #pragma once -#include +#include const int WAYPOINTFLAG_GENERATED = BIT(23); const int WAYPOINTFLAG_ITEM = BIT(22); diff --git a/qcsrc/server/bot/default/_mod.inc b/qcsrc/server/bot/default/_mod.inc index ec73502c3e..03fb7e0f5b 100644 --- a/qcsrc/server/bot/default/_mod.inc +++ b/qcsrc/server/bot/default/_mod.inc @@ -5,3 +5,5 @@ #include #include #include + +#include diff --git a/qcsrc/server/bot/default/_mod.qh b/qcsrc/server/bot/default/_mod.qh index 9252f195e6..04896279bf 100644 --- a/qcsrc/server/bot/default/_mod.qh +++ b/qcsrc/server/bot/default/_mod.qh @@ -5,3 +5,5 @@ #include #include #include + +#include diff --git a/qcsrc/server/bot/default/aim.qc b/qcsrc/server/bot/default/aim.qc index 15dfb0b9b4..1624676b82 100644 --- a/qcsrc/server/bot/default/aim.qc +++ b/qcsrc/server/bot/default/aim.qc @@ -9,7 +9,7 @@ #include "../../weapons/weaponsystem.qh" -#include "../../mutators/all.qh" +#include "../../mutators/_mod.qh" // traces multiple trajectories to find one that will impact the target // 'end' vector is the place it aims for, diff --git a/qcsrc/server/bot/default/bot.qc b/qcsrc/server/bot/default/bot.qc index cab38143db..502e2532c6 100644 --- a/qcsrc/server/bot/default/bot.qc +++ b/qcsrc/server/bot/default/bot.qc @@ -15,13 +15,13 @@ #include "../../antilag.qh" #include "../../autocvars.qh" #include "../../campaign.qh" -#include "../../cl_client.qh" +#include "../../client.qh" #include "../../constants.qh" #include "../../defs.qh" #include "../../race.qh" #include -#include "../../mutators/all.qh" +#include "../../mutators/_mod.qh" #include "../../weapons/accuracy.qh" @@ -31,7 +31,9 @@ #include #include -#include +#include + +#include #include diff --git a/qcsrc/server/bot/default/havocbot/havocbot.qc b/qcsrc/server/bot/default/havocbot/havocbot.qc index 5ccc80a3a0..d23c29675a 100644 --- a/qcsrc/server/bot/default/havocbot/havocbot.qc +++ b/qcsrc/server/bot/default/havocbot/havocbot.qc @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include diff --git a/qcsrc/server/bot/default/navigation.qc b/qcsrc/server/bot/default/navigation.qc index 1d18a582ed..51325f4f96 100644 --- a/qcsrc/server/bot/default/navigation.qc +++ b/qcsrc/server/bot/default/navigation.qc @@ -7,7 +7,7 @@ #include -#include +#include #include #include diff --git a/qcsrc/server/bot/null/_mod.inc b/qcsrc/server/bot/null/_mod.inc new file mode 100644 index 0000000000..d40f4512e9 --- /dev/null +++ b/qcsrc/server/bot/null/_mod.inc @@ -0,0 +1,2 @@ +// generated file; do not modify +#include diff --git a/qcsrc/server/bot/null/_mod.qh b/qcsrc/server/bot/null/_mod.qh new file mode 100644 index 0000000000..0449a1c43d --- /dev/null +++ b/qcsrc/server/bot/null/_mod.qh @@ -0,0 +1,2 @@ +// generated file; do not modify +#include diff --git a/qcsrc/server/bot/null/bot_null.qc b/qcsrc/server/bot/null/bot_null.qc new file mode 100644 index 0000000000..271252dee9 --- /dev/null +++ b/qcsrc/server/bot/null/bot_null.qc @@ -0,0 +1,43 @@ +#include "bot_null.qh" + +#if 0 +bool bot_aim(entity this, float shotspeed, float shotspeedupward, float maxshottime, float applygravity) { return false; } +void bot_clientconnect(entity this) { } +void bot_clientdisconnect(entity this) { } +void bot_cmdhelp(string scmd) { } +void bot_endgame() { } +bool bot_fixcount() { return true; } +void bot_list_commands() { } +void bot_queuecommand(entity bot, string cmdstring) { } +void bot_relinkplayerlist() { } +void bot_resetqueues() { } +void bot_serverframe() { } +bool bot_shouldattack(entity this, entity e) { return false; } +void bot_think(entity this) { } + +entity find_bot_by_name(string name) { return NULL; } +entity find_bot_by_number(float number) { return NULL; } + +void havocbot_goalrating_controlpoints(entity this, float ratingscale, vector org, float sradius) { } +void havocbot_goalrating_enemyplayers(entity this, float ratingscale, vector org, float sradius) { } +void havocbot_goalrating_items(entity this, float ratingscale, vector org, float sradius) { } + +entity navigation_findnearestwaypoint(entity ent, float walkfromwp) { return NULL; } +void navigation_goalrating_end(entity this) { } +void navigation_goalrating_start(entity this) { } +void navigation_markroutes(entity this, entity fixed_source_waypoint) { } +void navigation_markroutes_inverted(entity fixed_source_waypoint) { } +void navigation_routerating(entity this, entity e, float f, float rangebias) { } + +bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float movemode) { return false; } + +void waypoint_remove(entity e) { } +void waypoint_saveall() { } +void waypoint_schedulerelinkall() { } +void waypoint_schedulerelink(entity wp) { } +void waypoint_spawnforitem(entity e) { } +void waypoint_spawnforitem_force(entity e, vector org) { } +void waypoint_spawnforteleporter(entity e, vector destination, float timetaken) { } +void waypoint_spawnforteleporter_v(entity e, vector org, vector destination, float timetaken) { } +entity waypoint_spawn(vector m1, vector m2, float f) { return NULL; } +#endif diff --git a/qcsrc/server/bot/null/bot_null.qh b/qcsrc/server/bot/null/bot_null.qh new file mode 100644 index 0000000000..28709a2b2e --- /dev/null +++ b/qcsrc/server/bot/null/bot_null.qh @@ -0,0 +1,3 @@ +#pragma once + +#include "../api.qh" diff --git a/qcsrc/server/cheats.qc b/qcsrc/server/cheats.qc index f31b3e5c2f..71e0cd2e86 100644 --- a/qcsrc/server/cheats.qc +++ b/qcsrc/server/cheats.qc @@ -4,7 +4,7 @@ #include "race.qh" #include "../common/triggers/teleporters.qh" -#include "mutators/all.qh" +#include "mutators/_mod.qh" #include "weapons/tracing.qh" @@ -16,7 +16,7 @@ #include "../common/monsters/all.qh" -#include "../common/weapons/all.qh" +#include #include "../common/triggers/subs.qh" diff --git a/qcsrc/server/cl_client.qc b/qcsrc/server/cl_client.qc deleted file mode 100644 index c6eee3fb5f..0000000000 --- a/qcsrc/server/cl_client.qc +++ /dev/null @@ -1,2600 +0,0 @@ -#include "cl_client.qh" - -#include "anticheat.qh" -#include "cl_impulse.qh" -#include "cl_player.qh" -#include "ipban.qh" -#include "miscfunctions.qh" -#include "portals.qh" -#include "teamplay.qh" -#include "playerdemo.qh" -#include "spawnpoints.qh" -#include "g_damage.qh" -#include "g_hook.qh" -#include "command/common.qh" -#include "cheats.qh" -#include "g_world.qh" -#include "race.qh" -#include "antilag.qh" -#include "campaign.qh" -#include "command/common.qh" - -#include "bot/api.qh" - -#include "../common/ent_cs.qh" -#include - -#include - -#include "../common/triggers/teleporters.qh" - -#include "../common/vehicles/all.qh" - -#include "weapons/hitplot.qh" -#include "weapons/weaponsystem.qh" - -#include "../common/net_notice.qh" -#include "../common/physics/player.qh" - -#include "../common/items/all.qc" - -#include "../common/mutators/mutator/waypoints/all.qh" - -#include "../common/triggers/subs.qh" -#include "../common/triggers/triggers.qh" -#include "../common/triggers/trigger/secret.qh" - -#include "../common/minigames/sv_minigames.qh" - -#include "../common/items/inventory.qh" - -#include "../common/monsters/sv_monsters.qh" - -#include "../lib/warpzone/server.qh" - -STATIC_METHOD(Client, Add, void(Client this, int _team)) -{ - ClientConnect(this); - TRANSMUTE(Player, this); - this.frame = 12; // 7 - this.team = _team; - PutClientInServer(this); -} - -void PutObserverInServer(entity this); - -STATIC_METHOD(Client, Remove, void(Client this)) -{ - TRANSMUTE(Observer, this); - PutClientInServer(this); - ClientDisconnect(this); -} - -void send_CSQC_teamnagger() { - WriteHeader(MSG_BROADCAST, TE_CSQC_TEAMNAGGER); -} - -int CountSpectators(entity player, entity to) -{ - if(!player) { return 0; } // not sure how, but best to be safe - - int spec_count = 0; - - FOREACH_CLIENT(IS_REAL_CLIENT(it) && IS_SPEC(it) && it != to && it.enemy == player, - { - spec_count++; - }); - - return spec_count; -} - -void WriteSpectators(entity player, entity to) -{ - if(!player) { return; } // not sure how, but best to be safe - - FOREACH_CLIENT(IS_REAL_CLIENT(it) && IS_SPEC(it) && it != to && it.enemy == player, - { - WriteByte(MSG_ENTITY, num_for_edict(it)); - }); -} - -bool ClientData_Send(entity this, entity to, int sf) -{ - assert(to == this.owner, return false); - - entity e = to; - if (IS_SPEC(e)) e = e.enemy; - - sf = 0; - if (e.race_completed) sf |= 1; // forced scoreboard - if (to.spectatee_status) sf |= 2; // spectator ent number follows - if (e.zoomstate) sf |= 4; // zoomed - if (e.porto_v_angle_held) sf |= 8; // angles held - if (autocvar_sv_showspectators) sf |= 16; // show spectators - - WriteHeader(MSG_ENTITY, ENT_CLIENT_CLIENTDATA); - WriteByte(MSG_ENTITY, sf); - - if (sf & 2) - { - WriteByte(MSG_ENTITY, to.spectatee_status); - } - if (sf & 8) - { - WriteAngle(MSG_ENTITY, e.v_angle.x); - WriteAngle(MSG_ENTITY, e.v_angle.y); - } - - if(sf & 16) - { - float specs = CountSpectators(e, to); - WriteByte(MSG_ENTITY, specs); - WriteSpectators(e, to); - } - - return true; -} - -void ClientData_Attach(entity this) -{ - Net_LinkEntity(this.clientdata = new_pure(clientdata), false, 0, ClientData_Send); - this.clientdata.drawonlytoclient = this; - this.clientdata.owner = this; -} - -void ClientData_Detach(entity this) -{ - delete(this.clientdata); - this.clientdata = NULL; -} - -void ClientData_Touch(entity e) -{ - e.clientdata.SendFlags = 1; - - // make it spectatable - FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != e && IS_SPEC(it) && it.enemy == e, LAMBDA(it.clientdata.SendFlags = 1)); -} - -.string netname_previous; - -void SetSpectatee(entity this, entity spectatee); -void SetSpectatee_status(entity this, int spectatee_num); - - -/* -============= -CheckPlayerModel - -Checks if the argument string can be a valid playermodel. -Returns a valid one in doubt. -============= -*/ -string FallbackPlayerModel; -string CheckPlayerModel(string plyermodel) { - if(FallbackPlayerModel != cvar_defstring("_cl_playermodel")) - { - // note: we cannot summon Don Strunzone here, some player may - // still have the model string set. In case anyone manages how - // to change a cvar default, we'll have a small leak here. - FallbackPlayerModel = strzone(cvar_defstring("_cl_playermodel")); - } - // only in right path - if( substring(plyermodel,0,14) != "models/player/") - return FallbackPlayerModel; - // only good file extensions - if(substring(plyermodel,-4,4) != ".zym") - if(substring(plyermodel,-4,4) != ".dpm") - if(substring(plyermodel,-4,4) != ".iqm") - if(substring(plyermodel,-4,4) != ".md3") - if(substring(plyermodel,-4,4) != ".psk") - return FallbackPlayerModel; - // forbid the LOD models - if(substring(plyermodel, -9,5) == "_lod1") - return FallbackPlayerModel; - if(substring(plyermodel, -9,5) == "_lod2") - return FallbackPlayerModel; - if(plyermodel != strtolower(plyermodel)) - return FallbackPlayerModel; - // also, restrict to server models - if(autocvar_sv_servermodelsonly) - { - if(!fexists(plyermodel)) - return FallbackPlayerModel; - } - return plyermodel; -} - -void setplayermodel(entity e, string modelname) -{ - precache_model(modelname); - _setmodel(e, modelname); - player_setupanimsformodel(e); - if(!autocvar_g_debug_globalsounds) - UpdatePlayerSounds(e); -} - -void FixPlayermodel(entity player); -/** putting a client as observer in the server */ -void PutObserverInServer(entity this) -{ - bool mutator_returnvalue = MUTATOR_CALLHOOK(MakePlayerObserver, this); - PlayerState_detach(this); - - if (IS_PLAYER(this) && this.health >= 1) { - // despawn effect - Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1); - } - - { - entity spot = SelectSpawnPoint(this, true); - if (!spot) LOG_FATAL("No spawnpoints for observers?!?"); - this.angles = spot.angles; - this.angles_z = 0; - this.fixangle = true; - // offset it so that the spectator spawns higher off the ground, looks better this way - setorigin(this, spot.origin + STAT(PL_VIEW_OFS, NULL)); - this.prevorigin = this.origin; - if (IS_REAL_CLIENT(this)) - { - msg_entity = this; - WriteByte(MSG_ONE, SVC_SETVIEW); - WriteEntity(MSG_ONE, this); - } - // give the spectator some space between walls for MOVETYPE_FLY_WORLDONLY - // so that your view doesn't go into the ceiling with MOVETYPE_FLY_WORLDONLY, previously "PL_VIEW_OFS" - if(!autocvar_g_debug_globalsounds) - { - // needed for player sounds - this.model = ""; - FixPlayermodel(this); - } - setmodel(this, MDL_Null); - setsize(this, STAT(PL_CROUCH_MIN, NULL), STAT(PL_CROUCH_MAX, NULL)); - this.view_ofs = '0 0 0'; - } - - RemoveGrapplingHook(this); - Portal_ClearAll(this); - Unfreeze(this); - SetSpectatee(this, NULL); - - if (this.alivetime) - { - if (!warmup_stage) - PS_GR_P_ADDVAL(this, PLAYERSTATS_ALIVETIME, time - this.alivetime); - this.alivetime = 0; - } - - if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE); - - WaypointSprite_PlayerDead(this); - - if (mutator_returnvalue) { - // mutator prevents resetting teams+score - } else { - this.team = -1; // move this as it is needed to log the player spectating in eventlog - this.frags = FRAGS_SPECTATOR; - PlayerScore_Clear(this); // clear scores when needed - } - - if (this.killcount != FRAGS_SPECTATOR) - { - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_SPECTATE, this.netname); - if(!intermission_running) - if(autocvar_g_chat_nospectators == 1 || (!(warmup_stage || gameover) && autocvar_g_chat_nospectators == 2)) - Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_CHAT_NOSPECTATORS); - - if(this.just_joined == false) { - LogTeamchange(this.playerid, -1, 4); - } else - this.just_joined = false; - } - - accuracy_resend(this); - - this.spectatortime = time; - this.bot_attack = false; - this.hud = HUD_NORMAL; - TRANSMUTE(Observer, this); - this.iscreature = false; - this.teleportable = TELEPORT_SIMPLE; - this.damagedbycontents = false; - this.health = FRAGS_SPECTATOR; - SetSpectatee_status(this, etof(this)); - this.takedamage = DAMAGE_NO; - this.solid = SOLID_NOT; - set_movetype(this, MOVETYPE_FLY_WORLDONLY); // user preference is controlled by playerprethink - this.flags = FL_CLIENT | FL_NOTARGET; - this.armorvalue = 666; - this.effects = 0; - this.armorvalue = autocvar_g_balance_armor_start; - this.pauserotarmor_finished = 0; - this.pauserothealth_finished = 0; - this.pauseregen_finished = 0; - this.damageforcescale = 0; - this.death_time = 0; - this.respawn_flags = 0; - this.respawn_time = 0; - this.stat_respawn_time = 0; - this.alpha = 0; - this.scale = 0; - this.fade_time = 0; - this.pain_frame = 0; - this.pain_finished = 0; - this.strength_finished = 0; - this.invincible_finished = 0; - this.superweapons_finished = 0; - this.pushltime = 0; - this.istypefrag = 0; - setthink(this, func_null); - this.nextthink = 0; - this.hook_time = 0; - this.deadflag = DEAD_NO; - this.crouch = false; - this.revival_time = 0; - - this.items = 0; - this.weapons = '0 0 0'; - this.drawonlytoclient = this; - - this.weaponname = ""; - this.weaponmodel = ""; - for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) - { - this.weaponentities[slot] = NULL; - } - this.exteriorweaponentity = NULL; - this.killcount = FRAGS_SPECTATOR; - this.velocity = '0 0 0'; - this.avelocity = '0 0 0'; - this.punchangle = '0 0 0'; - this.punchvector = '0 0 0'; - this.oldvelocity = this.velocity; - this.fire_endtime = -1; - this.event_damage = func_null; - - STAT(ACTIVEWEAPON, this) = WEP_Null.m_id; - STAT(SWITCHINGWEAPON, this) = WEP_Null.m_id; - STAT(SWITCHWEAPON, this) = WEP_Null.m_id; -} - -int player_getspecies(entity this) -{ - get_model_parameters(this.model, this.skin); - int s = get_model_parameters_species; - get_model_parameters(string_null, 0); - if (s < 0) return SPECIES_HUMAN; - return s; -} - -.float model_randomizer; -void FixPlayermodel(entity player) -{ - string defaultmodel = ""; - int defaultskin = 0; - if(autocvar_sv_defaultcharacter) - { - if(teamplay) - { - string s = Static_Team_ColorName_Lower(player.team); - if (s != "neutral") - { - defaultmodel = cvar_string(strcat("sv_defaultplayermodel_", s)); - defaultskin = cvar(strcat("sv_defaultplayerskin_", s)); - } - } - - if(defaultmodel == "") - { - defaultmodel = autocvar_sv_defaultplayermodel; - defaultskin = autocvar_sv_defaultplayerskin; - } - - int n = tokenize_console(defaultmodel); - if(n > 0) - { - defaultmodel = argv(floor(n * player.model_randomizer)); - // However, do NOT randomize if the player-selected model is in the list. - for (int i = 0; i < n; ++i) - if ((argv(i) == player.playermodel && defaultskin == stof(player.playerskin)) || argv(i) == strcat(player.playermodel, ":", player.playerskin)) - defaultmodel = argv(i); - } - - int i = strstrofs(defaultmodel, ":", 0); - if(i >= 0) - { - defaultskin = stof(substring(defaultmodel, i+1, -1)); - defaultmodel = substring(defaultmodel, 0, i); - } - } - if(autocvar_sv_defaultcharacterskin && !defaultskin) - { - if(teamplay) - { - string s = Static_Team_ColorName_Lower(player.team); - if (s != "neutral") - defaultskin = cvar(strcat("sv_defaultplayerskin_", s)); - } - - if(!defaultskin) - defaultskin = autocvar_sv_defaultplayerskin; - } - - MUTATOR_CALLHOOK(FixPlayermodel, defaultmodel, defaultskin, player); - defaultmodel = M_ARGV(0, string); - defaultskin = M_ARGV(1, int); - - bool chmdl = false; - int oldskin; - if(defaultmodel != "") - { - if (defaultmodel != player.model) - { - vector m1 = player.mins; - vector m2 = player.maxs; - setplayermodel (player, defaultmodel); - setsize (player, m1, m2); - chmdl = true; - } - - oldskin = player.skin; - player.skin = defaultskin; - } else { - if (player.playermodel != player.model || player.playermodel == "") - { - player.playermodel = CheckPlayerModel(player.playermodel); // this is never "", so no endless loop - vector m1 = player.mins; - vector m2 = player.maxs; - setplayermodel (player, player.playermodel); - setsize (player, m1, m2); - chmdl = true; - } - - if(!autocvar_sv_defaultcharacterskin) - { - oldskin = player.skin; - player.skin = stof(player.playerskin); - } - else - { - oldskin = player.skin; - player.skin = defaultskin; - } - } - - if(chmdl || oldskin != player.skin) // model or skin has changed - { - player.species = player_getspecies(player); // update species - if(!autocvar_g_debug_globalsounds) - UpdatePlayerSounds(player); // update skin sounds - } - - if(!teamplay) - if(strlen(autocvar_sv_defaultplayercolors)) - if(player.clientcolors != stof(autocvar_sv_defaultplayercolors)) - setcolor(player, stof(autocvar_sv_defaultplayercolors)); -} - - -/** Called when a client spawns in the server */ -void PutClientInServer(entity this) -{ - if (IS_BOT_CLIENT(this)) { - TRANSMUTE(Player, this); - } else if (IS_REAL_CLIENT(this)) { - msg_entity = this; - WriteByte(MSG_ONE, SVC_SETVIEW); - WriteEntity(MSG_ONE, this); - } - if (gameover) { - TRANSMUTE(Observer, this); - } - - SetSpectatee(this, NULL); - - // reset player keys - this.itemkeys = 0; - - MUTATOR_CALLHOOK(PutClientInServer, this); - - if (IS_OBSERVER(this)) { - PutObserverInServer(this); - } else if (IS_PLAYER(this)) { - if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE); - - PlayerState_attach(this); - accuracy_resend(this); - - if (this.team < 0) - JoinBestTeam(this, false, true); - - entity spot = SelectSpawnPoint(this, false); - if (!spot) { - Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_NOSPAWNS); - return; // spawn failed - } - - TRANSMUTE(Player, this); - - this.wasplayer = true; - this.iscreature = true; - this.teleportable = TELEPORT_NORMAL; - this.damagedbycontents = true; - set_movetype(this, MOVETYPE_WALK); - this.solid = SOLID_SLIDEBOX; - this.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_SOLID; - if (autocvar_g_playerclip_collisions) - this.dphitcontentsmask |= DPCONTENTS_PLAYERCLIP; - if (IS_BOT_CLIENT(this) && autocvar_g_botclip_collisions) - this.dphitcontentsmask |= DPCONTENTS_BOTCLIP; - this.frags = FRAGS_PLAYER; - if (INDEPENDENT_PLAYERS) MAKE_INDEPENDENT_PLAYER(this); - this.flags = FL_CLIENT | FL_PICKUPITEMS; - if (autocvar__notarget) - this.flags |= FL_NOTARGET; - this.takedamage = DAMAGE_AIM; - this.effects = EF_TELEPORT_BIT | EF_RESTARTANIM_BIT; - this.dmg = 2; // WTF - - if (warmup_stage) { - this.ammo_shells = warmup_start_ammo_shells; - this.ammo_nails = warmup_start_ammo_nails; - this.ammo_rockets = warmup_start_ammo_rockets; - this.ammo_cells = warmup_start_ammo_cells; - this.ammo_plasma = warmup_start_ammo_plasma; - this.ammo_fuel = warmup_start_ammo_fuel; - this.health = warmup_start_health; - this.armorvalue = warmup_start_armorvalue; - this.weapons = WARMUP_START_WEAPONS; - } else { - this.ammo_shells = start_ammo_shells; - this.ammo_nails = start_ammo_nails; - this.ammo_rockets = start_ammo_rockets; - this.ammo_cells = start_ammo_cells; - this.ammo_plasma = start_ammo_plasma; - this.ammo_fuel = start_ammo_fuel; - this.health = start_health; - this.armorvalue = start_armorvalue; - this.weapons = start_weapons; - } - SetSpectatee_status(this, 0); - - this.superweapons_finished = (this.weapons & WEPSET_SUPERWEAPONS) ? time + autocvar_g_balance_superweapons_time : 0; - - this.items = start_items; - - this.spawnshieldtime = time + autocvar_g_spawnshieldtime; - this.pauserotarmor_finished = time + autocvar_g_balance_pause_armor_rot_spawn; - this.pauserothealth_finished = time + autocvar_g_balance_pause_health_rot_spawn; - this.pauserotfuel_finished = time + autocvar_g_balance_pause_fuel_rot_spawn; - this.pauseregen_finished = time + autocvar_g_balance_pause_health_regen_spawn; - // extend the pause of rotting if client was reset at the beginning of the countdown - if (!autocvar_sv_ready_restart_after_countdown && time < game_starttime) { // TODO why is this cvar NOTted? - float f = game_starttime - time; - this.spawnshieldtime += f; - this.pauserotarmor_finished += f; - this.pauserothealth_finished += f; - this.pauseregen_finished += f; - } - this.damageforcescale = 2; - this.death_time = 0; - this.respawn_flags = 0; - this.respawn_time = 0; - this.stat_respawn_time = 0; - this.scale = autocvar_sv_player_scale; - this.fade_time = 0; - this.pain_frame = 0; - this.pain_finished = 0; - this.pushltime = 0; - setthink(this, func_null); // players have no think function - this.nextthink = 0; - this.dmg_team = 0; - this.ballistics_density = autocvar_g_ballistics_density_player; - - this.deadflag = DEAD_NO; - - this.angles = spot.angles; - this.angles_z = 0; // never spawn tilted even if the spot says to - if (IS_BOT_CLIENT(this)) - this.v_angle = this.angles; - this.fixangle = true; // turn this way immediately - this.oldvelocity = this.velocity = '0 0 0'; - this.avelocity = '0 0 0'; - this.punchangle = '0 0 0'; - this.punchvector = '0 0 0'; - - this.strength_finished = 0; - this.invincible_finished = 0; - this.fire_endtime = -1; - this.revival_time = 0; - this.air_finished = time + 12; - - entity spawnevent = new_pure(spawnevent); - spawnevent.owner = this; - Net_LinkEntity(spawnevent, false, 0.5, SpawnEvent_Send); - - // Cut off any still running player sounds. - stopsound(this, CH_PLAYER_SINGLE); - - this.model = ""; - FixPlayermodel(this); - this.drawonlytoclient = NULL; - - this.crouch = false; - this.view_ofs = STAT(PL_VIEW_OFS, NULL); - setsize(this, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL)); - this.spawnorigin = spot.origin; - setorigin(this, spot.origin + '0 0 1' * (1 - this.mins.z - 24)); - // don't reset back to last position, even if new position is stuck in solid - this.oldorigin = this.origin; - this.prevorigin = this.origin; - this.lastteleporttime = time; // prevent insane speeds due to changing origin - this.conveyor = NULL; // prevent conveyors at the previous location from moving a freshly spawned player - this.hud = HUD_NORMAL; - - this.event_damage = PlayerDamage; - - this.bot_attack = true; - this.monster_attack = true; - - PHYS_INPUT_BUTTON_ATCK(this) = PHYS_INPUT_BUTTON_JUMP(this) = PHYS_INPUT_BUTTON_ATCK2(this) = false; - - if (this.killcount == FRAGS_SPECTATOR) { - PlayerScore_Clear(this); - this.killcount = 0; - } - - for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) - { - CL_SpawnWeaponentity(this, weaponentities[slot]); - } - this.alpha = default_player_alpha; - this.colormod = '1 1 1' * autocvar_g_player_brightness; - this.exteriorweaponentity.alpha = default_weapon_alpha; - - this.speedrunning = false; - - target_voicescript_clear(this); - - // reset fields the weapons may use - FOREACH(Weapons, true, LAMBDA( - it.wr_resetplayer(it, this); - // reload all reloadable weapons - if (it.spawnflags & WEP_FLAG_RELOADABLE) { - this.weapon_load[it.m_id] = it.reloading_ammo; - } - )); - - { - string s = spot.target; - spot.target = string_null; - SUB_UseTargets(spot, this, NULL); - spot.target = s; - } - - Unfreeze(this); - - MUTATOR_CALLHOOK(PlayerSpawn, this, spot); - - if (autocvar_spawn_debug) - { - sprint(this, strcat("spawnpoint origin: ", vtos(spot.origin), "\n")); - delete(spot); // usefull for checking if there are spawnpoints, that let drop through the floor - } - - PS(this).m_switchweapon = w_getbestweapon(this); - this.cnt = -1; // W_LastWeapon will not complain - PS(this).m_weapon = WEP_Null; - this.weaponname = ""; - PS(this).m_switchingweapon = WEP_Null; - - if (!warmup_stage && !this.alivetime) - this.alivetime = time; - - antilag_clear(this, CS(this)); - } -} - -void ClientInit_misc(entity this); - -.float ebouncefactor, ebouncestop; // electro's values -// TODO do we need all these fields, or should we stop autodetecting runtime -// changes and just have a console command to update this? -bool ClientInit_SendEntity(entity this, entity to, int sf) -{ - WriteHeader(MSG_ENTITY, _ENT_CLIENT_INIT); - return = true; - msg_entity = to; - // MSG_INIT replacement - // TODO: make easier to use - Registry_send_all(); - W_PROP_reload(MSG_ONE, to); - ClientInit_misc(this); - MUTATOR_CALLHOOK(Ent_Init); -} -void ClientInit_misc(entity this) -{ - int channel = MSG_ONE; - WriteHeader(channel, ENT_CLIENT_INIT); - WriteByte(channel, g_nexball_meter_period * 32); - WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[0])); - WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[1])); - WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[2])); - WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[3])); - WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[0])); - WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[1])); - WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[2])); - WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[3])); - - if(sv_foginterval && world.fog != "") - WriteString(channel, world.fog); - else - WriteString(channel, ""); - WriteByte(channel, this.count * 255.0); // g_balance_armor_blockpercent - WriteByte(channel, serverflags); // client has to know if it should zoom or not - WriteCoord(channel, autocvar_g_trueaim_minrange); -} - -void ClientInit_CheckUpdate(entity this) -{ - this.nextthink = time; - if(this.count != autocvar_g_balance_armor_blockpercent) - { - this.count = autocvar_g_balance_armor_blockpercent; - this.SendFlags |= 1; - } -} - -void ClientInit_Spawn() -{ - entity e = new_pure(clientinit); - setthink(e, ClientInit_CheckUpdate); - Net_LinkEntity(e, false, 0, ClientInit_SendEntity); - - ClientInit_CheckUpdate(e); -} - -/* -============= -SetNewParms -============= -*/ -void SetNewParms () -{ - // initialize parms for a new player - parm1 = -(86400 * 366); - - MUTATOR_CALLHOOK(SetNewParms); -} - -/* -============= -SetChangeParms -============= -*/ -void SetChangeParms (entity this) -{ - // save parms for level change - parm1 = this.parm_idlesince - time; - - MUTATOR_CALLHOOK(SetChangeParms); -} - -/* -============= -DecodeLevelParms -============= -*/ -void DecodeLevelParms(entity this) -{ - // load parms - this.parm_idlesince = parm1; - if (this.parm_idlesince == -(86400 * 366)) - this.parm_idlesince = time; - - // whatever happens, allow 60 seconds of idling directly after connect for map loading - this.parm_idlesince = max(this.parm_idlesince, time - sv_maxidle + 60); - - MUTATOR_CALLHOOK(DecodeLevelParms); -} - -/* -============= -ClientKill - -Called when a client types 'kill' in the console -============= -*/ - -.float clientkill_nexttime; -void ClientKill_Now_TeamChange(entity this) -{ - if(this.killindicator_teamchange == -1) - { - JoinBestTeam( this, false, true ); - } - else if(this.killindicator_teamchange == -2) - { - if(blockSpectators) - Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime); - PutObserverInServer(this); - } - else - SV_ChangeTeam(this, this.killindicator_teamchange - 1); - this.killindicator_teamchange = 0; -} - -void ClientKill_Now(entity this) -{ - if(this.vehicle) - { - vehicles_exit(this.vehicle, VHEF_RELEASE); - if(!this.killindicator_teamchange) - { - this.vehicle_health = -1; - Damage(this, this, this, 1 , DEATH_KILL.m_id, this.origin, '0 0 0'); - } - } - - if(this.killindicator && !wasfreed(this.killindicator)) - delete(this.killindicator); - - this.killindicator = NULL; - - if(this.killindicator_teamchange) - ClientKill_Now_TeamChange(this); - - if(!IS_SPEC(this) && !IS_OBSERVER(this)) - Damage(this, this, this, 100000, DEATH_KILL.m_id, this.origin, '0 0 0'); - - // now I am sure the player IS dead -} -void KillIndicator_Think(entity this) -{ - if (gameover) - { - this.owner.killindicator = NULL; - delete(this); - return; - } - - if (this.owner.alpha < 0 && !this.owner.vehicle) - { - this.owner.killindicator = NULL; - delete(this); - return; - } - - if(this.cnt <= 0) - { - ClientKill_Now(this.owner); - return; - } - else if(g_cts && this.health == 1) // health == 1 means that it's silent - { - this.nextthink = time + 1; - this.cnt -= 1; - } - else - { - if(this.cnt <= 10) - setmodel(this, MDL_NUM(this.cnt)); - if(IS_REAL_CLIENT(this.owner)) - { - if(this.cnt <= 10) - { Send_Notification(NOTIF_ONE, this.owner, MSG_ANNCE, Announcer_PickNumber(CNT_KILL, this.cnt)); } - } - this.nextthink = time + 1; - this.cnt -= 1; - } -} - -float clientkilltime; -void ClientKill_TeamChange (entity this, float targetteam) // 0 = don't change, -1 = auto, -2 = spec -{ - float killtime; - float starttime; - - if (gameover) - return; - - killtime = autocvar_g_balance_kill_delay; - - if(g_race_qualifying || g_cts) - killtime = 0; - - if(MUTATOR_CALLHOOK(ClientKill, this, killtime)) - return; - - this.killindicator_teamchange = targetteam; - - if(!this.killindicator) - { - if(!IS_DEAD(this)) - { - killtime = max(killtime, this.clientkill_nexttime - time); - this.clientkill_nexttime = time + killtime + autocvar_g_balance_kill_antispam; - } - - if(killtime <= 0 || !IS_PLAYER(this) || IS_DEAD(this)) - { - ClientKill_Now(this); - } - else - { - starttime = max(time, clientkilltime); - - this.killindicator = spawn(); - this.killindicator.owner = this; - this.killindicator.scale = 0.5; - setattachment(this.killindicator, this, ""); - setorigin(this.killindicator, '0 0 52'); - setthink(this.killindicator, KillIndicator_Think); - this.killindicator.nextthink = starttime + (this.lip) * 0.05; - clientkilltime = max(clientkilltime, this.killindicator.nextthink + 0.05); - this.killindicator.cnt = ceil(killtime); - this.killindicator.count = bound(0, ceil(killtime), 10); - //sprint(this, strcat("^1You'll be dead in ", ftos(this.killindicator.cnt), " seconds\n")); - - FOREACH_ENTITY_ENT(enemy, this, - { - if(it.classname != "body") - continue; - it.killindicator = spawn(); - it.killindicator.owner = it; - it.killindicator.scale = 0.5; - setattachment(it.killindicator, it, ""); - setorigin(it.killindicator, '0 0 52'); - setthink(it.killindicator, KillIndicator_Think); - it.killindicator.nextthink = starttime + (it.lip) * 0.05; - //clientkilltime = max(clientkilltime, it.killindicator.nextthink + 0.05); - it.killindicator.cnt = ceil(killtime); - }); - this.lip = 0; - } - } - if(this.killindicator) - { - if(targetteam == 0) // just die - { - this.killindicator.colormod = '0 0 0'; - if(IS_REAL_CLIENT(this)) - if(this.killindicator.cnt > 0) - Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_TEAMCHANGE_SUICIDE, this.killindicator.cnt); - } - else if(targetteam == -1) // auto - { - this.killindicator.colormod = '0 1 0'; - if(IS_REAL_CLIENT(this)) - if(this.killindicator.cnt > 0) - Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_TEAMCHANGE_AUTO, this.killindicator.cnt); - } - else if(targetteam == -2) // spectate - { - this.killindicator.colormod = '0.5 0.5 0.5'; - if(IS_REAL_CLIENT(this)) - if(this.killindicator.cnt > 0) - Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_TEAMCHANGE_SPECTATE, this.killindicator.cnt); - } - else - { - this.killindicator.colormod = Team_ColorRGB(targetteam); - if(IS_REAL_CLIENT(this)) - if(this.killindicator.cnt > 0) - Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, APP_TEAM_NUM(targetteam, CENTER_TEAMCHANGE), this.killindicator.cnt); - } - } - -} - -void ClientKill (entity this) -{ - if(gameover) return; - if(this.player_blocked) return; - if(STAT(FROZEN, this)) return; - - ClientKill_TeamChange(this, 0); -} - -void FixClientCvars(entity e) -{ - // send prediction settings to the client - stuffcmd(e, "\nin_bindmap 0 0\n"); - if(autocvar_g_antilag == 3) // client side hitscan - stuffcmd(e, "cl_cmd settemp cl_prydoncursor_notrace 0\n"); - if(autocvar_sv_gentle) - stuffcmd(e, "cl_cmd settemp cl_gentle 1\n"); - - MUTATOR_CALLHOOK(FixClientCvars, e); -} - -float PlayerInIDList(entity p, string idlist) -{ - float n, i; - string s; - - // NOTE: we do NOT check crypto_idfp_signed here, an unsigned ID is fine too for this - if (!p.crypto_idfp) - return 0; - - // this function allows abbreviated player IDs too! - n = tokenize_console(idlist); - for(i = 0; i < n; ++i) - { - s = argv(i); - if(s == substring(p.crypto_idfp, 0, strlen(s))) - return 1; - } - - return 0; -} - -#ifdef DP_EXT_PRECONNECT -/* -============= -ClientPreConnect - -Called once (not at each match start) when a client begins a connection to the server -============= -*/ -void ClientPreConnect () -{ENGINE_EVENT(); - if(autocvar_sv_eventlog) - { - GameLogEcho(sprintf(":connect:%d:%d:%s", - this.playerid, - etof(this), - ((IS_REAL_CLIENT(this)) ? this.netaddress : "bot") - )); - } -} -#endif - -/** -============= -ClientConnect - -Called when a client connects to the server -============= -*/ -void ClientConnect(entity this) -{ - if (Ban_MaybeEnforceBanOnce(this)) return; - assert(!IS_CLIENT(this), return); - this.flags |= FL_CLIENT; - assert(player_count >= 0, player_count = 0); - -#ifdef WATERMARK - Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_WATERMARK, WATERMARK); -#endif - this.version_nagtime = time + 10 + random() * 10; - TRANSMUTE(Client, this); - - // identify the right forced team - if (autocvar_g_campaign) - { - if (IS_REAL_CLIENT(this)) // only players, not bots - { - switch (autocvar_g_campaign_forceteam) - { - case 1: this.team_forced = NUM_TEAM_1; break; - case 2: this.team_forced = NUM_TEAM_2; break; - case 3: this.team_forced = NUM_TEAM_3; break; - case 4: this.team_forced = NUM_TEAM_4; break; - default: this.team_forced = 0; - } - } - } - else if (PlayerInIDList(this, autocvar_g_forced_team_red)) this.team_forced = NUM_TEAM_1; - else if (PlayerInIDList(this, autocvar_g_forced_team_blue)) this.team_forced = NUM_TEAM_2; - else if (PlayerInIDList(this, autocvar_g_forced_team_yellow)) this.team_forced = NUM_TEAM_3; - else if (PlayerInIDList(this, autocvar_g_forced_team_pink)) this.team_forced = NUM_TEAM_4; - else switch (autocvar_g_forced_team_otherwise) - { - default: this.team_forced = 0; break; - case "red": this.team_forced = NUM_TEAM_1; break; - case "blue": this.team_forced = NUM_TEAM_2; break; - case "yellow": this.team_forced = NUM_TEAM_3; break; - case "pink": this.team_forced = NUM_TEAM_4; break; - case "spectate": - case "spectator": - this.team_forced = -1; - break; - } - if (!teamplay && this.team_forced > 0) this.team_forced = 0; - - { - int id = this.playerid; - this.playerid = 0; // silent - JoinBestTeam(this, false, false); // if the team number is valid, keep it - this.playerid = id; - } - - if (autocvar_sv_spectate || autocvar_g_campaign || this.team_forced < 0) { - TRANSMUTE(Observer, this); - } else { - if (!teamplay || autocvar_g_balance_teams) { - TRANSMUTE(Player, this); - campaign_bots_may_start = true; - } else { - TRANSMUTE(Observer, this); // do it anyway - } - } - - PlayerStats_GameReport_AddEvent(sprintf("kills-%d", this.playerid)); - - // always track bots, don't ask for cl_allow_uidtracking - if (IS_BOT_CLIENT(this)) PlayerStats_GameReport_AddPlayer(this); - - if (autocvar_sv_eventlog) - GameLogEcho(strcat(":join:", ftos(this.playerid), ":", ftos(etof(this)), ":", ((IS_REAL_CLIENT(this)) ? this.netaddress : "bot"), ":", this.netname)); - - LogTeamchange(this.playerid, this.team, 1); - - this.just_joined = true; // stop spamming the eventlog with additional lines when the client connects - - this.netname_previous = strzone(this.netname); - - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((teamplay && IS_PLAYER(this)) ? APP_TEAM_ENT(this, INFO_JOIN_CONNECT_TEAM) : INFO_JOIN_CONNECT), this.netname); - - stuffcmd(this, clientstuff, "\n"); - stuffcmd(this, "cl_particles_reloadeffects\n"); // TODO do we still need this? - - FixClientCvars(this); - - // get version info from player - stuffcmd(this, "cmd clientversion $gameversion\n"); - - // notify about available teams - if (teamplay) - { - CheckAllowedTeams(this); - int t = 0; - if (c1 >= 0) t |= BIT(0); - if (c2 >= 0) t |= BIT(1); - if (c3 >= 0) t |= BIT(2); - if (c4 >= 0) t |= BIT(3); - stuffcmd(this, sprintf("set _teams_available %d\n", t)); - } - else - { - stuffcmd(this, "set _teams_available 0\n"); - } - - bot_relinkplayerlist(); - - this.spectatortime = time; - if (blockSpectators) - { - Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime); - } - - this.jointime = time; - this.allowed_timeouts = autocvar_sv_timeout_number; - - if (IS_REAL_CLIENT(this)) - { - if (!autocvar_g_campaign) - { - this.motd_actived_time = -1; - Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, getwelcomemessage(this)); - } - - if (g_weaponarena_weapons == WEPSET(TUBA)) - stuffcmd(this, "cl_cmd settemp chase_active 1\n"); - } - - if (!sv_foginterval && world.fog != "") - stuffcmd(this, strcat("\nfog ", world.fog, "\nr_fog_exp2 0\nr_drawfog 1\n")); - - if (autocvar_sv_teamnagger && !(autocvar_bot_vs_human && AvailableTeams() == 2)) - if (!g_ca && !g_cts && !g_race) // teamnagger is currently bad for ca, race & cts - send_CSQC_teamnagger(); - - CSQCMODEL_AUTOINIT(this); - - this.model_randomizer = random(); - - if (IS_REAL_CLIENT(this)) - sv_notice_join(this); - - FOREACH_ENTITY_FLOAT(init_for_player_needed, true, { - it.init_for_player(it, this); - }); - - MUTATOR_CALLHOOK(ClientConnect, this); -} -/* -============= -ClientDisconnect - -Called when a client disconnects from the server -============= -*/ -.entity chatbubbleentity; -void ReadyCount(); -void ClientDisconnect(entity this) -{ - assert(IS_CLIENT(this), return); - - PlayerStats_GameReport_FinalizePlayer(this); - if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE); - if (this.active_minigame) part_minigame(this); - if (IS_PLAYER(this)) Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1); - - if (autocvar_sv_eventlog) - GameLogEcho(strcat(":part:", ftos(this.playerid))); - - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_DISCONNECT, this.netname); - - SetSpectatee(this, NULL); - - MUTATOR_CALLHOOK(ClientDisconnect, this); - - ClientState_detach(this); - - Portal_ClearAll(this); - - Unfreeze(this); - - RemoveGrapplingHook(this); - - // Here, everything has been done that requires this player to be a client. - - this.flags &= ~FL_CLIENT; - - if (this.chatbubbleentity) delete(this.chatbubbleentity); - if (this.killindicator) delete(this.killindicator); - - WaypointSprite_PlayerGone(this); - - bot_relinkplayerlist(); - - if (this.netname_previous) strunzone(this.netname_previous); - if (this.clientstatus) strunzone(this.clientstatus); - if (this.weaponorder_byimpulse) strunzone(this.weaponorder_byimpulse); - if (this.personal) delete(this.personal); - - this.playerid = 0; - ReadyCount(); - if (vote_called && IS_REAL_CLIENT(this)) VoteCount(false); -} - -void ChatBubbleThink(entity this) -{ - this.nextthink = time; - if ((this.owner.alpha < 0) || this.owner.chatbubbleentity != this) - { - if(this.owner) // but why can that ever be NULL? - this.owner.chatbubbleentity = NULL; - delete(this); - return; - } - - this.mdl = ""; - - if ( !IS_DEAD(this.owner) && IS_PLAYER(this.owner) ) - { - if ( this.owner.active_minigame ) - this.mdl = "models/sprites/minigame_busy.iqm"; - else if (PHYS_INPUT_BUTTON_CHAT(this.owner)) - this.mdl = "models/misc/chatbubble.spr"; - } - - if ( this.model != this.mdl ) - _setmodel(this, this.mdl); - -} - -void UpdateChatBubble(entity this) -{ - if (this.alpha < 0) - return; - // spawn a chatbubble entity if needed - if (!this.chatbubbleentity) - { - this.chatbubbleentity = new(chatbubbleentity); - this.chatbubbleentity.owner = this; - this.chatbubbleentity.exteriormodeltoclient = this; - setthink(this.chatbubbleentity, ChatBubbleThink); - this.chatbubbleentity.nextthink = time; - setmodel(this.chatbubbleentity, MDL_CHAT); // precision set below - //setorigin(this.chatbubbleentity, this.origin + '0 0 15' + this.maxs_z * '0 0 1'); - setorigin(this.chatbubbleentity, '0 0 15' + this.maxs_z * '0 0 1'); - setattachment(this.chatbubbleentity, this, ""); // sticks to moving player better, also conserves bandwidth - this.chatbubbleentity.mdl = this.chatbubbleentity.model; - //this.chatbubbleentity.model = ""; - this.chatbubbleentity.effects = EF_LOWPRECISION; - } -} - - -// LordHavoc: this hack will be removed when proper _pants/_shirt layers are -// added to the model skins -/*void UpdateColorModHack() -{ - float c; - c = this.clientcolors & 15; - // LordHavoc: only bothering to support white, green, red, yellow, blue - if (!teamplay) this.colormod = '0 0 0'; - else if (c == 0) this.colormod = '1.00 1.00 1.00'; - else if (c == 3) this.colormod = '0.10 1.73 0.10'; - else if (c == 4) this.colormod = '1.73 0.10 0.10'; - else if (c == 12) this.colormod = '1.22 1.22 0.10'; - else if (c == 13) this.colormod = '0.10 0.10 1.73'; - else this.colormod = '1 1 1'; -}*/ - -void respawn(entity this) -{ - if(this.alpha >= 0 && autocvar_g_respawn_ghosts) - { - this.solid = SOLID_NOT; - this.takedamage = DAMAGE_NO; - set_movetype(this, MOVETYPE_FLY); - this.velocity = '0 0 1' * autocvar_g_respawn_ghosts_speed; - this.avelocity = randomvec() * autocvar_g_respawn_ghosts_speed * 3 - randomvec() * autocvar_g_respawn_ghosts_speed * 3; - this.effects |= CSQCMODEL_EF_RESPAWNGHOST; - Send_Effect(EFFECT_RESPAWN_GHOST, this.origin, '0 0 0', 1); - if(autocvar_g_respawn_ghosts_maxtime) - SUB_SetFade (this, time + autocvar_g_respawn_ghosts_maxtime / 2 + random () * (autocvar_g_respawn_ghosts_maxtime - autocvar_g_respawn_ghosts_maxtime / 2), 1.5); - } - - CopyBody(this, 1); - - this.effects |= EF_NODRAW; // prevent another CopyBody - PutClientInServer(this); -} - -void play_countdown(entity this, float finished, Sound samp) -{ - TC(Sound, samp); - if(IS_REAL_CLIENT(this)) - if(floor(finished - time - frametime) != floor(finished - time)) - if(finished - time < 6) - sound (this, CH_INFO, samp, VOL_BASE, ATTEN_NORM); -} - -void player_powerups(entity this) -{ - // add a way to see what the items were BEFORE all of these checks for the mutator hook - int items_prev = this.items; - - if((this.items & IT_USING_JETPACK) && !IS_DEAD(this) && !gameover) - this.modelflags |= MF_ROCKET; - else - this.modelflags &= ~MF_ROCKET; - - this.effects &= ~(EF_RED | EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT | EF_FLAME | EF_NODEPTHTEST); - - if((this.alpha < 0 || IS_DEAD(this)) && !this.vehicle) // don't apply the flags if the player is gibbed - return; - - Fire_ApplyDamage(this); - Fire_ApplyEffect(this); - - if (!g_instagib) - { - if (this.items & ITEM_Strength.m_itemid) - { - play_countdown(this, this.strength_finished, SND_POWEROFF); - this.effects = this.effects | (EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT); - if (time > this.strength_finished) - { - this.items = this.items - (this.items & ITEM_Strength.m_itemid); - //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERDOWN_STRENGTH, this.netname); - Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERDOWN_STRENGTH); - } - } - else - { - if (time < this.strength_finished) - { - this.items = this.items | ITEM_Strength.m_itemid; - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_STRENGTH, this.netname); - Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERUP_STRENGTH); - } - } - if (this.items & ITEM_Shield.m_itemid) - { - play_countdown(this, this.invincible_finished, SND_POWEROFF); - this.effects = this.effects | (EF_RED | EF_ADDITIVE | EF_FULLBRIGHT); - if (time > this.invincible_finished) - { - this.items = this.items - (this.items & ITEM_Shield.m_itemid); - //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERDOWN_SHIELD, this.netname); - Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERDOWN_SHIELD); - } - } - else - { - if (time < this.invincible_finished) - { - this.items = this.items | ITEM_Shield.m_itemid; - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_SHIELD, this.netname); - Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERUP_SHIELD); - } - } - if (this.items & IT_SUPERWEAPON) - { - if (!(this.weapons & WEPSET_SUPERWEAPONS)) - { - this.superweapons_finished = 0; - this.items = this.items - (this.items & IT_SUPERWEAPON); - //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_LOST, this.netname); - Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_LOST); - } - else if (this.items & IT_UNLIMITED_SUPERWEAPONS) - { - // don't let them run out - } - else - { - play_countdown(this, this.superweapons_finished, SND_POWEROFF); - if (time > this.superweapons_finished) - { - this.items = this.items - (this.items & IT_SUPERWEAPON); - this.weapons &= ~WEPSET_SUPERWEAPONS; - //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_BROKEN, this.netname); - Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_BROKEN); - } - } - } - else if(this.weapons & WEPSET_SUPERWEAPONS) - { - if (time < this.superweapons_finished || (this.items & IT_UNLIMITED_SUPERWEAPONS)) - { - this.items = this.items | IT_SUPERWEAPON; - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_PICKUP, this.netname); - Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_PICKUP); - } - else - { - this.superweapons_finished = 0; - this.weapons &= ~WEPSET_SUPERWEAPONS; - } - } - else - { - this.superweapons_finished = 0; - } - } - - if(autocvar_g_nodepthtestplayers) - this.effects = this.effects | EF_NODEPTHTEST; - - if(autocvar_g_fullbrightplayers) - this.effects = this.effects | EF_FULLBRIGHT; - - if (time >= game_starttime) - if (time < this.spawnshieldtime) - this.effects = this.effects | (EF_ADDITIVE | EF_FULLBRIGHT); - - MUTATOR_CALLHOOK(PlayerPowerups, this, items_prev); -} - -float CalcRegen(float current, float stable, float regenfactor, float regenframetime) -{ - if(current > stable) - return current; - else if(current > stable - 0.25) // when close enough, "snap" - return stable; - else - return min(stable, current + (stable - current) * regenfactor * regenframetime); -} - -float CalcRot(float current, float stable, float rotfactor, float rotframetime) -{ - if(current < stable) - return current; - else if(current < stable + 0.25) // when close enough, "snap" - return stable; - else - return max(stable, current + (stable - current) * rotfactor * rotframetime); -} - -float CalcRotRegen(float current, float regenstable, float regenfactor, float regenlinear, float regenframetime, float rotstable, float rotfactor, float rotlinear, float rotframetime, float limit) -{ - if(current > rotstable) - { - if(rotframetime > 0) - { - current = CalcRot(current, rotstable, rotfactor, rotframetime); - current = max(rotstable, current - rotlinear * rotframetime); - } - } - else if(current < regenstable) - { - if(regenframetime > 0) - { - current = CalcRegen(current, regenstable, regenfactor, regenframetime); - current = min(regenstable, current + regenlinear * regenframetime); - } - } - - if(current > limit) - current = limit; - - return current; -} - -void player_regen(entity this) -{ - float max_mod, regen_mod, rot_mod, limit_mod; - max_mod = regen_mod = rot_mod = limit_mod = 1; - - float regen_health = autocvar_g_balance_health_regen; - float regen_health_linear = autocvar_g_balance_health_regenlinear; - float regen_health_rot = autocvar_g_balance_health_rot; - float regen_health_rotlinear = autocvar_g_balance_health_rotlinear; - float regen_health_stable = autocvar_g_balance_health_regenstable; - float regen_health_rotstable = autocvar_g_balance_health_rotstable; - bool mutator_returnvalue = MUTATOR_CALLHOOK(PlayerRegen, this, max_mod, regen_mod, rot_mod, limit_mod, regen_health, regen_health_linear, regen_health_rot, - regen_health_rotlinear, regen_health_stable, regen_health_rotstable); - max_mod = M_ARGV(1, float); - regen_mod = M_ARGV(2, float); - rot_mod = M_ARGV(3, float); - limit_mod = M_ARGV(4, float); - regen_health = M_ARGV(5, float); - regen_health_linear = M_ARGV(6, float); - regen_health_rot = M_ARGV(7, float); - regen_health_rotlinear = M_ARGV(8, float); - regen_health_stable = M_ARGV(9, float); - regen_health_rotstable = M_ARGV(10, float); - - - if(!mutator_returnvalue) - if(!STAT(FROZEN, this)) - { - float mina, maxa, limith, limita; - maxa = autocvar_g_balance_armor_rotstable; - mina = autocvar_g_balance_armor_regenstable; - limith = autocvar_g_balance_health_limit; - limita = autocvar_g_balance_armor_limit; - - regen_health_rotstable = regen_health_rotstable * max_mod; - regen_health_stable = regen_health_stable * max_mod; - limith = limith * limit_mod; - limita = limita * limit_mod; - - this.armorvalue = CalcRotRegen(this.armorvalue, mina, autocvar_g_balance_armor_regen, autocvar_g_balance_armor_regenlinear, regen_mod * frametime * (time > this.pauseregen_finished), maxa, autocvar_g_balance_armor_rot, autocvar_g_balance_armor_rotlinear, rot_mod * frametime * (time > this.pauserotarmor_finished), limita); - this.health = CalcRotRegen(this.health, regen_health_stable, regen_health, regen_health_linear, regen_mod * frametime * (time > this.pauseregen_finished), regen_health_rotstable, regen_health_rot, regen_health_rotlinear, rot_mod * frametime * (time > this.pauserothealth_finished), limith); - } - - // if player rotted to death... die! - // check this outside above checks, as player may still be able to rot to death - if(this.health < 1) - { - if(this.vehicle) - vehicles_exit(this.vehicle, VHEF_RELEASE); - if(this.event_damage) - this.event_damage(this, this, this, 1, DEATH_ROT.m_id, this.origin, '0 0 0'); - } - - if (!(this.items & IT_UNLIMITED_WEAPON_AMMO)) - { - float minf, maxf, limitf; - - maxf = autocvar_g_balance_fuel_rotstable; - minf = autocvar_g_balance_fuel_regenstable; - limitf = autocvar_g_balance_fuel_limit; - - this.ammo_fuel = CalcRotRegen(this.ammo_fuel, minf, autocvar_g_balance_fuel_regen, autocvar_g_balance_fuel_regenlinear, frametime * (time > this.pauseregen_finished) * ((this.items & ITEM_JetpackRegen.m_itemid) != 0), maxf, autocvar_g_balance_fuel_rot, autocvar_g_balance_fuel_rotlinear, frametime * (time > this.pauserotfuel_finished), limitf); - } -} - -bool zoomstate_set; -void SetZoomState(entity this, float z) -{ - if(z != this.zoomstate) - { - this.zoomstate = z; - ClientData_Touch(this); - } - zoomstate_set = true; -} - -void GetPressedKeys(entity this) -{ - MUTATOR_CALLHOOK(GetPressedKeys, this); - int keys = this.pressedkeys; - keys = BITSET(keys, KEY_FORWARD, this.movement.x > 0); - keys = BITSET(keys, KEY_BACKWARD, this.movement.x < 0); - keys = BITSET(keys, KEY_RIGHT, this.movement.y > 0); - keys = BITSET(keys, KEY_LEFT, this.movement.y < 0); - - keys = BITSET(keys, KEY_JUMP, PHYS_INPUT_BUTTON_JUMP(this)); - keys = BITSET(keys, KEY_CROUCH, PHYS_INPUT_BUTTON_CROUCH(this)); - keys = BITSET(keys, KEY_ATCK, PHYS_INPUT_BUTTON_ATCK(this)); - keys = BITSET(keys, KEY_ATCK2, PHYS_INPUT_BUTTON_ATCK2(this)); - this.pressedkeys = keys; -} - -/* -====================== -spectate mode routines -====================== -*/ - -void SpectateCopy(entity this, entity spectatee) -{ - TC(Client, this); TC(Client, spectatee); - - MUTATOR_CALLHOOK(SpectateCopy, spectatee, this); - PS(this) = PS(spectatee); - this.armortype = spectatee.armortype; - this.armorvalue = spectatee.armorvalue; - this.ammo_cells = spectatee.ammo_cells; - this.ammo_plasma = spectatee.ammo_plasma; - this.ammo_shells = spectatee.ammo_shells; - this.ammo_nails = spectatee.ammo_nails; - this.ammo_rockets = spectatee.ammo_rockets; - this.ammo_fuel = spectatee.ammo_fuel; - this.clip_load = spectatee.clip_load; - this.clip_size = spectatee.clip_size; - this.effects = spectatee.effects & EFMASK_CHEAP; // eat performance - this.health = spectatee.health; - this.impulse = 0; - this.items = spectatee.items; - this.last_pickup = spectatee.last_pickup; - this.hit_time = spectatee.hit_time; - this.strength_finished = spectatee.strength_finished; - this.invincible_finished = spectatee.invincible_finished; - this.pressedkeys = spectatee.pressedkeys; - this.weapons = spectatee.weapons; - this.vortex_charge = spectatee.vortex_charge; - this.vortex_chargepool_ammo = spectatee.vortex_chargepool_ammo; - this.hagar_load = spectatee.hagar_load; - this.arc_heat_percent = spectatee.arc_heat_percent; - this.minelayer_mines = spectatee.minelayer_mines; - this.punchangle = spectatee.punchangle; - this.view_ofs = spectatee.view_ofs; - this.velocity = spectatee.velocity; - this.dmg_take = spectatee.dmg_take; - this.dmg_save = spectatee.dmg_save; - this.dmg_inflictor = spectatee.dmg_inflictor; - this.v_angle = spectatee.v_angle; - this.angles = spectatee.v_angle; - STAT(FROZEN, this) = STAT(FROZEN, spectatee); - this.revive_progress = spectatee.revive_progress; - if(!PHYS_INPUT_BUTTON_USE(this) && STAT(CAMERA_SPECTATOR, this) != 2) - this.fixangle = true; - setorigin(this, spectatee.origin); - setsize(this, spectatee.mins, spectatee.maxs); - SetZoomState(this, spectatee.zoomstate); - - anticheat_spectatecopy(this, spectatee); - this.hud = spectatee.hud; - if(spectatee.vehicle) - { - this.angles = spectatee.v_angle; - - //this.fixangle = false; - //this.velocity = spectatee.vehicle.velocity; - this.vehicle_health = spectatee.vehicle_health; - this.vehicle_shield = spectatee.vehicle_shield; - this.vehicle_energy = spectatee.vehicle_energy; - this.vehicle_ammo1 = spectatee.vehicle_ammo1; - this.vehicle_ammo2 = spectatee.vehicle_ammo2; - this.vehicle_reload1 = spectatee.vehicle_reload1; - this.vehicle_reload2 = spectatee.vehicle_reload2; - - //msg_entity = this; - - // WriteByte (MSG_ONE, SVC_SETVIEWANGLES); - //WriteAngle(MSG_ONE, spectatee.v_angle.x); - // WriteAngle(MSG_ONE, spectatee.v_angle.y); - // WriteAngle(MSG_ONE, spectatee.v_angle.z); - - //WriteByte (MSG_ONE, SVC_SETVIEW); - // WriteEntity(MSG_ONE, this); - //makevectors(spectatee.v_angle); - //setorigin(this, spectatee.origin - v_forward * 400 + v_up * 300);*/ - } -} - -bool SpectateUpdate(entity this) -{ - if(!this.enemy) - return false; - - if(!IS_PLAYER(this.enemy) || this == this.enemy) - { - SetSpectatee(this, NULL); - return false; - } - - SpectateCopy(this, this.enemy); - - return true; -} - -bool SpectateSet(entity this) -{ - if(!IS_PLAYER(this.enemy)) - return false; - - ClientData_Touch(this.enemy); - - msg_entity = this; - WriteByte(MSG_ONE, SVC_SETVIEW); - WriteEntity(MSG_ONE, this.enemy); - set_movetype(this, MOVETYPE_NONE); - accuracy_resend(this); - - if(!SpectateUpdate(this)) - PutObserverInServer(this); - - return true; -} - -void SetSpectatee_status(entity this, int spectatee_num) -{ - int oldspectatee_status = this.spectatee_status; - this.spectatee_status = spectatee_num; - - if (this.spectatee_status != oldspectatee_status) - { - ClientData_Touch(this); - if (g_race || g_cts) race_InitSpectator(); - } -} - -void SetSpectatee(entity this, entity spectatee) -{ - entity old_spectatee = this.enemy; - - this.enemy = spectatee; - - // WEAPONTODO - // these are required to fix the spectator bug with arc - if(old_spectatee) - { - for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) - { - .entity weaponentity = weaponentities[slot]; - if(old_spectatee.(weaponentity).arc_beam) - old_spectatee.(weaponentity).arc_beam.SendFlags |= ARC_SF_SETTINGS; - } - } - if(this.enemy) - { - for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) - { - .entity weaponentity = weaponentities[slot]; - if(this.enemy.(weaponentity).arc_beam) - this.enemy.(weaponentity).arc_beam.SendFlags |= ARC_SF_SETTINGS; - } - } - - if (this.enemy) - SetSpectatee_status(this, etof(this.enemy)); - - // needed to update spectator list - if(old_spectatee) { ClientData_Touch(old_spectatee); } -} - -bool Spectate(entity this, entity pl) -{ - if(MUTATOR_CALLHOOK(SpectateSet, this, pl)) - return false; - pl = M_ARGV(1, entity); - - SetSpectatee(this, pl); - return SpectateSet(this); -} - -bool SpectateNext(entity this) -{ - entity ent = find(this.enemy, classname, STR_PLAYER); - - if (MUTATOR_CALLHOOK(SpectateNext, this, ent)) - ent = M_ARGV(1, entity); - else if (!ent) - ent = find(ent, classname, STR_PLAYER); - - if(ent) { SetSpectatee(this, ent); } - - return SpectateSet(this); -} - -bool SpectatePrev(entity this) -{ - // NOTE: chain order is from the highest to the lower entnum (unlike find) - entity ent = findchain(classname, STR_PLAYER); - if (!ent) // no player - return false; - - entity first = ent; - // skip players until current spectated player - if(this.enemy) - while(ent && ent != this.enemy) - ent = ent.chain; - - switch (MUTATOR_CALLHOOK(SpectatePrev, this, ent, first)) - { - case MUT_SPECPREV_FOUND: - ent = M_ARGV(1, entity); - break; - case MUT_SPECPREV_RETURN: - return true; - case MUT_SPECPREV_CONTINUE: - default: - { - if(ent.chain) - ent = ent.chain; - else - ent = first; - break; - } - } - - SetSpectatee(this, ent); - return SpectateSet(this); -} - -/* -============= -ShowRespawnCountdown() - -Update a respawn countdown display. -============= -*/ -void ShowRespawnCountdown(entity this) -{ - float number; - if(!IS_DEAD(this)) // just respawned? - return; - else - { - number = ceil(this.respawn_time - time); - if(number <= 0) - return; - if(number <= this.respawn_countdown) - { - this.respawn_countdown = number - 1; - if(ceil(this.respawn_time - (time + 0.5)) == number) // only say it if it is the same number even in 0.5s; to prevent overlapping sounds - { Send_Notification(NOTIF_ONE, this, MSG_ANNCE, Announcer_PickNumber(CNT_RESPAWN, number)); } - } - } -} - -void LeaveSpectatorMode(entity this) -{ - if(this.caplayer) - return; - if(nJoinAllowed(this, this)) - { - if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || (this.wasplayer && autocvar_g_changeteam_banned) || this.team_forced > 0) - { - TRANSMUTE(Player, this); - - SetSpectatee(this, NULL); - - if(autocvar_g_campaign || autocvar_g_balance_teams) - { JoinBestTeam(this, false, true); } - - if(autocvar_g_campaign) - { campaign_bots_may_start = true; } - - Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_PREVENT_JOIN); - - PutClientInServer(this); - - if(IS_PLAYER(this)) { Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((teamplay && this.team != -1) ? APP_TEAM_ENT(this, INFO_JOIN_PLAY_TEAM) : INFO_JOIN_PLAY), this.netname); } - } - else - stuffcmd(this, "menu_showteamselect\n"); - } - else - { - // Player may not join because g_maxplayers is set - Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PREVENT); - } -} - -/** - * Determines whether the player is allowed to join. This depends on cvar - * g_maxplayers, if it isn't used this function always return true, otherwise - * it checks whether the number of currently playing players exceeds g_maxplayers. - * @return int number of free slots for players, 0 if none - */ -bool nJoinAllowed(entity this, entity ignore) -{ - if(!ignore) - // this is called that way when checking if anyone may be able to join (to build qcstatus) - // so report 0 free slots if restricted - { - if(autocvar_g_forced_team_otherwise == "spectate") - return false; - if(autocvar_g_forced_team_otherwise == "spectator") - return false; - } - - if(this.team_forced < 0) - return false; // forced spectators can never join - - // TODO simplify this - int totalClients = 0; - int currentlyPlaying = 0; - FOREACH_CLIENT(true, LAMBDA( - if(it != ignore) - ++totalClients; - if(IS_REAL_CLIENT(it)) - if(IS_PLAYER(it) || it.caplayer) - ++currentlyPlaying; - )); - - if (!autocvar_g_maxplayers) - return maxclients - totalClients; - - if(currentlyPlaying < autocvar_g_maxplayers) - return min(maxclients - totalClients, autocvar_g_maxplayers - currentlyPlaying); - - return false; -} - -/** - * Checks whether the client is an observer or spectator, if so, he will get kicked after - * g_maxplayers_spectator_blocktime seconds - */ -void checkSpectatorBlock(entity this) -{ - if(IS_SPEC(this) || IS_OBSERVER(this)) - if(!this.caplayer) - if(IS_REAL_CLIENT(this)) - { - if( time > (this.spectatortime + autocvar_g_maxplayers_spectator_blocktime) ) { - Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_QUIT_KICK_SPECTATING); - dropclient(this); - } - } -} - -void PrintWelcomeMessage(entity this) -{ - if(this.motd_actived_time == 0) - { - if (autocvar_g_campaign) { - if ((IS_PLAYER(this) && PHYS_INPUT_BUTTON_INFO(this)) || (!IS_PLAYER(this))) { - this.motd_actived_time = time; - Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, campaign_message); - } - } else { - if (PHYS_INPUT_BUTTON_INFO(this)) { - this.motd_actived_time = time; - Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, getwelcomemessage(this)); - } - } - } - else if(this.motd_actived_time > 0) // showing MOTD or campaign message - { - if (autocvar_g_campaign) { - if (PHYS_INPUT_BUTTON_INFO(this)) - this.motd_actived_time = time; - else if ((time - this.motd_actived_time > 2) && IS_PLAYER(this)) { // hide it some seconds after BUTTON_INFO has been released - this.motd_actived_time = 0; - Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD); - } - } else { - if (PHYS_INPUT_BUTTON_INFO(this)) - this.motd_actived_time = time; - else if (time - this.motd_actived_time > 2) { // hide it some seconds after BUTTON_INFO has been released - this.motd_actived_time = 0; - Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD); - } - } - } - else //if(this.motd_actived_time < 0) // just connected, motd is active - { - if(PHYS_INPUT_BUTTON_INFO(this)) // BUTTON_INFO hides initial MOTD - this.motd_actived_time = -2; // wait until BUTTON_INFO gets released - else if(this.motd_actived_time == -2 || IS_PLAYER(this) || IS_SPEC(this)) - { - // instanctly hide MOTD - this.motd_actived_time = 0; - Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD); - } - } -} - -void ObserverThink(entity this) -{ - if ( this.impulse ) - { - MinigameImpulse(this, this.impulse); - this.impulse = 0; - } - if (this.flags & FL_JUMPRELEASED) { - if (PHYS_INPUT_BUTTON_JUMP(this) && !this.version_mismatch) { - this.flags &= ~FL_JUMPRELEASED; - this.flags |= FL_SPAWNING; - } else if(PHYS_INPUT_BUTTON_ATCK(this) && !this.version_mismatch) { - this.flags &= ~FL_JUMPRELEASED; - if(SpectateNext(this)) { - TRANSMUTE(Spectator, this); - } - } else { - int preferred_movetype = ((!PHYS_INPUT_BUTTON_USE(this) ? this.cvar_cl_clippedspectating : !this.cvar_cl_clippedspectating) ? MOVETYPE_FLY_WORLDONLY : MOVETYPE_NOCLIP); - set_movetype(this, preferred_movetype); - } - } else { - if (!(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this))) { - this.flags |= FL_JUMPRELEASED; - if(this.flags & FL_SPAWNING) - { - this.flags &= ~FL_SPAWNING; - LeaveSpectatorMode(this); - return; - } - } - } -} - -void SpectatorThink(entity this) -{ - if ( this.impulse ) - { - if(MinigameImpulse(this, this.impulse)) - this.impulse = 0; - - if (this.impulse == IMP_weapon_drop.impulse) - { - STAT(CAMERA_SPECTATOR, this) = (STAT(CAMERA_SPECTATOR, this) + 1) % 3; - this.impulse = 0; - return; - } - } - if (this.flags & FL_JUMPRELEASED) { - if (PHYS_INPUT_BUTTON_JUMP(this) && !this.version_mismatch) { - this.flags &= ~FL_JUMPRELEASED; - this.flags |= FL_SPAWNING; - } else if(PHYS_INPUT_BUTTON_ATCK(this) || this.impulse == 10 || this.impulse == 15 || this.impulse == 18 || (this.impulse >= 200 && this.impulse <= 209)) { - this.flags &= ~FL_JUMPRELEASED; - if(SpectateNext(this)) { - TRANSMUTE(Spectator, this); - } else { - TRANSMUTE(Observer, this); - PutClientInServer(this); - } - this.impulse = 0; - } else if(this.impulse == 12 || this.impulse == 16 || this.impulse == 19 || (this.impulse >= 220 && this.impulse <= 229)) { - this.flags &= ~FL_JUMPRELEASED; - if(SpectatePrev(this)) { - TRANSMUTE(Spectator, this); - } else { - TRANSMUTE(Observer, this); - PutClientInServer(this); - } - this.impulse = 0; - } else if (PHYS_INPUT_BUTTON_ATCK2(this)) { - this.flags &= ~FL_JUMPRELEASED; - TRANSMUTE(Observer, this); - PutClientInServer(this); - } else { - if(!SpectateUpdate(this)) - PutObserverInServer(this); - } - } else { - if (!(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this))) { - this.flags |= FL_JUMPRELEASED; - if(this.flags & FL_SPAWNING) - { - this.flags &= ~FL_SPAWNING; - LeaveSpectatorMode(this); - return; - } - } - if(!SpectateUpdate(this)) - PutObserverInServer(this); - } - - this.flags |= FL_CLIENT | FL_NOTARGET; -} - -void vehicles_enter (entity pl, entity veh); -void PlayerUseKey(entity this) -{ - if (!IS_PLAYER(this)) - return; - - if(this.vehicle) - { - if(!gameover) - { - vehicles_exit(this.vehicle, VHEF_NORMAL); - return; - } - } - else if(autocvar_g_vehicles_enter) - { - if(!STAT(FROZEN, this)) - if(!IS_DEAD(this)) - if(!gameover) - { - entity head, closest_target = NULL; - head = WarpZone_FindRadius(this.origin, autocvar_g_vehicles_enter_radius, true); - - while(head) // find the closest acceptable target to enter - { - if(IS_VEHICLE(head)) - if(!IS_DEAD(head)) - if(!head.owner || ((head.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(head.owner, this))) - if(head.takedamage != DAMAGE_NO) - { - if(closest_target) - { - if(vlen2(this.origin - head.origin) < vlen2(this.origin - closest_target.origin)) - { closest_target = head; } - } - else { closest_target = head; } - } - - head = head.chain; - } - - if(closest_target) { vehicles_enter(this, closest_target); return; } - } - } - - // a use key was pressed; call handlers - MUTATOR_CALLHOOK(PlayerUseKey, this); -} - - -/* -============= -PlayerPreThink - -Called every frame for each client before the physics are run -============= -*/ -.float usekeypressed; -.float last_vehiclecheck; -.int items_added; -void PlayerPreThink (entity this) -{ - WarpZone_PlayerPhysics_FixVAngle(this); - - STAT(GAMESTARTTIME, this) = game_starttime; - STAT(ROUNDSTARTTIME, this) = round_starttime; - STAT(ALLOW_OLDVORTEXBEAM, this) = autocvar_g_allow_oldvortexbeam; - STAT(LEADLIMIT, this) = autocvar_leadlimit; - - STAT(WEAPONSINMAP, this) = weaponsInMap; - - if (frametime) { - // physics frames: update anticheat stuff - anticheat_prethink(this); - } - - if (blockSpectators && frametime) { - // WORKAROUND: only use dropclient in server frames (frametime set). - // Never use it in cl_movement frames (frametime zero). - checkSpectatorBlock(this); - } - - zoomstate_set = false; - - // Check for nameless players - if (isInvisibleString(this.netname)) { - this.netname = strzone(sprintf("Player#%d", this.playerid)); - // stuffcmd(this, strcat("name ", this.netname, "\n")); // maybe? - } - if (this.netname != this.netname_previous) { - if (autocvar_sv_eventlog) { - GameLogEcho(strcat(":name:", ftos(this.playerid), ":", this.netname)); - } - if (this.netname_previous) strunzone(this.netname_previous); - this.netname_previous = strzone(this.netname); - } - - // version nagging - if (this.version_nagtime && this.cvar_g_xonoticversion && time > this.version_nagtime) { - this.version_nagtime = 0; - if (strstrofs(this.cvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(this.cvar_g_xonoticversion, "autobuild", 0) >= 0) { - // git client - } else if (strstrofs(autocvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(autocvar_g_xonoticversion, "autobuild", 0) >= 0) { - // git server - Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_BETA, autocvar_g_xonoticversion, this.cvar_g_xonoticversion); - } else { - int r = vercmp(this.cvar_g_xonoticversion, autocvar_g_xonoticversion); - if (r < 0) { // old client - Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OUTDATED, autocvar_g_xonoticversion, this.cvar_g_xonoticversion); - } else if (r > 0) { // old server - Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OLD, autocvar_g_xonoticversion, this.cvar_g_xonoticversion); - } - } - } - - // GOD MODE info - if (!(this.flags & FL_GODMODE) && this.max_armorvalue) - { - Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_GODMODE_OFF, this.max_armorvalue); - this.max_armorvalue = 0; - } - - if (STAT(FROZEN, this) == 2) - { - this.revive_progress = bound(0, this.revive_progress + frametime * this.revive_speed, 1); - this.health = max(1, this.revive_progress * start_health); - this.iceblock.alpha = bound(0.2, 1 - this.revive_progress, 1); - - if (this.revive_progress >= 1) - Unfreeze(this); - } - else if (STAT(FROZEN, this) == 3) - { - this.revive_progress = bound(0, this.revive_progress - frametime * this.revive_speed, 1); - this.health = max(0, autocvar_g_nades_ice_health + (start_health-autocvar_g_nades_ice_health) * this.revive_progress ); - - if (this.health < 1) - { - if (this.vehicle) - vehicles_exit(this.vehicle, VHEF_RELEASE); - if(this.event_damage) - this.event_damage(this, this, this.frozen_by, 1, DEATH_NADE_ICE_FREEZE.m_id, this.origin, '0 0 0'); - } - else if (this.revive_progress <= 0) - Unfreeze(this); - } - - MUTATOR_CALLHOOK(PlayerPreThink, this); - - if(autocvar_g_vehicles_enter && (time > this.last_vehiclecheck) && !gameover && !this.vehicle) - if(IS_PLAYER(this) && !STAT(FROZEN, this) && !IS_DEAD(this)) - { - FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_vehicles_enter_radius, IS_VEHICLE(it), - { - if(!IS_DEAD(it) && it.takedamage != DAMAGE_NO) - if((it.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(it.owner, this)) - { - Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_GUNNER); - } - else if(!it.owner) - { - if(!it.team || SAME_TEAM(this, it)) - Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER); - else if(autocvar_g_vehicles_steal) - Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_STEAL); - } - }); - - this.last_vehiclecheck = time + 1; - } - - if(!this.cvar_cl_newusekeysupported) // FIXME remove this - it was a stupid idea to begin with, we can JUST use the button - { - if(PHYS_INPUT_BUTTON_USE(this) && !this.usekeypressed) - PlayerUseKey(this); - this.usekeypressed = PHYS_INPUT_BUTTON_USE(this); - } - - if (IS_REAL_CLIENT(this)) - PrintWelcomeMessage(this); - - if (IS_PLAYER(this)) { - CheckRules_Player(this); - - if (intermission_running) { - IntermissionThink(this); - return; - } - - if (timeout_status == TIMEOUT_ACTIVE) { - // don't allow the player to turn around while game is paused - // FIXME turn this into CSQC stuff - this.v_angle = this.lastV_angle; - this.angles = this.lastV_angle; - this.fixangle = true; - } - - if (frametime) player_powerups(this); - - if (IS_DEAD(this)) { - if (this.personal && g_race_qualifying) { - if (time > this.respawn_time) { - STAT(RESPAWN_TIME, this) = this.respawn_time = time + 1; // only retry once a second - respawn(this); - this.impulse = CHIMPULSE_SPEEDRUN.impulse; - } - } else { - if (frametime) player_anim(this); - bool button_pressed = (PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_ATCK2(this) || PHYS_INPUT_BUTTON_HOOK(this) || PHYS_INPUT_BUTTON_USE(this)); - - switch(this.deadflag) - { - case DEAD_DYING: - { - if ((this.respawn_flags & RESPAWN_FORCE) && !(this.respawn_time < this.respawn_time_max)) - this.deadflag = DEAD_RESPAWNING; - else if (!button_pressed || (this.respawn_flags & RESPAWN_FORCE)) - this.deadflag = DEAD_DEAD; - break; - } - case DEAD_DEAD: - { - if (button_pressed) - this.deadflag = DEAD_RESPAWNABLE; - else if (time >= this.respawn_time_max && (this.respawn_flags & RESPAWN_FORCE)) - this.deadflag = DEAD_RESPAWNING; - break; - } - case DEAD_RESPAWNABLE: - { - if (!button_pressed || (this.respawn_flags & RESPAWN_FORCE)) - this.deadflag = DEAD_RESPAWNING; - break; - } - case DEAD_RESPAWNING: - { - if (time > this.respawn_time) - { - this.respawn_time = time + 1; // only retry once a second - this.respawn_time_max = this.respawn_time; - respawn(this); - } - break; - } - } - - ShowRespawnCountdown(this); - - if (this.respawn_flags & RESPAWN_SILENT) - STAT(RESPAWN_TIME, this) = 0; - else if ((this.respawn_flags & RESPAWN_FORCE) && this.respawn_time < this.respawn_time_max) - { - if (time < this.respawn_time) - STAT(RESPAWN_TIME, this) = this.respawn_time; - else if (this.deadflag != DEAD_RESPAWNING) - STAT(RESPAWN_TIME, this) = -this.respawn_time_max; - } - else - STAT(RESPAWN_TIME, this) = this.respawn_time; - } - - // if respawning, invert stat_respawn_time to indicate this, the client translates it - if (this.deadflag == DEAD_RESPAWNING && STAT(RESPAWN_TIME, this) > 0) - STAT(RESPAWN_TIME, this) *= -1; - - return; - } - - this.prevorigin = this.origin; - - bool do_crouch = PHYS_INPUT_BUTTON_CROUCH(this); - if (this.hook.state) { - do_crouch = false; - } else if (this.waterlevel >= WATERLEVEL_SWIMMING) { - do_crouch = false; - } else if (this.vehicle) { - do_crouch = false; - } else if (STAT(FROZEN, this)) { - do_crouch = false; - } - - if (do_crouch) { - if (!this.crouch) { - this.crouch = true; - this.view_ofs = STAT(PL_CROUCH_VIEW_OFS, this); - setsize(this, STAT(PL_CROUCH_MIN, this), STAT(PL_CROUCH_MAX, this)); - // setanim(this, this.anim_duck, false, true, true); // this anim is BROKEN anyway - } - } else if (this.crouch) { - tracebox(this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), this.origin, false, this); - if (!trace_startsolid) { - this.crouch = false; - this.view_ofs = STAT(PL_VIEW_OFS, this); - setsize(this, STAT(PL_MIN, this), STAT(PL_MAX, this)); - } - } - - FixPlayermodel(this); - - // LordHavoc: allow firing on move frames (sub-ticrate), this gives better timing on slow servers - //if(frametime) - { - this.items &= ~this.items_added; - - //for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) - //{ - //.entity weaponentity = weaponentities[slot]; - //W_WeaponFrame(this, weaponentity); - //} - .entity weaponentity = weaponentities[0]; // TODO - W_WeaponFrame(this, weaponentity); - - this.items_added = 0; - if (this.items & ITEM_Jetpack.m_itemid && (this.items & ITEM_JetpackRegen.m_itemid || this.ammo_fuel >= 0.01)) - this.items_added |= IT_FUEL; - - this.items |= this.items_added; - } - - player_regen(this); - - // WEAPONTODO: Add a weapon request for this - // rot vortex charge to the charge limit - if (WEP_CVAR(vortex, charge_rot_rate) && this.vortex_charge > WEP_CVAR(vortex, charge_limit) && this.vortex_charge_rottime < time) - this.vortex_charge = bound(WEP_CVAR(vortex, charge_limit), this.vortex_charge - WEP_CVAR(vortex, charge_rot_rate) * frametime / W_TICSPERFRAME, 1); - - if (frametime) player_anim(this); - - // secret status - secrets_setstatus(this); - - // monsters status - monsters_setstatus(this); - - this.dmg_team = max(0, this.dmg_team - autocvar_g_teamdamage_resetspeed * frametime); - } - else if (gameover) { - if (intermission_running) IntermissionThink(this); - return; - } - else if (IS_OBSERVER(this)) { - ObserverThink(this); - } - else if (IS_SPEC(this)) { - SpectatorThink(this); - } - - // WEAPONTODO: Add weapon request for this - if (!zoomstate_set) { - SetZoomState(this, - PHYS_INPUT_BUTTON_ZOOM(this) || PHYS_INPUT_BUTTON_ZOOMSCRIPT(this) - || (PHYS_INPUT_BUTTON_ATCK2(this) && PS(this).m_weapon == WEP_VORTEX) - || (PHYS_INPUT_BUTTON_ATCK2(this) && PS(this).m_weapon == WEP_RIFLE && WEP_CVAR(rifle, secondary) == 0) - ); - } - - if (this.teamkill_soundtime && time > this.teamkill_soundtime) - { - this.teamkill_soundtime = 0; - - entity e = this.teamkill_soundsource; - entity oldpusher = e.pusher; - e.pusher = this; - PlayerSound(e, playersound_teamshoot, CH_VOICE, VOL_BASEVOICE, VOICETYPE_LASTATTACKER_ONLY); - e.pusher = oldpusher; - } - - if (this.taunt_soundtime && time > this.taunt_soundtime) { - this.taunt_soundtime = 0; - PlayerSound(this, playersound_taunt, CH_VOICE, VOL_BASEVOICE, VOICETYPE_AUTOTAUNT); - } - - target_voicescript_next(this); - - // WEAPONTODO: Move into weaponsystem somehow - // if a player goes unarmed after holding a loaded weapon, empty his clip size and remove the crosshair ammo ring - if (PS(this).m_weapon == WEP_Null) - this.clip_load = this.clip_size = 0; -} - -void DrownPlayer(entity this) -{ - if(IS_DEAD(this)) - return; - - if (this.waterlevel != WATERLEVEL_SUBMERGED || this.vehicle) - { - if(this.air_finished < time) - PlayerSound(this, playersound_gasp, CH_PLAYER, VOL_BASE, VOICETYPE_PLAYERSOUND); - this.air_finished = time + autocvar_g_balance_contents_drowndelay; - this.dmg = 2; - } - else if (this.air_finished < time) - { // drown! - if (this.pain_finished < time) - { - Damage (this, NULL, NULL, autocvar_g_balance_contents_playerdamage_drowning * autocvar_g_balance_contents_damagerate, DEATH_DROWN.m_id, this.origin, '0 0 0'); - this.pain_finished = time + 0.5; - } - } -} - -void Player_Physics(entity this) -{ - set_movetype(this, ((this.move_qcphysics) ? MOVETYPE_NONE : this.move_movetype)); - - if(!this.move_qcphysics) - return; - - int mt = this.move_movetype; - - if(mt == MOVETYPE_PUSH || mt == MOVETYPE_FAKEPUSH || mt == MOVETYPE_PHYSICS) - { - this.move_qcphysics = false; - set_movetype(this, mt); - return; - } - - if(!frametime && !this.pm_frametime) - return; - - Movetype_Physics_NoMatchTicrate(this, this.pm_frametime, true); - - this.pm_frametime = 0; -} - -/* -============= -PlayerPostThink - -Called every frame for each client after the physics are run -============= -*/ -.float idlekick_lasttimeleft; -void PlayerPostThink (entity this) -{ - Player_Physics(this); - - if (sv_maxidle > 0) - if (frametime) // WORKAROUND: only use dropclient in server frames (frametime set). Never use it in cl_movement frames (frametime zero). - if (IS_REAL_CLIENT(this)) - if (IS_PLAYER(this) || sv_maxidle_spectatorsareidle) - { - int totalClients = 0; - if(sv_maxidle_slots > 0) - { - FOREACH_CLIENT(IS_REAL_CLIENT(it) || sv_maxidle_slots_countbots, - { - ++totalClients; - }); - } - - if (sv_maxidle_slots > 0 && (maxclients - totalClients) > sv_maxidle_slots) - { /* do nothing */ } - else if (time - this.parm_idlesince < 1) // instead of (time == this.parm_idlesince) to support sv_maxidle <= 10 - { - if (this.idlekick_lasttimeleft) - { - this.idlekick_lasttimeleft = 0; - Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_IDLING); - } - } - else - { - float timeleft = ceil(sv_maxidle - (time - this.parm_idlesince)); - if (timeleft == min(10, sv_maxidle - 1)) { // - 1 to support sv_maxidle <= 10 - if (!this.idlekick_lasttimeleft) - Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_DISCONNECT_IDLING, timeleft); - } - if (timeleft <= 0) { - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_KICK_IDLING, this.netname); - dropclient(this); - return; - } - else if (timeleft <= 10) { - if (timeleft != this.idlekick_lasttimeleft) { - Send_Notification(NOTIF_ONE, this, MSG_ANNCE, Announcer_PickNumber(CNT_IDLE, timeleft)); - } - this.idlekick_lasttimeleft = timeleft; - } - } - } - - CheatFrame(this); - - //CheckPlayerJump(); - - if (IS_PLAYER(this)) { - DrownPlayer(this); - CheckRules_Player(this); - UpdateChatBubble(this); - if (this.impulse) ImpulseCommands(this); - if (intermission_running) return; // intermission or finale - GetPressedKeys(this); - } - - if (this.waypointsprite_attachedforcarrier) { - vector v = healtharmor_maxdamage(this.health, this.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id); - WaypointSprite_UpdateHealth(this.waypointsprite_attachedforcarrier, '1 0 0' * v); - } - - playerdemo_write(this); - - CSQCMODEL_AUTOUPDATE(this); -} diff --git a/qcsrc/server/cl_client.qh b/qcsrc/server/cl_client.qh deleted file mode 100644 index 35ff6e961c..0000000000 --- a/qcsrc/server/cl_client.qh +++ /dev/null @@ -1,109 +0,0 @@ -#pragma once - -void ClientState_attach(entity this); - -CLASS(Client, Object) - /** Client name */ - ATTRIB(Client, netname, string, this.netname); - ATTRIB(Client, colormap, int, this.colormap); - ATTRIB(Client, team, int, this.team); - ATTRIB(Client, clientcolors, int, this.clientcolors); - /** Client IP */ - ATTRIB(Client, netaddress, string, this.netaddress); - ATTRIB(Client, playermodel, string, this.playermodel); - ATTRIB(Client, playerskin, int, this.playerskin); - - /** fingerprint of CA key the player used to authenticate */ - ATTRIB(Client, crypto_keyfp, string, this.crypto_keyfp); - /** fingerprint of CA key the server used to authenticate to the player */ - ATTRIB(Client, crypto_mykeyfp, string, this.crypto_mykeyfp); - /** fingerprint of ID used by the player entity, or string_null if not identified */ - ATTRIB(Client, crypto_idfp, string, this.crypto_idfp); - /** set if the player's ID has been signed */ - ATTRIB(Client, crypto_idfp_signed, bool, this.crypto_idfp_signed); - /** the string "AES128" if encrypting, and string_null if plaintext */ - ATTRIB(Client, crypto_encryptmethod, string, this.crypto_encryptmethod); - /** the string "HMAC-SHA256" if signing, and string_null if plaintext */ - ATTRIB(Client, crypto_signmethod, string, this.crypto_signmethod); - - // custom - - ATTRIB(Client, playerid, int, this.playerid); - - METHOD(Client, m_unwind, bool(Client this)); - - STATIC_METHOD(Client, Add, void(Client this, int _team)); - STATIC_METHOD(Client, Remove, void(Client this)); - - INIT(Client) { - if (this.m_unwind(this)) return this; - make_impure(this); - this.classname = "player_joining"; - static int playerid_last; - this.playerid = ++playerid_last; - ClientState_attach(this); - } - DESTRUCTOR(Client) { - Client_Remove(this); - } - CONSTRUCTOR(Client, string name) { - CONSTRUCT(Client); - this.netname = name; - this.netaddress = "local"; - this.playermodel = "models/player/megaerebus.iqm"; - } -ENDCLASS(Client) - -CLASS(Observer, Client) - INIT(Observer) { - this.classname = STR_OBSERVER; - } - DESTRUCTOR(Observer) { } -ENDCLASS(Observer) - -CLASS(Spectator, Client) - INIT(Spectator) { - this.classname = STR_SPECTATOR; - } - DESTRUCTOR(Spectator) { } -ENDCLASS(Spectator) - -CLASS(Player, Client) - INIT(Player) { - this.classname = STR_PLAYER; - } - DESTRUCTOR(Player) { } -ENDCLASS(Player) - -METHOD(Client, m_unwind, bool(Client this)) -{ - TC(Client, this); - #define UNWIND(class) MACRO_BEGIN if (this.instanceOf##class) { METHOD_REFERENCE(class, dtorimpl)(this); } MACRO_END - switch (this.classname) { - case "Observer": - UNWIND(Spectator); - UNWIND(Player); - return true; - case "Spectator": - UNWIND(Observer); - UNWIND(Player); - return true; - case "Player": - UNWIND(Observer); - UNWIND(Spectator); - return true; - } - #undef UNWIND - return false; -} - -float c1, c2, c3, c4; - -void play_countdown(entity this, float finished, Sound samp); - -float CalcRotRegen(float current, float regenstable, float regenfactor, float regenlinear, float regenframetime, float rotstable, float rotfactor, float rotlinear, float rotframetime, float limit); - -bool Spectate(entity this, entity pl); - -#define SPECTATE_COPY() [[accumulate]] void SpectateCopy(entity this, entity spectatee) -#define SPECTATE_COPYFIELD(fld) SPECTATE_COPY() { this.(fld) = spectatee.(fld); } diff --git a/qcsrc/server/cl_impulse.qc b/qcsrc/server/cl_impulse.qc deleted file mode 100644 index 00c4ec22cd..0000000000 --- a/qcsrc/server/cl_impulse.qc +++ /dev/null @@ -1,606 +0,0 @@ -#include "cl_impulse.qh" -#include "round_handler.qh" - -#include "bot/api.qh" - -#include "weapons/throwing.qh" -#include "command/common.qh" -#include "cheats.qh" -#include "weapons/selection.qh" -#include "weapons/tracing.qh" -#include "weapons/weaponsystem.qh" - -#include - -#include "../common/minigames/sv_minigames.qh" - -#include "../common/weapons/all.qh" -#include "../common/vehicles/sv_vehicles.qh" - -#include "../common/mutators/mutator/waypoints/waypointsprites.qh" - -.entity vehicle; - -#define IMPULSE(id) _IMPULSE(IMP_##id) -#define _IMPULSE(id) \ - void id##_handle(entity this); \ - STATIC_INIT_LATE(id) \ - { \ - id.impulse_handle = id##_handle; \ - } \ - void id##_handle(entity this) - -/** - * Impulse map: - * - * 0 reserved (no input) - * - * 99: loaded - * - * 140: moving clone - * 141: ctf speedrun - * 142: fixed clone - * 143: emergency teleport - * 148: unfairly eliminate - * - * TODO: - * 200 to 209: prev weapon shortcuts - * 210 to 219: best weapon shortcuts - * 220 to 229: next weapon shortcuts - * 230 to 253: individual weapons (up to 24) - */ - -// weapon switching impulses - -#define X(slot) \ - IMPULSE(weapon_group_##slot) \ - { \ - if (IS_DEAD(this)) \ - { \ - this.impulse = IMP_weapon_group_##slot.impulse; \ - return; \ - } \ - W_NextWeaponOnImpulse(this, slot); \ - } -X(1) -X(2) -X(3) -X(4) -X(5) -X(6) -X(7) -X(8) -X(9) -X(0) -#undef X - -// custom order weapon cycling - -#define X(slot, dir) \ - IMPULSE(weapon_priority_##slot##_##dir) \ - { \ - if (this.vehicle) return; \ - if (IS_DEAD(this)) \ - { \ - this.impulse = IMP_weapon_priority_##slot##_##dir.impulse; \ - return; \ - } \ - noref int prev = -1; \ - noref int best = 0; \ - noref int next = +1; \ - W_CycleWeapon(this, this.cvar_cl_weaponpriorities[slot], dir); \ - } -X(0, prev) -X(1, prev) -X(2, prev) -X(3, prev) -X(4, prev) -X(5, prev) -X(6, prev) -X(7, prev) -X(8, prev) -X(9, prev) - -X(0, best) -X(1, best) -X(2, best) -X(3, best) -X(4, best) -X(5, best) -X(6, best) -X(7, best) -X(8, best) -X(9, best) - -X(0, next) -X(1, next) -X(2, next) -X(3, next) -X(4, next) -X(5, next) -X(6, next) -X(7, next) -X(8, next) -X(9, next) -#undef X - -// direct weapons - -#define X(i) \ - IMPULSE(weapon_byid_##i) \ - { \ - if (this.vehicle) return; \ - if (IS_DEAD(this)) \ - { \ - this.impulse = IMP_weapon_byid_##i.impulse; \ - return; \ - } \ - W_SwitchWeapon(this, Weapons_from(WEP_FIRST + i)); \ - } -X(0) -X(1) -X(2) -X(3) -X(4) -X(5) -X(6) -X(7) -X(8) -X(9) -X(10) -X(11) -X(12) -X(13) -X(14) -X(15) -X(16) -X(17) -X(18) -X(19) -X(20) -X(21) -X(22) -X(23) -#undef X - -IMPULSE(weapon_next_byid) -{ - if (this.vehicle) return; - if (IS_DEAD(this)) - { - this.impulse = IMP_weapon_next_byid.impulse; - return; - } - W_NextWeapon(this, 0); -} - -IMPULSE(weapon_prev_byid) -{ - if (this.vehicle) return; - if (IS_DEAD(this)) - { - this.impulse = IMP_weapon_prev_byid.impulse; - return; - } - W_PreviousWeapon(this, 0); -} - -IMPULSE(weapon_next_bygroup) -{ - if (this.vehicle) return; - if (IS_DEAD(this)) - { - this.impulse = IMP_weapon_next_bygroup.impulse; - return; - } - W_NextWeapon(this, 1); -} - -IMPULSE(weapon_prev_bygroup) -{ - if (this.vehicle) return; - if (IS_DEAD(this)) - { - this.impulse = IMP_weapon_prev_bygroup.impulse; - return; - } - W_PreviousWeapon(this, 1); -} - -IMPULSE(weapon_next_bypriority) -{ - if (this.vehicle) return; - if (IS_DEAD(this)) - { - this.impulse = IMP_weapon_next_bypriority.impulse; - return; - } - W_NextWeapon(this, 2); -} - -IMPULSE(weapon_prev_bypriority) -{ - if (this.vehicle) return; - if (IS_DEAD(this)) - { - this.impulse = IMP_weapon_prev_bypriority.impulse; - return; - } - W_PreviousWeapon(this, 2); -} - -IMPULSE(weapon_last) -{ - if (this.vehicle) return; - if (IS_DEAD(this)) return; - W_LastWeapon(this); -} - -IMPULSE(weapon_best) -{ - if (this.vehicle) return; - if (IS_DEAD(this)) return; - W_SwitchWeapon(this, w_getbestweapon(this)); -} - -IMPULSE(weapon_drop) -{ - if (this.vehicle) return; - if (IS_DEAD(this)) return; - W_ThrowWeapon(this, weaponentities[0], W_CalculateProjectileVelocity(this, this.velocity, v_forward * 750, false), '0 0 0', true); -} - -IMPULSE(weapon_reload) -{ - if (this.vehicle) return; - if (IS_DEAD(this)) return; - if (forbidWeaponUse(this)) return; - Weapon w = PS(this).m_weapon; - entity actor = this; - for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) - { - .entity weaponentity = weaponentities[slot]; - w.wr_reload(w, actor, weaponentity); - } -} - -void ImpulseCommands(entity this) -{ - if (gameover) return; - - int imp = this.impulse; - if (!imp) return; - this.impulse = 0; - - if (MinigameImpulse(this, imp)) return; - - if (timeout_status == TIMEOUT_ACTIVE) return; // don't allow any impulses while the game is paused - - // allow only weapon change impulses when not in round time - if (round_handler_IsActive() && !round_handler_IsRoundStarted()) - { - #define X(id) case IMP_##id.impulse: - switch (imp) - { - X(weapon_group_0) - X(weapon_group_1) - X(weapon_group_2) - X(weapon_group_3) - X(weapon_group_4) - X(weapon_group_5) - X(weapon_group_6) - X(weapon_group_7) - X(weapon_group_8) - X(weapon_group_9) - X(weapon_next_byid) - X(weapon_prev_byid) - X(weapon_next_bygroup) - X(weapon_prev_bygroup) - X(weapon_next_bypriority) - X(weapon_prev_bypriority) - X(weapon_last) - X(weapon_best) - X(weapon_reload) - X(weapon_priority_0_prev) - X(weapon_priority_1_prev) - X(weapon_priority_2_prev) - X(weapon_priority_3_prev) - X(weapon_priority_4_prev) - X(weapon_priority_5_prev) - X(weapon_priority_6_prev) - X(weapon_priority_7_prev) - X(weapon_priority_8_prev) - X(weapon_priority_9_prev) - X(weapon_priority_0_next) - X(weapon_priority_1_next) - X(weapon_priority_2_next) - X(weapon_priority_3_next) - X(weapon_priority_4_next) - X(weapon_priority_5_next) - X(weapon_priority_6_next) - X(weapon_priority_7_next) - X(weapon_priority_8_next) - X(weapon_priority_9_next) - X(weapon_priority_0_best) - X(weapon_priority_1_best) - X(weapon_priority_2_best) - X(weapon_priority_3_best) - X(weapon_priority_4_best) - X(weapon_priority_5_best) - X(weapon_priority_6_best) - X(weapon_priority_7_best) - X(weapon_priority_8_best) - X(weapon_priority_9_best) - X(weapon_byid_0) - X(weapon_byid_1) - X(weapon_byid_2) - X(weapon_byid_3) - X(weapon_byid_4) - X(weapon_byid_5) - X(weapon_byid_6) - X(weapon_byid_7) - X(weapon_byid_8) - X(weapon_byid_9) - X(weapon_byid_10) - X(weapon_byid_11) - X(weapon_byid_12) - X(weapon_byid_13) - X(weapon_byid_14) - X(weapon_byid_15) - X(weapon_byid_16) - X(weapon_byid_17) - X(weapon_byid_18) - X(weapon_byid_19) - X(weapon_byid_20) - X(weapon_byid_21) - X(weapon_byid_22) - X(weapon_byid_23) - break; - default: return; - } -#undef X - } - - if (vehicle_impulse(this, imp)) return; - - if (CheatImpulse(this, imp)) return; - - FOREACH(IMPULSES, it.impulse == imp, { - void(entity) f = it.impulse_handle; - if (!f) continue; - f(this); - return; - }); -} - -IMPULSE(use) -{ - PlayerUseKey(this); -} - -IMPULSE(waypoint_personal_here) -{ - entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.origin, RADARICON_WAYPOINT); - if (wp) WaypointSprite_Ping(wp); - sprint(this, "personal waypoint spawned at location\n"); -} - -IMPULSE(waypoint_personal_crosshair) -{ - WarpZone_crosshair_trace(this); - entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, trace_endpos, RADARICON_WAYPOINT); - if (wp) WaypointSprite_Ping(wp); - sprint(this, "personal waypoint spawned at crosshair\n"); -} - -IMPULSE(waypoint_personal_death) -{ - if (!this.death_origin) return; - entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.death_origin, RADARICON_WAYPOINT); - if (wp) WaypointSprite_Ping(wp); - sprint(this, "personal waypoint spawned at death location\n"); -} - -IMPULSE(waypoint_here_follow) -{ - if (!teamplay) return; - if (IS_DEAD(this)) return; - if (!MUTATOR_CALLHOOK(HelpMePing, this)) - { - entity wp = WaypointSprite_Attach(WP_Helpme, this, true, RADARICON_HELPME); - if (!wp) WaypointSprite_HelpMePing(this.waypointsprite_attachedforcarrier); - else WaypointSprite_Ping(wp); - } - sprint(this, "HELP ME attached\n"); -} - -IMPULSE(waypoint_here_here) -{ - entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.origin, RADARICON_HERE); - if (wp) WaypointSprite_Ping(wp); - sprint(this, "HERE spawned at location\n"); -} - -IMPULSE(waypoint_here_crosshair) -{ - WarpZone_crosshair_trace(this); - entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, trace_endpos, RADARICON_HERE); - if (wp) WaypointSprite_Ping(wp); - sprint(this, "HERE spawned at crosshair\n"); -} - -IMPULSE(waypoint_here_death) -{ - if (!this.death_origin) return; - entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.death_origin, RADARICON_HERE); - if (wp) WaypointSprite_Ping(wp); - sprint(this, "HERE spawned at death location\n"); -} - -IMPULSE(waypoint_danger_here) -{ - entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.origin, RADARICON_DANGER); - if (wp) WaypointSprite_Ping(wp); - sprint(this, "DANGER spawned at location\n"); -} - -IMPULSE(waypoint_danger_crosshair) -{ - WarpZone_crosshair_trace(this); - entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, trace_endpos, RADARICON_DANGER); - if (wp) WaypointSprite_Ping(wp); - sprint(this, "DANGER spawned at crosshair\n"); -} - -IMPULSE(waypoint_danger_death) -{ - if (!this.death_origin) return; - entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.death_origin, RADARICON_DANGER); - if (wp) WaypointSprite_Ping(wp); - sprint(this, "DANGER spawned at death location\n"); -} - -IMPULSE(waypoint_clear_personal) -{ - WaypointSprite_ClearPersonal(this); - if (this.personal) - { - delete(this.personal); - this.personal = NULL; - } - sprint(this, "personal waypoint cleared\n"); -} - -IMPULSE(waypoint_clear) -{ - WaypointSprite_ClearOwned(this); - if (this.personal) - { - delete(this.personal); - this.personal = NULL; - } - sprint(this, "all waypoints cleared\n"); -} - -IMPULSE(navwaypoint_spawn) -{ - if (!autocvar_g_waypointeditor) return; - waypoint_schedulerelink(waypoint_spawn(this.origin, this.origin, 0)); - bprint(strcat("Waypoint spawned at ", vtos(this.origin), "\n")); -} - -IMPULSE(navwaypoint_remove) -{ - if (!autocvar_g_waypointeditor) return; - entity e = navigation_findnearestwaypoint(this, false); - if (!e) return; - if (e.wpflags & WAYPOINTFLAG_GENERATED) return; - bprint(strcat("Waypoint removed at ", vtos(e.origin), "\n")); - waypoint_remove(e); -} - -IMPULSE(navwaypoint_relink) -{ - if (!autocvar_g_waypointeditor) return; - waypoint_schedulerelinkall(); -} - -IMPULSE(navwaypoint_save) -{ - if (!autocvar_g_waypointeditor) return; - waypoint_saveall(); -} - -IMPULSE(navwaypoint_unreachable) -{ - if (!autocvar_g_waypointeditor) return; - IL_EACH(g_waypoints, true, - { - it.colormod = '0.5 0.5 0.5'; - it.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE); - }); - entity e2 = navigation_findnearestwaypoint(this, false); - navigation_markroutes(this, e2); - - int j, m; - - j = 0; - m = 0; - IL_EACH(g_waypoints, it.wpcost >= 10000000, - { - LOG_INFO("unreachable: ", etos(it), " ", vtos(it.origin), "\n"); - it.colormod_z = 8; - it.effects |= EF_NODEPTHTEST | EF_BLUE; - ++j; - ++m; - }); - if (j) LOG_INFOF("%d waypoints cannot be reached from here in any way (marked with blue light)\n", j); - navigation_markroutes_inverted(e2); - - j = 0; - IL_EACH(g_waypoints, it.wpcost >= 10000000, - { - LOG_INFO("cannot reach me: ", etos(it), " ", vtos(it.origin), "\n"); - it.colormod_x = 8; - if (!(it.effects & EF_NODEPTHTEST)) // not already reported before - ++m; - it.effects |= EF_NODEPTHTEST | EF_RED; - ++j; - }); - if (j) LOG_INFOF("%d waypoints cannot walk to here in any way (marked with red light)\n", j); - if (m) LOG_INFOF("%d waypoints have been marked total\n", m); - - j = 0; - FOREACH_ENTITY_CLASS("info_player_deathmatch", true, - { - vector org = it.origin; - tracebox(it.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - '0 0 512', MOVE_NOMONSTERS, NULL); - setorigin(it, trace_endpos); - if (navigation_findnearestwaypoint(it, false)) - { - setorigin(it, org); - it.effects &= ~EF_NODEPTHTEST; - it.model = ""; - } - else - { - setorigin(it, org); - LOG_INFO("spawn without waypoint: ", etos(it), " ", vtos(it.origin), "\n"); - it.effects |= EF_NODEPTHTEST; - _setmodel(it, this.model); - it.frame = this.frame; - it.skin = this.skin; - it.colormod = '8 0.5 8'; - setsize(it, '0 0 0', '0 0 0'); - ++j; - } - }); - if (j) LOG_INFOF("%d spawnpoints have no nearest waypoint (marked by player model)\n", j); - - j = 0; - FOREACH_ENTITY_FLAGS(flags, FL_ITEM, - { - it.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE); - it.colormod = '0.5 0.5 0.5'; - }); - FOREACH_ENTITY_FLAGS(flags, FL_ITEM, - { - if (navigation_findnearestwaypoint(it, false)) continue; - LOG_INFO("item without waypoint: ", etos(it), " ", vtos(it.origin), "\n"); - it.effects |= EF_NODEPTHTEST | EF_RED; - it.colormod_x = 8; - ++j; - }); - if (j) LOG_INFOF("%d items have no nearest waypoint and cannot be walked away from (marked with red light)\n", j); - - j = 0; - FOREACH_ENTITY_FLAGS(flags, FL_ITEM, - { - if (navigation_findnearestwaypoint(it, true)) continue; - LOG_INFO("item without waypoint: ", etos(it), " ", vtos(it.origin), "\n"); - it.effects |= EF_NODEPTHTEST | EF_BLUE; - it.colormod_z = 8; - ++j; - }); - if (j) LOG_INFOF("%d items have no nearest waypoint and cannot be walked to (marked with blue light)\n", j); -} diff --git a/qcsrc/server/cl_impulse.qh b/qcsrc/server/cl_impulse.qh deleted file mode 100644 index 50edc2c9c5..0000000000 --- a/qcsrc/server/cl_impulse.qh +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void ImpulseCommands(entity this); diff --git a/qcsrc/server/cl_player.qc b/qcsrc/server/cl_player.qc deleted file mode 100644 index 1ae97c99e5..0000000000 --- a/qcsrc/server/cl_player.qc +++ /dev/null @@ -1,962 +0,0 @@ -#include "cl_player.qh" - -#include "bot/api.qh" -#include "cheats.qh" -#include "g_damage.qh" -#include "g_subs.qh" -#include "miscfunctions.qh" -#include "portals.qh" -#include "teamplay.qh" -#include "weapons/throwing.qh" -#include "command/common.qh" -#include "../common/state.qh" -#include "../common/anim.qh" -#include "../common/animdecide.qh" -#include "../common/csqcmodel_settings.qh" -#include "../common/deathtypes/all.qh" -#include "../common/triggers/subs.qh" -#include "../common/playerstats.qh" -#include "../lib/csqcmodel/sv_model.qh" - -#include "../common/minigames/sv_minigames.qh" - -#include "../common/physics/player.qh" -#include "../common/effects/qc/all.qh" -#include "../common/mutators/mutator/waypoints/waypointsprites.qh" -#include "../common/triggers/include.qh" - -#include "weapons/weaponstats.qh" - -#include "../common/animdecide.qh" - -void Drop_Special_Items(entity player) -{ - // called when the player has become stuck or frozen - // so objective items aren't stuck with the player - - MUTATOR_CALLHOOK(DropSpecialItems, player); -} - -void CopyBody_Think(entity this) -{ - if(this.CopyBody_nextthink && time > this.CopyBody_nextthink) - { - this.CopyBody_think(this); - if(wasfreed(this)) - return; - this.CopyBody_nextthink = this.nextthink; - this.CopyBody_think = getthink(this); - setthink(this, CopyBody_Think); - } - CSQCMODEL_AUTOUPDATE(this); - this.nextthink = time; -} -void CopyBody(entity this, float keepvelocity) -{ - if (this.effects & EF_NODRAW) - return; - entity clone = new(body); - clone.enemy = this; - clone.lip = this.lip; - clone.colormap = this.colormap; - clone.iscreature = this.iscreature; - clone.teleportable = this.teleportable; - clone.damagedbycontents = this.damagedbycontents; - clone.angles = this.angles; - clone.v_angle = this.v_angle; - clone.avelocity = this.avelocity; - clone.damageforcescale = this.damageforcescale; - clone.effects = this.effects; - clone.glowmod = this.glowmod; - clone.event_damage = this.event_damage; - clone.anim_state = this.anim_state; - clone.anim_time = this.anim_time; - clone.anim_lower_action = this.anim_lower_action; - clone.anim_lower_time = this.anim_lower_time; - clone.anim_upper_action = this.anim_upper_action; - clone.anim_upper_time = this.anim_upper_time; - clone.anim_implicit_state = this.anim_implicit_state; - clone.anim_implicit_time = this.anim_implicit_time; - clone.anim_lower_implicit_action = this.anim_lower_implicit_action; - clone.anim_lower_implicit_time = this.anim_lower_implicit_time; - clone.anim_upper_implicit_action = this.anim_upper_implicit_action; - clone.anim_upper_implicit_time = this.anim_upper_implicit_time; - clone.dphitcontentsmask = this.dphitcontentsmask; - clone.death_time = this.death_time; - clone.pain_finished = this.pain_finished; - clone.health = this.health; - clone.armorvalue = this.armorvalue; - clone.armortype = this.armortype; - clone.model = this.model; - clone.modelindex = this.modelindex; - clone.skin = this.skin; - clone.species = this.species; - clone.move_qcphysics = false; // don't run gamecode logic on clones, too many - set_movetype(clone, this.move_movetype); - clone.solid = this.solid; - clone.ballistics_density = this.ballistics_density; - clone.takedamage = this.takedamage; - setcefc(clone, getcefc(this)); - clone.uncustomizeentityforclient = this.uncustomizeentityforclient; - clone.uncustomizeentityforclient_set = this.uncustomizeentityforclient_set; - if (keepvelocity == 1) - clone.velocity = this.velocity; - clone.oldvelocity = clone.velocity; - clone.alpha = this.alpha; - clone.fade_time = this.fade_time; - clone.fade_rate = this.fade_rate; - //clone.weapon = this.weapon; - setorigin(clone, this.origin); - setsize(clone, this.mins, this.maxs); - clone.prevorigin = this.origin; - clone.reset = SUB_Remove; - clone._ps = this._ps; - - Drag_MoveDrag(this, clone); - - if(clone.colormap <= maxclients && clone.colormap > 0) - clone.colormap = 1024 + this.clientcolors; - - CSQCMODEL_AUTOINIT(clone); - clone.CopyBody_nextthink = this.nextthink; - clone.CopyBody_think = getthink(this); - clone.nextthink = time; - setthink(clone, CopyBody_Think); - // "bake" the current animation frame for clones (they don't get clientside animation) - animdecide_load_if_needed(clone); - animdecide_setframes(clone, false, frame, frame1time, frame2, frame2time); -} - -void player_setupanimsformodel(entity this) -{ - // load animation info - animdecide_load_if_needed(this); - animdecide_setstate(this, 0, false); -} - -void player_anim(entity this) -{ - int deadbits = (this.anim_state & (ANIMSTATE_DEAD1 | ANIMSTATE_DEAD2)); - if(IS_DEAD(this)) { - if (!deadbits) { - // Decide on which death animation to use. - if(random() < 0.5) - deadbits = ANIMSTATE_DEAD1; - else - deadbits = ANIMSTATE_DEAD2; - } - } else { - // Clear a previous death animation. - deadbits = 0; - } - int animbits = deadbits; - if(STAT(FROZEN, this)) - animbits |= ANIMSTATE_FROZEN; - if(this.move_movetype == MOVETYPE_FOLLOW) - animbits |= ANIMSTATE_FOLLOW; - if(this.crouch) - animbits |= ANIMSTATE_DUCK; - animdecide_setstate(this, animbits, false); - animdecide_setimplicitstate(this, IS_ONGROUND(this)); -} - -void PlayerCorpseDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) -{ - float take, save; - vector v; - Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, this, attacker); - - // damage resistance (ignore most of the damage from a bullet or similar) - damage = max(damage - 5, 1); - - v = healtharmor_applydamage(this.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, damage); - take = v.x; - save = v.y; - - if(sound_allowed(MSG_BROADCAST, attacker)) - { - if (save > 10) - sound (this, CH_SHOTS, SND_ARMORIMPACT, VOL_BASE, ATTEN_NORM); - else if (take > 30) - sound (this, CH_SHOTS, SND_BODYIMPACT2, VOL_BASE, ATTEN_NORM); - else if (take > 10) - sound (this, CH_SHOTS, SND_BODYIMPACT1, VOL_BASE, ATTEN_NORM); - } - - if (take > 50) - Violence_GibSplash_At(hitloc, force * -0.1, 3, 1, this, attacker); - if (take > 100) - Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, this, attacker); - - this.armorvalue = this.armorvalue - save; - this.health = this.health - take; - // pause regeneration for 5 seconds - this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_health_regen); - - this.dmg_save = this.dmg_save + save;//max(save - 10, 0); - this.dmg_take = this.dmg_take + take;//max(take - 10, 0); - this.dmg_inflictor = inflictor; - - if (this.health <= -autocvar_sv_gibhealth && this.alpha >= 0) - { - // don't use any animations as a gib - this.frame = 0; - // view just above the floor - this.view_ofs = '0 0 4'; - - Violence_GibSplash(this, 1, 1, attacker); - this.alpha = -1; - this.solid = SOLID_NOT; // restore later - this.takedamage = DAMAGE_NO; // restore later - this.damagedbycontents = false; - } -} - -void calculate_player_respawn_time(entity this) -{ - if(g_ca) - return; - - float gametype_setting_tmp; - float sdelay_max = GAMETYPE_DEFAULTED_SETTING(respawn_delay_max); - float sdelay_small = GAMETYPE_DEFAULTED_SETTING(respawn_delay_small); - float sdelay_large = GAMETYPE_DEFAULTED_SETTING(respawn_delay_large); - float sdelay_small_count = GAMETYPE_DEFAULTED_SETTING(respawn_delay_small_count); - float sdelay_large_count = GAMETYPE_DEFAULTED_SETTING(respawn_delay_large_count); - float waves = GAMETYPE_DEFAULTED_SETTING(respawn_waves); - - float pcount = 1; // Include myself whether or not team is already set right and I'm a "player". - if (teamplay) - { - FOREACH_CLIENT(IS_PLAYER(it) && it != this, LAMBDA( - if(it.team == this.team) - ++pcount; - )); - if (sdelay_small_count == 0) - sdelay_small_count = 1; - if (sdelay_large_count == 0) - sdelay_large_count = 1; - } - else - { - FOREACH_CLIENT(IS_PLAYER(it) && it != this, LAMBDA( - ++pcount; - )); - if (sdelay_small_count == 0) - { - if (g_cts) - { - // Players play independently. No point in requiring enemies. - sdelay_small_count = 1; - } - else - { - // Players play AGAINST each other. Enemies required. - sdelay_small_count = 2; - } - } - if (sdelay_large_count == 0) - { - if (g_cts) - { - // Players play independently. No point in requiring enemies. - sdelay_large_count = 1; - } - else - { - // Players play AGAINST each other. Enemies required. - sdelay_large_count = 2; - } - } - } - - float sdelay; - - if (pcount <= sdelay_small_count) - sdelay = sdelay_small; - else if (pcount >= sdelay_large_count) - sdelay = sdelay_large; - else // NOTE: this case implies sdelay_large_count > sdelay_small_count. - sdelay = sdelay_small + (sdelay_large - sdelay_small) * (pcount - sdelay_small_count) / (sdelay_large_count - sdelay_small_count); - - if(waves) - this.respawn_time = ceil((time + sdelay) / waves) * waves; - else - this.respawn_time = time + sdelay; - - if(sdelay < sdelay_max) - this.respawn_time_max = time + sdelay_max; - else - this.respawn_time_max = this.respawn_time; - - if((sdelay + waves >= 5.0) && (this.respawn_time - time > 1.75)) - this.respawn_countdown = 10; // first number to count down from is 10 - else - this.respawn_countdown = -1; // do not count down - - if(autocvar_g_forced_respawn) - this.respawn_flags = this.respawn_flags | RESPAWN_FORCE; -} - -void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) -{ - float take, save, dh, da; - vector v; - float valid_damage_for_weaponstats; - float excess; - - dh = max(this.health, 0); - da = max(this.armorvalue, 0); - - if(!DEATH_ISSPECIAL(deathtype)) - { - damage *= sqrt(bound(1.0, this.cvar_cl_handicap, 100.0)); - if(this != attacker) - damage /= sqrt(bound(1.0, attacker.cvar_cl_handicap, 100.0)); - } - - if(DEATH_ISWEAPON(deathtype, WEP_TUBA)) - { - // tuba causes blood to come out of the ears - vector ear1, ear2; - vector d; - float f; - ear1 = this.origin; - ear1_z += 0.125 * this.view_ofs.z + 0.875 * this.maxs.z; // 7/8 - ear2 = ear1; - makevectors(this.angles); - ear1 += v_right * -10; - ear2 += v_right * +10; - d = inflictor.origin - this.origin; - if (d) - f = (d * v_right) / vlen(d); // this is cos of angle of d and v_right! - else - f = 0; // Assum ecenter. - force = v_right * vlen(force); - Violence_GibSplash_At(ear1, force * -1, 2, bound(0, damage, 25) / 2 * (0.5 - 0.5 * f), this, attacker); - Violence_GibSplash_At(ear2, force, 2, bound(0, damage, 25) / 2 * (0.5 + 0.5 * f), this, attacker); - if(f > 0) - { - hitloc = ear1; - force = force * -1; - } - else - { - hitloc = ear2; - // force is already good - } - } - else - Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, this, attacker); - - - v = healtharmor_applydamage(this.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, damage); - take = v.x; - save = v.y; - - if(attacker == this) - { - // don't reset pushltime for this damage as it may be an attempt to - // escape a lava pit or similar - //this.pushltime = 0; - this.istypefrag = 0; - } - else if(IS_PLAYER(attacker)) - { - this.pusher = attacker; - this.pushltime = time + autocvar_g_maxpushtime; - this.istypefrag = PHYS_INPUT_BUTTON_CHAT(this); - } - else if(time < this.pushltime) - { - attacker = this.pusher; - this.pushltime = max(this.pushltime, time + 0.6); - } - else - { - this.pushltime = 0; - this.istypefrag = 0; - } - - if (time < this.spawnshieldtime && autocvar_g_spawnshield_blockdamage < 1) - { - vector v = healtharmor_applydamage(this.armorvalue, max(0, autocvar_g_spawnshield_blockdamage), deathtype, damage); - take = v.x; - save = v.y; - } - - MUTATOR_CALLHOOK(PlayerDamage_SplitHealthArmor, inflictor, attacker, this, force, take, save, deathtype, damage); - take = bound(0, M_ARGV(4, float), this.health); - save = bound(0, M_ARGV(5, float), this.armorvalue); - excess = max(0, damage - take - save); - - if(sound_allowed(MSG_BROADCAST, attacker)) - { - if (save > 10) - sound (this, CH_SHOTS, SND_ARMORIMPACT, VOL_BASE, ATTEN_NORM); - else if (take > 30) - sound (this, CH_SHOTS, SND_BODYIMPACT2, VOL_BASE, ATTEN_NORM); - else if (take > 10) - sound (this, CH_SHOTS, SND_BODYIMPACT1, VOL_BASE, ATTEN_NORM); // FIXME possibly remove them? - } - - if (take > 50) - Violence_GibSplash_At(hitloc, force * -0.1, 3, 1, this, attacker); - if (take > 100) - Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, this, attacker); - - if (time >= this.spawnshieldtime || autocvar_g_spawnshield_blockdamage < 1) - { - if (!(this.flags & FL_GODMODE)) - { - this.armorvalue = this.armorvalue - save; - this.health = this.health - take; - // pause regeneration for 5 seconds - if(take) - this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_health_regen); - - if (time > this.pain_finished) //Don't switch pain sequences like crazy - { - this.pain_finished = time + 0.5; //Supajoe - - if(autocvar_sv_gentle < 1) { - if(this.classname != "body") // pain anim is BORKED on our ZYMs, FIXME remove this once we have good models - { - if (!this.animstate_override) - { - if (random() > 0.5) - animdecide_setaction(this, ANIMACTION_PAIN1, true); - else - animdecide_setaction(this, ANIMACTION_PAIN2, true); - } - } - - if(sound_allowed(MSG_BROADCAST, attacker)) - if((this.health < 2 * WEP_CVAR_PRI(blaster, damage) * autocvar_g_balance_selfdamagepercent + 1) || !(DEATH_WEAPONOF(deathtype).spawnflags & WEP_FLAG_CANCLIMB) || attacker != this) // WEAPONTODO: create separate limit for pain notification with laser - if(this.health > 1) - // exclude pain sounds for laserjumps as long as you aren't REALLY low on health and would die of the next two - { - if(deathtype == DEATH_FALL.m_id) - PlayerSound(this, playersound_fall, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); - else if(this.health > 75) - PlayerSound(this, playersound_pain100, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); - else if(this.health > 50) - PlayerSound(this, playersound_pain75, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); - else if(this.health > 25) - PlayerSound(this, playersound_pain50, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); - else - PlayerSound(this, playersound_pain25, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); - } - } - } - - // throw off bot aim temporarily - float shake; - if(IS_BOT_CLIENT(this) && this.health >= 1) - { - shake = damage * 5 / (bound(0,skill,100) + 1); - this.v_angle_x = this.v_angle.x + (random() * 2 - 1) * shake; - this.v_angle_y = this.v_angle.y + (random() * 2 - 1) * shake; - this.v_angle_x = bound(-90, this.v_angle.x, 90); - } - } - else - this.max_armorvalue += (save + take); - } - this.dmg_save = this.dmg_save + save;//max(save - 10, 0); - this.dmg_take = this.dmg_take + take;//max(take - 10, 0); - this.dmg_inflictor = inflictor; - - if (this != attacker) { - float realdmg = damage - excess; - if (IS_PLAYER(attacker)) { - PlayerScore_Add(attacker, SP_DMG, realdmg); - } - if (IS_PLAYER(this)) { - PlayerScore_Add(this, SP_DMGTAKEN, realdmg); - } - } - - bool abot = (IS_BOT_CLIENT(attacker)); - bool vbot = (IS_BOT_CLIENT(this)); - - valid_damage_for_weaponstats = 0; - Weapon awep = WEP_Null; - - if(vbot || IS_REAL_CLIENT(this)) - if(abot || IS_REAL_CLIENT(attacker)) - if(attacker && this != attacker) - if(DIFF_TEAM(this, attacker)) - { - if(DEATH_ISSPECIAL(deathtype)) - awep = PS(attacker).m_weapon; - else - awep = DEATH_WEAPONOF(deathtype); - valid_damage_for_weaponstats = 1; - } - - dh = dh - max(this.health, 0); - da = da - max(this.armorvalue, 0); - if(valid_damage_for_weaponstats) - { - WeaponStats_LogDamage(awep.m_id, abot, PS(this).m_weapon.m_id, vbot, dh + da); - } - if (dh + da) - { - MUTATOR_CALLHOOK(PlayerDamaged, attacker, this, dh, da, hitloc, deathtype); - } - - if (this.health < 1) - { - float defer_ClientKill_Now_TeamChange; - defer_ClientKill_Now_TeamChange = false; - - if(this.alivetime) - { - PS_GR_P_ADDVAL(this, PLAYERSTATS_ALIVETIME, time - this.alivetime); - this.alivetime = 0; - } - - if(valid_damage_for_weaponstats) - WeaponStats_LogKill(awep.m_id, abot, PS(this).m_weapon.m_id, vbot); - - if(autocvar_sv_gentle < 1) - if(sound_allowed(MSG_BROADCAST, attacker)) - { - if(deathtype == DEATH_DROWN.m_id) - PlayerSound(this, playersound_drown, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); - else - PlayerSound(this, playersound_death, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); - } - - // get rid of kill indicator - if(this.killindicator) - { - delete(this.killindicator); - this.killindicator = NULL; - if(this.killindicator_teamchange) - defer_ClientKill_Now_TeamChange = true; - - if(this.classname == "body") - if(deathtype == DEATH_KILL.m_id) - { - // for the lemmings fans, a small harmless explosion - Send_Effect(EFFECT_ROCKET_EXPLODE, this.origin, '0 0 0', 1); - } - } - - // print an obituary message - if(this.classname != "body") - Obituary (attacker, inflictor, this, deathtype); - - // increment frag counter for used weapon type - Weapon w = DEATH_WEAPONOF(deathtype); - if(w != WEP_Null) - if(accuracy_isgooddamage(attacker, this)) - attacker.accuracy.(accuracy_frags[w.m_id-1]) += 1; - - MUTATOR_CALLHOOK(PlayerDies, inflictor, attacker, this, deathtype, damage); - excess = M_ARGV(4, float); - - Weapon wep = PS(this).m_weapon; - /*for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) - { - .entity weaponentity = weaponentities[slot]; - wep.wr_playerdeath(wep, this, weaponentity); - }*/ - .entity weaponentity = weaponentities[0]; // TODO: unhardcode - wep.wr_playerdeath(wep, this, weaponentity); - - RemoveGrapplingHook(this); - - Portal_ClearAllLater(this); - - this.fixangle = true; - - if(defer_ClientKill_Now_TeamChange) - ClientKill_Now_TeamChange(this); // can turn player into spectator - - // player could have been miraculously resuscitated ;) - // e.g. players in freezetag get frozen, they don't really die - if(this.health >= 1 || !(IS_PLAYER(this) || this.classname == "body")) - return; - - // when we get here, player actually dies - - Unfreeze(this); // remove any icy remains - this.health = 0; // Unfreeze resets health, so we need to set it back - - // clear waypoints - WaypointSprite_PlayerDead(this); - // throw a weapon - SpawnThrownWeapon(this, this.origin + (this.mins + this.maxs) * 0.5, PS(this).m_switchweapon.m_id); - - // become fully visible - this.alpha = default_player_alpha; - // make the corpse upright (not tilted) - this.angles_x = 0; - this.angles_z = 0; - // don't spin - this.avelocity = '0 0 0'; - // view from the floor - this.view_ofs = '0 0 -8'; - // toss the corpse - set_movetype(this, MOVETYPE_TOSS); - // shootable corpse - this.solid = SOLID_CORPSE; - this.ballistics_density = autocvar_g_ballistics_density_corpse; - // don't stick to the floor - UNSET_ONGROUND(this); - // dying animation - this.deadflag = DEAD_DYING; - - // when to allow respawn - calculate_player_respawn_time(this); - - this.death_time = time; - if (random() < 0.5) - animdecide_setstate(this, this.anim_state | ANIMSTATE_DEAD1, true); - else - animdecide_setstate(this, this.anim_state | ANIMSTATE_DEAD2, true); - if (this.maxs.z > 5) - { - this.maxs_z = 5; - setsize(this, this.mins, this.maxs); - } - // set damage function to corpse damage - this.event_damage = PlayerCorpseDamage; - // call the corpse damage function just in case it wants to gib - this.event_damage(this, inflictor, attacker, excess, deathtype, hitloc, force); - - // set up to fade out later - SUB_SetFade (this, time + 6 + random (), 1); - // reset body think wrapper broken by SUB_SetFade - if(this.classname == "body" && getthink(this) != CopyBody_Think) { - this.CopyBody_think = getthink(this); - this.CopyBody_nextthink = this.nextthink; - setthink(this, CopyBody_Think); - this.nextthink = time; - } - - if(autocvar_sv_gentle > 0 || autocvar_ekg || this.classname == "body") { - // remove corpse - // clones don't run any animation code any more, so we must gib them when they die :( - PlayerCorpseDamage(this, inflictor, attacker, autocvar_sv_gibhealth+1.0, deathtype, hitloc, force); - } - - // reset fields the weapons may use just in case - FOREACH(Weapons, it != WEP_Null, LAMBDA( - it.wr_resetplayer(it, this); - for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) - { - ATTACK_FINISHED_FOR(this, it.m_id, slot) = 0; - } - )); - } -} - -void MoveToTeam(entity client, int team_colour, int type) -{ - int lockteams_backup = lockteams; // backup any team lock - lockteams = 0; // disable locked teams - TeamchangeFrags(client); // move the players frags - SetPlayerColors(client, team_colour - 1); // set the players colour - Damage(client, client, client, 100000, DEATH_AUTOTEAMCHANGE.m_id, client.origin, '0 0 0'); // kill the player - lockteams = lockteams_backup; // restore the team lock - LogTeamchange(client.playerid, client.team, type); -} - -/** print(), but only print if the server is not local */ -void dedicated_print(string input) -{ - if (server_is_dedicated) print(input); -} - -/** - * message "": do not say, just test flood control - * return value: - * 1 = accept - * 0 = reject - * -1 = fake accept - */ -int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodcontrol) -{ - if (!teamsay && !privatesay) if (substring(msgin, 0, 1) == " ") - msgin = substring(msgin, 1, -1); // work around DP say bug (say_team does not have this!) - - msgin = formatmessage(source, msgin); - - string colorstr; - if (!IS_PLAYER(source)) - colorstr = "^0"; // black for spectators - else if(teamplay) - colorstr = Team_ColorCode(source.team); - else - { - colorstr = ""; - teamsay = false; - } - - if(intermission_running) - teamsay = false; - - if (!source) { - colorstr = ""; - teamsay = false; - } - - if(msgin != "") - msgin = trigger_magicear_processmessage_forallears(source, teamsay, privatesay, msgin); - - /* - * using bprint solves this... me stupid - // how can we prevent the message from appearing in a listen server? - // for now, just give "say" back and only handle say_team - if(!teamsay) - { - clientcommand(source, strcat("say ", msgin)); - return; - } - */ - - string namestr = ""; - if (source) - namestr = autocvar_g_chat_teamcolors ? playername(source) : source.netname; - - string colorprefix = (strdecolorize(namestr) == namestr) ? "^3" : "^7"; - - string msgstr, cmsgstr; - string privatemsgprefix = string_null; - int privatemsgprefixlen = 0; - if (msgin == "") { - msgstr = cmsgstr = ""; - } else { - if(privatesay) - { - msgstr = strcat("\{1}\{13}* ", colorprefix, namestr, "^3 tells you: ^7"); - privatemsgprefixlen = strlen(msgstr); - msgstr = strcat(msgstr, msgin); - cmsgstr = strcat(colorstr, colorprefix, namestr, "^3 tells you:\n^7", msgin); - if(autocvar_g_chat_teamcolors) - privatemsgprefix = strcat("\{1}\{13}* ^3You tell ", playername(privatesay), ": ^7"); - else - privatemsgprefix = strcat("\{1}\{13}* ^3You tell ", privatesay.netname, ": ^7"); - } - else if(teamsay) - { - if(strstrofs(msgin, "/me", 0) >= 0) - { - //msgin = strreplace("/me", "", msgin); - //msgin = substring(msgin, 3, strlen(msgin)); - msgin = strreplace("/me", strcat(colorstr, "(", colorprefix, namestr, colorstr, ")^7"), msgin); - msgstr = strcat("\{1}\{13}^4* ", "^7", msgin); - } - else - msgstr = strcat("\{1}\{13}", colorstr, "(", colorprefix, namestr, colorstr, ") ^7", msgin); - cmsgstr = strcat(colorstr, "(", colorprefix, namestr, colorstr, ")\n^7", msgin); - } - else - { - if(strstrofs(msgin, "/me", 0) >= 0) - { - //msgin = strreplace("/me", "", msgin); - //msgin = substring(msgin, 3, strlen(msgin)); - msgin = strreplace("/me", strcat(colorprefix, namestr), msgin); - msgstr = strcat("\{1}^4* ", "^7", msgin); - } - else { - msgstr = "\{1}"; - msgstr = strcat(msgstr, (namestr != "") ? strcat(colorprefix, namestr, "^7: ") : "^7"); - msgstr = strcat(msgstr, msgin); - } - cmsgstr = ""; - } - msgstr = strcat(strreplace("\n", " ", msgstr), "\n"); // newlines only are good for centerprint - } - - string fullmsgstr = msgstr; - string fullcmsgstr = cmsgstr; - - // FLOOD CONTROL - int flood = 0; - var .float flood_field = floodcontrol_chat; - if(floodcontrol) - { - float flood_spl; - float flood_burst; - float flood_lmax; - float lines; - if(privatesay) - { - flood_spl = autocvar_g_chat_flood_spl_tell; - flood_burst = autocvar_g_chat_flood_burst_tell; - flood_lmax = autocvar_g_chat_flood_lmax_tell; - flood_field = floodcontrol_chattell; - } - else if(teamsay) - { - flood_spl = autocvar_g_chat_flood_spl_team; - flood_burst = autocvar_g_chat_flood_burst_team; - flood_lmax = autocvar_g_chat_flood_lmax_team; - flood_field = floodcontrol_chatteam; - } - else - { - flood_spl = autocvar_g_chat_flood_spl; - flood_burst = autocvar_g_chat_flood_burst; - flood_lmax = autocvar_g_chat_flood_lmax; - flood_field = floodcontrol_chat; - } - flood_burst = max(0, flood_burst - 1); - // to match explanation in default.cfg, a value of 3 must allow three-line bursts and not four! - - // do flood control for the default line size - if(msgstr != "") - { - getWrappedLine_remaining = msgstr; - msgstr = ""; - lines = 0; - while(getWrappedLine_remaining && (!flood_lmax || lines <= flood_lmax)) - { - msgstr = strcat(msgstr, " ", getWrappedLineLen(82.4289758859709, strlennocol)); // perl averagewidth.pl < gfx/vera-sans.width - ++lines; - } - msgstr = substring(msgstr, 1, strlen(msgstr) - 1); - - if(getWrappedLine_remaining != "") - { - msgstr = strcat(msgstr, "\n"); - flood = 2; - } - - if (time >= source.(flood_field)) - { - source.(flood_field) = max(time - flood_burst * flood_spl, source.(flood_field)) + lines * flood_spl; - } - else - { - flood = 1; - msgstr = fullmsgstr; - } - } - else - { - if (time >= source.(flood_field)) - source.(flood_field) = max(time - flood_burst * flood_spl, source.(flood_field)) + flood_spl; - else - flood = 1; - } - - if (timeout_status == TIMEOUT_ACTIVE) // when game is paused, no flood protection - source.(flood_field) = flood = 0; - } - - string sourcemsgstr, sourcecmsgstr; - if(flood == 2) // cannot happen for empty msgstr - { - if(autocvar_g_chat_flood_notify_flooder) - { - sourcemsgstr = strcat(msgstr, "\n^3FLOOD CONTROL: ^7message too long, trimmed\n"); - sourcecmsgstr = ""; - } - else - { - sourcemsgstr = fullmsgstr; - sourcecmsgstr = fullcmsgstr; - } - cmsgstr = ""; - } - else - { - sourcemsgstr = msgstr; - sourcecmsgstr = cmsgstr; - } - - if (!privatesay && source && !IS_PLAYER(source)) - { - if (!intermission_running) - if(teamsay || (autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !(warmup_stage || gameover))) - teamsay = -1; // spectators - } - - if(flood) - LOG_INFO("NOTE: ", playername(source), "^7 is flooding.\n"); - - // build sourcemsgstr by cutting off a prefix and replacing it by the other one - if(privatesay) - sourcemsgstr = strcat(privatemsgprefix, substring(sourcemsgstr, privatemsgprefixlen, -1)); - - int ret; - if(source.muted) - { - // always fake the message - ret = -1; - } - else if(flood == 1) - { - if (autocvar_g_chat_flood_notify_flooder) - { - sprint(source, strcat("^3FLOOD CONTROL: ^7wait ^1", ftos(source.(flood_field) - time), "^3 seconds\n")); - ret = 0; - } - else - ret = -1; - } - else - { - ret = 1; - } - - if(sourcemsgstr != "" && ret != 0) - { - if(ret < 0) // faked message, because the player is muted - { - sprint(source, sourcemsgstr); - if(sourcecmsgstr != "" && !privatesay) - centerprint(source, sourcecmsgstr); - } - else if(privatesay) // private message, between 2 people only - { - sprint(source, sourcemsgstr); - sprint(privatesay, msgstr); - if (!autocvar_g_chat_tellprivacy) { dedicated_print(msgstr); } // send to server console too if "tellprivacy" is disabled - if(cmsgstr != "") - centerprint(privatesay, cmsgstr); - } - else if ( teamsay && source.active_minigame ) - { - sprint(source, sourcemsgstr); - dedicated_print(msgstr); // send to server console too - FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && it.active_minigame == source.active_minigame, sprint(it, msgstr)); - } - else if(teamsay > 0) // team message, only sent to team mates - { - sprint(source, sourcemsgstr); - dedicated_print(msgstr); // send to server console too - if(sourcecmsgstr != "") - centerprint(source, sourcecmsgstr); - FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && it != source && it.team == source.team, { - sprint(it, msgstr); - if(cmsgstr != "") - centerprint(it, cmsgstr); - }); - } - else if(teamsay < 0) // spectator message, only sent to spectators - { - sprint(source, sourcemsgstr); - dedicated_print(msgstr); // send to server console too - FOREACH_CLIENT(!IS_PLAYER(it) && IS_REAL_CLIENT(it) && it != source, sprint(it, msgstr)); - } - else - { - if (source) { - sprint(source, sourcemsgstr); - dedicated_print(msgstr); // send to server console too - MX_Say(strcat(playername(source), "^7: ", msgin)); - } - FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source, sprint(it, msgstr)); - } - } - - return ret; -} diff --git a/qcsrc/server/cl_player.qh b/qcsrc/server/cl_player.qh deleted file mode 100644 index b5f8ca07c8..0000000000 --- a/qcsrc/server/cl_player.qh +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -.entity pusher; -.float pushltime; -.float istypefrag; - -.float CopyBody_nextthink; -.void(entity this) CopyBody_think; -void CopyBody_Think(entity this); -void CopyBody(entity this, float keepvelocity); - -void player_setupanimsformodel(entity this); - -void player_anim(entity this); - -void PlayerCorpseDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force); - -// g__str: -// If 0, default is used. -// If <0, 0 is used. -// Otherwise, g_str (default value) is used. -// For consistency, negative values there are mapped to zero too. -#define GAMETYPE_DEFAULTED_SETTING(str) \ - ((gametype_setting_tmp = cvar(strcat("g_", GetGametype(), "_" #str))), \ - (gametype_setting_tmp < 0) ? 0 \ - : (gametype_setting_tmp == 0 || autocvar_g_respawn_delay_forced) ? max(0, autocvar_g_##str) \ - : gametype_setting_tmp) - -void calculate_player_respawn_time(entity this); - -void ClientKill_Now_TeamChange(entity this); - -void MoveToTeam(entity client, float team_colour, float type); - -void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force); - -/** to be used by `prvm_edictset server playernumber muted 1` */ -.float muted; -int Say(entity source, float teamsay, entity privatesay, string msgin, float floodcontrol); diff --git a/qcsrc/server/client.qc b/qcsrc/server/client.qc new file mode 100644 index 0000000000..06254c42a0 --- /dev/null +++ b/qcsrc/server/client.qc @@ -0,0 +1,2605 @@ +#include "client.qh" + +#include "anticheat.qh" +#include "impulse.qh" +#include "player.qh" +#include "ipban.qh" +#include "miscfunctions.qh" +#include "portals.qh" +#include "teamplay.qh" +#include "playerdemo.qh" +#include "spawnpoints.qh" +#include "g_damage.qh" +#include "g_hook.qh" +#include "command/common.qh" +#include "cheats.qh" +#include "g_world.qh" +#include "race.qh" +#include "antilag.qh" +#include "campaign.qh" +#include "command/common.qh" +#include "scores_rules.qh" + +#include "bot/api.qh" + +#include "../common/ent_cs.qh" +#include + +#include + +#include "../common/triggers/teleporters.qh" + +#include "../common/vehicles/all.qh" + +#include "weapons/hitplot.qh" +#include "weapons/weaponsystem.qh" + +#include "../common/net_notice.qh" +#include "../common/physics/player.qh" + +#include "../common/items/_mod.qh" + +#include "../common/mutators/mutator/waypoints/all.qh" + +#include "../common/triggers/subs.qh" +#include "../common/triggers/triggers.qh" +#include "../common/triggers/trigger/secret.qh" + +#include "../common/minigames/sv_minigames.qh" + +#include "../common/items/inventory.qh" + +#include "../common/monsters/sv_monsters.qh" + +#include "../lib/warpzone/server.qh" + +STATIC_METHOD(Client, Add, void(Client this, int _team)) +{ + ClientConnect(this); + TRANSMUTE(Player, this); + this.frame = 12; // 7 + this.team = _team; + PutClientInServer(this); +} + +void PutObserverInServer(entity this); + +STATIC_METHOD(Client, Remove, void(Client this)) +{ + TRANSMUTE(Observer, this); + PutClientInServer(this); + ClientDisconnect(this); +} + +void send_CSQC_teamnagger() { + WriteHeader(MSG_BROADCAST, TE_CSQC_TEAMNAGGER); +} + +int CountSpectators(entity player, entity to) +{ + if(!player) { return 0; } // not sure how, but best to be safe + + int spec_count = 0; + + FOREACH_CLIENT(IS_REAL_CLIENT(it) && IS_SPEC(it) && it != to && it.enemy == player, + { + spec_count++; + }); + + return spec_count; +} + +void WriteSpectators(entity player, entity to) +{ + if(!player) { return; } // not sure how, but best to be safe + + FOREACH_CLIENT(IS_REAL_CLIENT(it) && IS_SPEC(it) && it != to && it.enemy == player, + { + WriteByte(MSG_ENTITY, num_for_edict(it)); + }); +} + +bool ClientData_Send(entity this, entity to, int sf) +{ + assert(to == this.owner, return false); + + entity e = to; + if (IS_SPEC(e)) e = e.enemy; + + sf = 0; + if (e.race_completed) sf |= 1; // forced scoreboard + if (to.spectatee_status) sf |= 2; // spectator ent number follows + if (e.zoomstate) sf |= 4; // zoomed + if (e.porto_v_angle_held) sf |= 8; // angles held + if (autocvar_sv_showspectators) sf |= 16; // show spectators + + WriteHeader(MSG_ENTITY, ENT_CLIENT_CLIENTDATA); + WriteByte(MSG_ENTITY, sf); + + if (sf & 2) + { + WriteByte(MSG_ENTITY, to.spectatee_status); + } + if (sf & 8) + { + WriteAngle(MSG_ENTITY, e.v_angle.x); + WriteAngle(MSG_ENTITY, e.v_angle.y); + } + + if(sf & 16) + { + float specs = CountSpectators(e, to); + WriteByte(MSG_ENTITY, specs); + WriteSpectators(e, to); + } + + return true; +} + +void ClientData_Attach(entity this) +{ + Net_LinkEntity(this.clientdata = new_pure(clientdata), false, 0, ClientData_Send); + this.clientdata.drawonlytoclient = this; + this.clientdata.owner = this; +} + +void ClientData_Detach(entity this) +{ + delete(this.clientdata); + this.clientdata = NULL; +} + +void ClientData_Touch(entity e) +{ + e.clientdata.SendFlags = 1; + + // make it spectatable + FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != e && IS_SPEC(it) && it.enemy == e, LAMBDA(it.clientdata.SendFlags = 1)); +} + +.string netname_previous; + +void SetSpectatee(entity this, entity spectatee); +void SetSpectatee_status(entity this, int spectatee_num); + + +/* +============= +CheckPlayerModel + +Checks if the argument string can be a valid playermodel. +Returns a valid one in doubt. +============= +*/ +string FallbackPlayerModel; +string CheckPlayerModel(string plyermodel) { + if(FallbackPlayerModel != cvar_defstring("_cl_playermodel")) + { + // note: we cannot summon Don Strunzone here, some player may + // still have the model string set. In case anyone manages how + // to change a cvar default, we'll have a small leak here. + FallbackPlayerModel = strzone(cvar_defstring("_cl_playermodel")); + } + // only in right path + if( substring(plyermodel,0,14) != "models/player/") + return FallbackPlayerModel; + // only good file extensions + if(substring(plyermodel,-4,4) != ".zym") + if(substring(plyermodel,-4,4) != ".dpm") + if(substring(plyermodel,-4,4) != ".iqm") + if(substring(plyermodel,-4,4) != ".md3") + if(substring(plyermodel,-4,4) != ".psk") + return FallbackPlayerModel; + // forbid the LOD models + if(substring(plyermodel, -9,5) == "_lod1") + return FallbackPlayerModel; + if(substring(plyermodel, -9,5) == "_lod2") + return FallbackPlayerModel; + if(plyermodel != strtolower(plyermodel)) + return FallbackPlayerModel; + // also, restrict to server models + if(autocvar_sv_servermodelsonly) + { + if(!fexists(plyermodel)) + return FallbackPlayerModel; + } + return plyermodel; +} + +void setplayermodel(entity e, string modelname) +{ + precache_model(modelname); + _setmodel(e, modelname); + player_setupanimsformodel(e); + if(!autocvar_g_debug_globalsounds) + UpdatePlayerSounds(e); +} + +void FixPlayermodel(entity player); +/** putting a client as observer in the server */ +void PutObserverInServer(entity this) +{ + bool mutator_returnvalue = MUTATOR_CALLHOOK(MakePlayerObserver, this); + PlayerState_detach(this); + + if (IS_PLAYER(this) && this.health >= 1) { + // despawn effect + Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1); + } + + { + entity spot = SelectSpawnPoint(this, true); + if (!spot) LOG_FATAL("No spawnpoints for observers?!?"); + this.angles = spot.angles; + this.angles_z = 0; + this.fixangle = true; + // offset it so that the spectator spawns higher off the ground, looks better this way + setorigin(this, spot.origin + STAT(PL_VIEW_OFS, NULL)); + this.prevorigin = this.origin; + if (IS_REAL_CLIENT(this)) + { + msg_entity = this; + WriteByte(MSG_ONE, SVC_SETVIEW); + WriteEntity(MSG_ONE, this); + } + // give the spectator some space between walls for MOVETYPE_FLY_WORLDONLY + // so that your view doesn't go into the ceiling with MOVETYPE_FLY_WORLDONLY, previously "PL_VIEW_OFS" + if(!autocvar_g_debug_globalsounds) + { + // needed for player sounds + this.model = ""; + FixPlayermodel(this); + } + setmodel(this, MDL_Null); + setsize(this, STAT(PL_CROUCH_MIN, NULL), STAT(PL_CROUCH_MAX, NULL)); + this.view_ofs = '0 0 0'; + } + + RemoveGrapplingHook(this); + Portal_ClearAll(this); + Unfreeze(this); + SetSpectatee(this, NULL); + + if (this.alivetime) + { + if (!warmup_stage) + PS_GR_P_ADDVAL(this, PLAYERSTATS_ALIVETIME, time - this.alivetime); + this.alivetime = 0; + } + + if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE); + + WaypointSprite_PlayerDead(this); + + if (mutator_returnvalue) { + // mutator prevents resetting teams+score + } else { + this.team = -1; // move this as it is needed to log the player spectating in eventlog + this.frags = FRAGS_SPECTATOR; + PlayerScore_Clear(this); // clear scores when needed + } + + if (this.killcount != FRAGS_SPECTATOR) + { + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_SPECTATE, this.netname); + if(!intermission_running) + if(autocvar_g_chat_nospectators == 1 || (!(warmup_stage || gameover) && autocvar_g_chat_nospectators == 2)) + Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_CHAT_NOSPECTATORS); + + if(this.just_joined == false) { + LogTeamchange(this.playerid, -1, 4); + } else + this.just_joined = false; + } + + accuracy_resend(this); + + this.spectatortime = time; + this.bot_attack = false; + this.hud = HUD_NORMAL; + TRANSMUTE(Observer, this); + this.iscreature = false; + this.teleportable = TELEPORT_SIMPLE; + this.damagedbycontents = false; + this.health = FRAGS_SPECTATOR; + SetSpectatee_status(this, etof(this)); + this.takedamage = DAMAGE_NO; + this.solid = SOLID_NOT; + set_movetype(this, MOVETYPE_FLY_WORLDONLY); // user preference is controlled by playerprethink + this.flags = FL_CLIENT | FL_NOTARGET; + this.armorvalue = 666; + this.effects = 0; + this.armorvalue = autocvar_g_balance_armor_start; + this.pauserotarmor_finished = 0; + this.pauserothealth_finished = 0; + this.pauseregen_finished = 0; + this.damageforcescale = 0; + this.death_time = 0; + this.respawn_flags = 0; + this.respawn_time = 0; + this.stat_respawn_time = 0; + this.alpha = 0; + this.scale = 0; + this.fade_time = 0; + this.pain_frame = 0; + this.pain_finished = 0; + this.strength_finished = 0; + this.invincible_finished = 0; + this.superweapons_finished = 0; + this.pushltime = 0; + this.istypefrag = 0; + setthink(this, func_null); + this.nextthink = 0; + this.hook_time = 0; + this.deadflag = DEAD_NO; + this.crouch = false; + this.revival_time = 0; + + this.items = 0; + this.weapons = '0 0 0'; + this.drawonlytoclient = this; + + this.weaponname = ""; + this.weaponmodel = ""; + for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + this.weaponentities[slot] = NULL; + } + this.exteriorweaponentity = NULL; + this.killcount = FRAGS_SPECTATOR; + this.velocity = '0 0 0'; + this.avelocity = '0 0 0'; + this.punchangle = '0 0 0'; + this.punchvector = '0 0 0'; + this.oldvelocity = this.velocity; + this.fire_endtime = -1; + this.event_damage = func_null; + + STAT(ACTIVEWEAPON, this) = WEP_Null.m_id; + STAT(SWITCHINGWEAPON, this) = WEP_Null.m_id; + STAT(SWITCHWEAPON, this) = WEP_Null.m_id; +} + +int player_getspecies(entity this) +{ + get_model_parameters(this.model, this.skin); + int s = get_model_parameters_species; + get_model_parameters(string_null, 0); + if (s < 0) return SPECIES_HUMAN; + return s; +} + +.float model_randomizer; +void FixPlayermodel(entity player) +{ + string defaultmodel = ""; + int defaultskin = 0; + if(autocvar_sv_defaultcharacter) + { + if(teamplay) + { + string s = Static_Team_ColorName_Lower(player.team); + if (s != "neutral") + { + defaultmodel = cvar_string(strcat("sv_defaultplayermodel_", s)); + defaultskin = cvar(strcat("sv_defaultplayerskin_", s)); + } + } + + if(defaultmodel == "") + { + defaultmodel = autocvar_sv_defaultplayermodel; + defaultskin = autocvar_sv_defaultplayerskin; + } + + int n = tokenize_console(defaultmodel); + if(n > 0) + { + defaultmodel = argv(floor(n * player.model_randomizer)); + // However, do NOT randomize if the player-selected model is in the list. + for (int i = 0; i < n; ++i) + if ((argv(i) == player.playermodel && defaultskin == stof(player.playerskin)) || argv(i) == strcat(player.playermodel, ":", player.playerskin)) + defaultmodel = argv(i); + } + + int i = strstrofs(defaultmodel, ":", 0); + if(i >= 0) + { + defaultskin = stof(substring(defaultmodel, i+1, -1)); + defaultmodel = substring(defaultmodel, 0, i); + } + } + if(autocvar_sv_defaultcharacterskin && !defaultskin) + { + if(teamplay) + { + string s = Static_Team_ColorName_Lower(player.team); + if (s != "neutral") + defaultskin = cvar(strcat("sv_defaultplayerskin_", s)); + } + + if(!defaultskin) + defaultskin = autocvar_sv_defaultplayerskin; + } + + MUTATOR_CALLHOOK(FixPlayermodel, defaultmodel, defaultskin, player); + defaultmodel = M_ARGV(0, string); + defaultskin = M_ARGV(1, int); + + bool chmdl = false; + int oldskin; + if(defaultmodel != "") + { + if (defaultmodel != player.model) + { + vector m1 = player.mins; + vector m2 = player.maxs; + setplayermodel (player, defaultmodel); + setsize (player, m1, m2); + chmdl = true; + } + + oldskin = player.skin; + player.skin = defaultskin; + } else { + if (player.playermodel != player.model || player.playermodel == "") + { + player.playermodel = CheckPlayerModel(player.playermodel); // this is never "", so no endless loop + vector m1 = player.mins; + vector m2 = player.maxs; + setplayermodel (player, player.playermodel); + setsize (player, m1, m2); + chmdl = true; + } + + if(!autocvar_sv_defaultcharacterskin) + { + oldskin = player.skin; + player.skin = stof(player.playerskin); + } + else + { + oldskin = player.skin; + player.skin = defaultskin; + } + } + + if(chmdl || oldskin != player.skin) // model or skin has changed + { + player.species = player_getspecies(player); // update species + if(!autocvar_g_debug_globalsounds) + UpdatePlayerSounds(player); // update skin sounds + } + + if(!teamplay) + if(strlen(autocvar_sv_defaultplayercolors)) + if(player.clientcolors != stof(autocvar_sv_defaultplayercolors)) + setcolor(player, stof(autocvar_sv_defaultplayercolors)); +} + + +/** Called when a client spawns in the server */ +void PutClientInServer(entity this) +{ + if (IS_BOT_CLIENT(this)) { + TRANSMUTE(Player, this); + } else if (IS_REAL_CLIENT(this)) { + msg_entity = this; + WriteByte(MSG_ONE, SVC_SETVIEW); + WriteEntity(MSG_ONE, this); + } + if (gameover) { + TRANSMUTE(Observer, this); + } + + SetSpectatee(this, NULL); + + // reset player keys + this.itemkeys = 0; + + MUTATOR_CALLHOOK(PutClientInServer, this); + + if (IS_OBSERVER(this)) { + PutObserverInServer(this); + } else if (IS_PLAYER(this)) { + if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE); + + PlayerState_attach(this); + accuracy_resend(this); + + if (this.team < 0) + JoinBestTeam(this, false, true); + + entity spot = SelectSpawnPoint(this, false); + if (!spot) { + Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_NOSPAWNS); + return; // spawn failed + } + + TRANSMUTE(Player, this); + + this.wasplayer = true; + this.iscreature = true; + this.teleportable = TELEPORT_NORMAL; + this.damagedbycontents = true; + set_movetype(this, MOVETYPE_WALK); + this.solid = SOLID_SLIDEBOX; + this.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_SOLID; + if (autocvar_g_playerclip_collisions) + this.dphitcontentsmask |= DPCONTENTS_PLAYERCLIP; + if (IS_BOT_CLIENT(this) && autocvar_g_botclip_collisions) + this.dphitcontentsmask |= DPCONTENTS_BOTCLIP; + this.frags = FRAGS_PLAYER; + if (INDEPENDENT_PLAYERS) MAKE_INDEPENDENT_PLAYER(this); + this.flags = FL_CLIENT | FL_PICKUPITEMS; + if (autocvar__notarget) + this.flags |= FL_NOTARGET; + this.takedamage = DAMAGE_AIM; + this.effects = EF_TELEPORT_BIT | EF_RESTARTANIM_BIT; + this.dmg = 2; // WTF + + if (warmup_stage) { + this.ammo_shells = warmup_start_ammo_shells; + this.ammo_nails = warmup_start_ammo_nails; + this.ammo_rockets = warmup_start_ammo_rockets; + this.ammo_cells = warmup_start_ammo_cells; + this.ammo_plasma = warmup_start_ammo_plasma; + this.ammo_fuel = warmup_start_ammo_fuel; + this.health = warmup_start_health; + this.armorvalue = warmup_start_armorvalue; + this.weapons = WARMUP_START_WEAPONS; + } else { + this.ammo_shells = start_ammo_shells; + this.ammo_nails = start_ammo_nails; + this.ammo_rockets = start_ammo_rockets; + this.ammo_cells = start_ammo_cells; + this.ammo_plasma = start_ammo_plasma; + this.ammo_fuel = start_ammo_fuel; + this.health = start_health; + this.armorvalue = start_armorvalue; + this.weapons = start_weapons; + } + SetSpectatee_status(this, 0); + + this.superweapons_finished = (this.weapons & WEPSET_SUPERWEAPONS) ? time + autocvar_g_balance_superweapons_time : 0; + + this.items = start_items; + + this.spawnshieldtime = time + autocvar_g_spawnshieldtime; + this.pauserotarmor_finished = time + autocvar_g_balance_pause_armor_rot_spawn; + this.pauserothealth_finished = time + autocvar_g_balance_pause_health_rot_spawn; + this.pauserotfuel_finished = time + autocvar_g_balance_pause_fuel_rot_spawn; + this.pauseregen_finished = time + autocvar_g_balance_pause_health_regen_spawn; + // extend the pause of rotting if client was reset at the beginning of the countdown + if (!autocvar_sv_ready_restart_after_countdown && time < game_starttime) { // TODO why is this cvar NOTted? + float f = game_starttime - time; + this.spawnshieldtime += f; + this.pauserotarmor_finished += f; + this.pauserothealth_finished += f; + this.pauseregen_finished += f; + } + this.damageforcescale = 2; + this.death_time = 0; + this.respawn_flags = 0; + this.respawn_time = 0; + this.stat_respawn_time = 0; + this.scale = autocvar_sv_player_scale; + this.fade_time = 0; + this.pain_frame = 0; + this.pain_finished = 0; + this.pushltime = 0; + setthink(this, func_null); // players have no think function + this.nextthink = 0; + this.dmg_team = 0; + this.ballistics_density = autocvar_g_ballistics_density_player; + + this.deadflag = DEAD_NO; + + this.angles = spot.angles; + this.angles_z = 0; // never spawn tilted even if the spot says to + if (IS_BOT_CLIENT(this)) + this.v_angle = this.angles; + this.fixangle = true; // turn this way immediately + this.oldvelocity = this.velocity = '0 0 0'; + this.avelocity = '0 0 0'; + this.punchangle = '0 0 0'; + this.punchvector = '0 0 0'; + + this.strength_finished = 0; + this.invincible_finished = 0; + this.fire_endtime = -1; + this.revival_time = 0; + this.air_finished = time + 12; + + entity spawnevent = new_pure(spawnevent); + spawnevent.owner = this; + Net_LinkEntity(spawnevent, false, 0.5, SpawnEvent_Send); + + // Cut off any still running player sounds. + stopsound(this, CH_PLAYER_SINGLE); + + this.model = ""; + FixPlayermodel(this); + this.drawonlytoclient = NULL; + + this.crouch = false; + this.view_ofs = STAT(PL_VIEW_OFS, NULL); + setsize(this, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL)); + this.spawnorigin = spot.origin; + setorigin(this, spot.origin + '0 0 1' * (1 - this.mins.z - 24)); + // don't reset back to last position, even if new position is stuck in solid + this.oldorigin = this.origin; + this.prevorigin = this.origin; + this.lastteleporttime = time; // prevent insane speeds due to changing origin + this.conveyor = NULL; // prevent conveyors at the previous location from moving a freshly spawned player + this.hud = HUD_NORMAL; + + this.event_damage = PlayerDamage; + + this.bot_attack = true; + this.monster_attack = true; + + PHYS_INPUT_BUTTON_ATCK(this) = PHYS_INPUT_BUTTON_JUMP(this) = PHYS_INPUT_BUTTON_ATCK2(this) = false; + + if (this.killcount == FRAGS_SPECTATOR) { + PlayerScore_Clear(this); + this.killcount = 0; + } + + for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + CL_SpawnWeaponentity(this, weaponentities[slot]); + } + this.alpha = default_player_alpha; + this.colormod = '1 1 1' * autocvar_g_player_brightness; + this.exteriorweaponentity.alpha = default_weapon_alpha; + + this.speedrunning = false; + + target_voicescript_clear(this); + + // reset fields the weapons may use + FOREACH(Weapons, true, LAMBDA( + it.wr_resetplayer(it, this); + // reload all reloadable weapons + if (it.spawnflags & WEP_FLAG_RELOADABLE) { + this.weapon_load[it.m_id] = it.reloading_ammo; + } + )); + + { + string s = spot.target; + spot.target = string_null; + SUB_UseTargets(spot, this, NULL); + spot.target = s; + } + + Unfreeze(this); + + MUTATOR_CALLHOOK(PlayerSpawn, this, spot); + + if (autocvar_spawn_debug) + { + sprint(this, strcat("spawnpoint origin: ", vtos(spot.origin), "\n")); + delete(spot); // usefull for checking if there are spawnpoints, that let drop through the floor + } + + PS(this).m_switchweapon = w_getbestweapon(this); + this.cnt = -1; // W_LastWeapon will not complain + PS(this).m_weapon = WEP_Null; + this.weaponname = ""; + PS(this).m_switchingweapon = WEP_Null; + + if (!warmup_stage && !this.alivetime) + this.alivetime = time; + + antilag_clear(this, CS(this)); + } +} + +void ClientInit_misc(entity this); + +.float ebouncefactor, ebouncestop; // electro's values +// TODO do we need all these fields, or should we stop autodetecting runtime +// changes and just have a console command to update this? +bool ClientInit_SendEntity(entity this, entity to, int sf) +{ + WriteHeader(MSG_ENTITY, _ENT_CLIENT_INIT); + return = true; + msg_entity = to; + // MSG_INIT replacement + // TODO: make easier to use + Registry_send_all(); + W_PROP_reload(MSG_ONE, to); + ClientInit_misc(this); + MUTATOR_CALLHOOK(Ent_Init); +} +void ClientInit_misc(entity this) +{ + int channel = MSG_ONE; + WriteHeader(channel, ENT_CLIENT_INIT); + WriteByte(channel, g_nexball_meter_period * 32); + WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[0])); + WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[1])); + WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[2])); + WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[3])); + WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[0])); + WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[1])); + WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[2])); + WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[3])); + + if(sv_foginterval && world.fog != "") + WriteString(channel, world.fog); + else + WriteString(channel, ""); + WriteByte(channel, this.count * 255.0); // g_balance_armor_blockpercent + WriteByte(channel, serverflags); // client has to know if it should zoom or not + WriteCoord(channel, autocvar_g_trueaim_minrange); +} + +void ClientInit_CheckUpdate(entity this) +{ + this.nextthink = time; + if(this.count != autocvar_g_balance_armor_blockpercent) + { + this.count = autocvar_g_balance_armor_blockpercent; + this.SendFlags |= 1; + } +} + +void ClientInit_Spawn() +{ + entity e = new_pure(clientinit); + setthink(e, ClientInit_CheckUpdate); + Net_LinkEntity(e, false, 0, ClientInit_SendEntity); + + ClientInit_CheckUpdate(e); +} + +/* +============= +SetNewParms +============= +*/ +void SetNewParms () +{ + // initialize parms for a new player + parm1 = -(86400 * 366); + + MUTATOR_CALLHOOK(SetNewParms); +} + +/* +============= +SetChangeParms +============= +*/ +void SetChangeParms (entity this) +{ + // save parms for level change + parm1 = this.parm_idlesince - time; + + MUTATOR_CALLHOOK(SetChangeParms); +} + +/* +============= +DecodeLevelParms +============= +*/ +void DecodeLevelParms(entity this) +{ + // load parms + this.parm_idlesince = parm1; + if (this.parm_idlesince == -(86400 * 366)) + this.parm_idlesince = time; + + // whatever happens, allow 60 seconds of idling directly after connect for map loading + this.parm_idlesince = max(this.parm_idlesince, time - sv_maxidle + 60); + + MUTATOR_CALLHOOK(DecodeLevelParms); +} + +/* +============= +ClientKill + +Called when a client types 'kill' in the console +============= +*/ + +.float clientkill_nexttime; +void ClientKill_Now_TeamChange(entity this) +{ + if(this.killindicator_teamchange == -1) + { + JoinBestTeam( this, false, true ); + } + else if(this.killindicator_teamchange == -2) + { + if(blockSpectators) + Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime); + PutObserverInServer(this); + } + else + SV_ChangeTeam(this, this.killindicator_teamchange - 1); + this.killindicator_teamchange = 0; +} + +void ClientKill_Now(entity this) +{ + if(this.vehicle) + { + vehicles_exit(this.vehicle, VHEF_RELEASE); + if(!this.killindicator_teamchange) + { + this.vehicle_health = -1; + Damage(this, this, this, 1 , DEATH_KILL.m_id, this.origin, '0 0 0'); + } + } + + if(this.killindicator && !wasfreed(this.killindicator)) + delete(this.killindicator); + + this.killindicator = NULL; + + if(this.killindicator_teamchange) + ClientKill_Now_TeamChange(this); + + if(!IS_SPEC(this) && !IS_OBSERVER(this)) + Damage(this, this, this, 100000, DEATH_KILL.m_id, this.origin, '0 0 0'); + + // now I am sure the player IS dead +} +void KillIndicator_Think(entity this) +{ + if (gameover) + { + this.owner.killindicator = NULL; + delete(this); + return; + } + + if (this.owner.alpha < 0 && !this.owner.vehicle) + { + this.owner.killindicator = NULL; + delete(this); + return; + } + + if(this.cnt <= 0) + { + ClientKill_Now(this.owner); + return; + } + else if(g_cts && this.health == 1) // health == 1 means that it's silent + { + this.nextthink = time + 1; + this.cnt -= 1; + } + else + { + if(this.cnt <= 10) + setmodel(this, MDL_NUM(this.cnt)); + if(IS_REAL_CLIENT(this.owner)) + { + if(this.cnt <= 10) + { Send_Notification(NOTIF_ONE, this.owner, MSG_ANNCE, Announcer_PickNumber(CNT_KILL, this.cnt)); } + } + this.nextthink = time + 1; + this.cnt -= 1; + } +} + +float clientkilltime; +void ClientKill_TeamChange (entity this, float targetteam) // 0 = don't change, -1 = auto, -2 = spec +{ + float killtime; + float starttime; + + if (gameover) + return; + + killtime = autocvar_g_balance_kill_delay; + + if(g_race_qualifying || g_cts) + killtime = 0; + + if(MUTATOR_CALLHOOK(ClientKill, this, killtime)) + return; + + this.killindicator_teamchange = targetteam; + + if(!this.killindicator) + { + if(!IS_DEAD(this)) + { + killtime = max(killtime, this.clientkill_nexttime - time); + this.clientkill_nexttime = time + killtime + autocvar_g_balance_kill_antispam; + } + + if(killtime <= 0 || !IS_PLAYER(this) || IS_DEAD(this)) + { + ClientKill_Now(this); + } + else + { + starttime = max(time, clientkilltime); + + this.killindicator = spawn(); + this.killindicator.owner = this; + this.killindicator.scale = 0.5; + setattachment(this.killindicator, this, ""); + setorigin(this.killindicator, '0 0 52'); + setthink(this.killindicator, KillIndicator_Think); + this.killindicator.nextthink = starttime + (this.lip) * 0.05; + clientkilltime = max(clientkilltime, this.killindicator.nextthink + 0.05); + this.killindicator.cnt = ceil(killtime); + this.killindicator.count = bound(0, ceil(killtime), 10); + //sprint(this, strcat("^1You'll be dead in ", ftos(this.killindicator.cnt), " seconds\n")); + + FOREACH_ENTITY_ENT(enemy, this, + { + if(it.classname != "body") + continue; + it.killindicator = spawn(); + it.killindicator.owner = it; + it.killindicator.scale = 0.5; + setattachment(it.killindicator, it, ""); + setorigin(it.killindicator, '0 0 52'); + setthink(it.killindicator, KillIndicator_Think); + it.killindicator.nextthink = starttime + (it.lip) * 0.05; + //clientkilltime = max(clientkilltime, it.killindicator.nextthink + 0.05); + it.killindicator.cnt = ceil(killtime); + }); + this.lip = 0; + } + } + if(this.killindicator) + { + if(targetteam == 0) // just die + { + this.killindicator.colormod = '0 0 0'; + if(IS_REAL_CLIENT(this)) + if(this.killindicator.cnt > 0) + Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_TEAMCHANGE_SUICIDE, this.killindicator.cnt); + } + else if(targetteam == -1) // auto + { + this.killindicator.colormod = '0 1 0'; + if(IS_REAL_CLIENT(this)) + if(this.killindicator.cnt > 0) + Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_TEAMCHANGE_AUTO, this.killindicator.cnt); + } + else if(targetteam == -2) // spectate + { + this.killindicator.colormod = '0.5 0.5 0.5'; + if(IS_REAL_CLIENT(this)) + if(this.killindicator.cnt > 0) + Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_TEAMCHANGE_SPECTATE, this.killindicator.cnt); + } + else + { + this.killindicator.colormod = Team_ColorRGB(targetteam); + if(IS_REAL_CLIENT(this)) + if(this.killindicator.cnt > 0) + Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, APP_TEAM_NUM(targetteam, CENTER_TEAMCHANGE), this.killindicator.cnt); + } + } + +} + +void ClientKill (entity this) +{ + if(gameover) return; + if(this.player_blocked) return; + if(STAT(FROZEN, this)) return; + + ClientKill_TeamChange(this, 0); +} + +void FixClientCvars(entity e) +{ + // send prediction settings to the client + stuffcmd(e, "\nin_bindmap 0 0\n"); + if(autocvar_g_antilag == 3) // client side hitscan + stuffcmd(e, "cl_cmd settemp cl_prydoncursor_notrace 0\n"); + if(autocvar_sv_gentle) + stuffcmd(e, "cl_cmd settemp cl_gentle 1\n"); + + MUTATOR_CALLHOOK(FixClientCvars, e); +} + +float PlayerInIDList(entity p, string idlist) +{ + float n, i; + string s; + + // NOTE: we do NOT check crypto_idfp_signed here, an unsigned ID is fine too for this + if (!p.crypto_idfp) + return 0; + + // this function allows abbreviated player IDs too! + n = tokenize_console(idlist); + for(i = 0; i < n; ++i) + { + s = argv(i); + if(s == substring(p.crypto_idfp, 0, strlen(s))) + return 1; + } + + return 0; +} + +#ifdef DP_EXT_PRECONNECT +/* +============= +ClientPreConnect + +Called once (not at each match start) when a client begins a connection to the server +============= +*/ +void ClientPreConnect () +{ENGINE_EVENT(); + if(autocvar_sv_eventlog) + { + GameLogEcho(sprintf(":connect:%d:%d:%s", + this.playerid, + etof(this), + ((IS_REAL_CLIENT(this)) ? this.netaddress : "bot") + )); + } +} +#endif + +/** +============= +ClientConnect + +Called when a client connects to the server +============= +*/ +void ClientConnect(entity this) +{ + if (Ban_MaybeEnforceBanOnce(this)) return; + assert(!IS_CLIENT(this), return); + this.flags |= FL_CLIENT; + assert(player_count >= 0, player_count = 0); + +#ifdef WATERMARK + Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_WATERMARK, WATERMARK); +#endif + this.version_nagtime = time + 10 + random() * 10; + TRANSMUTE(Client, this); + + // identify the right forced team + if (autocvar_g_campaign) + { + if (IS_REAL_CLIENT(this)) // only players, not bots + { + switch (autocvar_g_campaign_forceteam) + { + case 1: this.team_forced = NUM_TEAM_1; break; + case 2: this.team_forced = NUM_TEAM_2; break; + case 3: this.team_forced = NUM_TEAM_3; break; + case 4: this.team_forced = NUM_TEAM_4; break; + default: this.team_forced = 0; + } + } + } + else if (PlayerInIDList(this, autocvar_g_forced_team_red)) this.team_forced = NUM_TEAM_1; + else if (PlayerInIDList(this, autocvar_g_forced_team_blue)) this.team_forced = NUM_TEAM_2; + else if (PlayerInIDList(this, autocvar_g_forced_team_yellow)) this.team_forced = NUM_TEAM_3; + else if (PlayerInIDList(this, autocvar_g_forced_team_pink)) this.team_forced = NUM_TEAM_4; + else switch (autocvar_g_forced_team_otherwise) + { + default: this.team_forced = 0; break; + case "red": this.team_forced = NUM_TEAM_1; break; + case "blue": this.team_forced = NUM_TEAM_2; break; + case "yellow": this.team_forced = NUM_TEAM_3; break; + case "pink": this.team_forced = NUM_TEAM_4; break; + case "spectate": + case "spectator": + this.team_forced = -1; + break; + } + if (!teamplay && this.team_forced > 0) this.team_forced = 0; + + { + int id = this.playerid; + this.playerid = 0; // silent + JoinBestTeam(this, false, false); // if the team number is valid, keep it + this.playerid = id; + } + + if (autocvar_sv_spectate || autocvar_g_campaign || this.team_forced < 0) { + TRANSMUTE(Observer, this); + } else { + if (!teamplay || autocvar_g_balance_teams) { + TRANSMUTE(Player, this); + campaign_bots_may_start = true; + } else { + TRANSMUTE(Observer, this); // do it anyway + } + } + + PlayerStats_GameReport_AddEvent(sprintf("kills-%d", this.playerid)); + + // always track bots, don't ask for cl_allow_uidtracking + if (IS_BOT_CLIENT(this)) PlayerStats_GameReport_AddPlayer(this); + + if (autocvar_sv_eventlog) + GameLogEcho(strcat(":join:", ftos(this.playerid), ":", ftos(etof(this)), ":", ((IS_REAL_CLIENT(this)) ? this.netaddress : "bot"), ":", this.netname)); + + LogTeamchange(this.playerid, this.team, 1); + + this.just_joined = true; // stop spamming the eventlog with additional lines when the client connects + + this.netname_previous = strzone(this.netname); + + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((teamplay && IS_PLAYER(this)) ? APP_TEAM_ENT(this, INFO_JOIN_CONNECT_TEAM) : INFO_JOIN_CONNECT), this.netname); + + stuffcmd(this, clientstuff, "\n"); + stuffcmd(this, "cl_particles_reloadeffects\n"); // TODO do we still need this? + + FixClientCvars(this); + + // get version info from player + stuffcmd(this, "cmd clientversion $gameversion\n"); + + // notify about available teams + if (teamplay) + { + CheckAllowedTeams(this); + int t = 0; + if (c1 >= 0) t |= BIT(0); + if (c2 >= 0) t |= BIT(1); + if (c3 >= 0) t |= BIT(2); + if (c4 >= 0) t |= BIT(3); + stuffcmd(this, sprintf("set _teams_available %d\n", t)); + } + else + { + stuffcmd(this, "set _teams_available 0\n"); + } + + bot_relinkplayerlist(); + + this.spectatortime = time; + if (blockSpectators) + { + Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime); + } + + this.jointime = time; + this.allowed_timeouts = autocvar_sv_timeout_number; + + if (IS_REAL_CLIENT(this)) + { + if (!autocvar_g_campaign) + { + this.motd_actived_time = -1; + Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, getwelcomemessage(this)); + } + + if (g_weaponarena_weapons == WEPSET(TUBA)) + stuffcmd(this, "cl_cmd settemp chase_active 1\n"); + } + + if (!sv_foginterval && world.fog != "") + stuffcmd(this, strcat("\nfog ", world.fog, "\nr_fog_exp2 0\nr_drawfog 1\n")); + + if (autocvar_sv_teamnagger && !(autocvar_bot_vs_human && AvailableTeams() == 2)) + if (!g_ca && !g_cts && !g_race) // teamnagger is currently bad for ca, race & cts + send_CSQC_teamnagger(); + + CSQCMODEL_AUTOINIT(this); + + this.model_randomizer = random(); + + if (IS_REAL_CLIENT(this)) + sv_notice_join(this); + + FOREACH_ENTITY_FLOAT(init_for_player_needed, true, { + it.init_for_player(it, this); + }); + + MUTATOR_CALLHOOK(ClientConnect, this); +} +/* +============= +ClientDisconnect + +Called when a client disconnects from the server +============= +*/ +.entity chatbubbleentity; +void ReadyCount(); +void ClientDisconnect(entity this) +{ + assert(IS_CLIENT(this), return); + + PlayerStats_GameReport_FinalizePlayer(this); + if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE); + if (this.active_minigame) part_minigame(this); + if (IS_PLAYER(this)) Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1); + + if (autocvar_sv_eventlog) + GameLogEcho(strcat(":part:", ftos(this.playerid))); + + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_DISCONNECT, this.netname); + + SetSpectatee(this, NULL); + + MUTATOR_CALLHOOK(ClientDisconnect, this); + + ClientState_detach(this); + + Portal_ClearAll(this); + + Unfreeze(this); + + RemoveGrapplingHook(this); + + // Here, everything has been done that requires this player to be a client. + + this.flags &= ~FL_CLIENT; + + if (this.chatbubbleentity) delete(this.chatbubbleentity); + if (this.killindicator) delete(this.killindicator); + + WaypointSprite_PlayerGone(this); + + bot_relinkplayerlist(); + + if (this.netname_previous) strunzone(this.netname_previous); + if (this.clientstatus) strunzone(this.clientstatus); + if (this.weaponorder_byimpulse) strunzone(this.weaponorder_byimpulse); + if (this.personal) delete(this.personal); + + this.playerid = 0; + ReadyCount(); + if (vote_called && IS_REAL_CLIENT(this)) VoteCount(false); +} + +void ChatBubbleThink(entity this) +{ + this.nextthink = time; + if ((this.owner.alpha < 0) || this.owner.chatbubbleentity != this) + { + if(this.owner) // but why can that ever be NULL? + this.owner.chatbubbleentity = NULL; + delete(this); + return; + } + + this.mdl = ""; + + if ( !IS_DEAD(this.owner) && IS_PLAYER(this.owner) ) + { + if ( this.owner.active_minigame ) + this.mdl = "models/sprites/minigame_busy.iqm"; + else if (PHYS_INPUT_BUTTON_CHAT(this.owner)) + this.mdl = "models/misc/chatbubble.spr"; + } + + if ( this.model != this.mdl ) + _setmodel(this, this.mdl); + +} + +void UpdateChatBubble(entity this) +{ + if (this.alpha < 0) + return; + // spawn a chatbubble entity if needed + if (!this.chatbubbleentity) + { + this.chatbubbleentity = new(chatbubbleentity); + this.chatbubbleentity.owner = this; + this.chatbubbleentity.exteriormodeltoclient = this; + setthink(this.chatbubbleentity, ChatBubbleThink); + this.chatbubbleentity.nextthink = time; + setmodel(this.chatbubbleentity, MDL_CHAT); // precision set below + //setorigin(this.chatbubbleentity, this.origin + '0 0 15' + this.maxs_z * '0 0 1'); + setorigin(this.chatbubbleentity, '0 0 15' + this.maxs_z * '0 0 1'); + setattachment(this.chatbubbleentity, this, ""); // sticks to moving player better, also conserves bandwidth + this.chatbubbleentity.mdl = this.chatbubbleentity.model; + //this.chatbubbleentity.model = ""; + this.chatbubbleentity.effects = EF_LOWPRECISION; + } +} + + +// LordHavoc: this hack will be removed when proper _pants/_shirt layers are +// added to the model skins +/*void UpdateColorModHack() +{ + float c; + c = this.clientcolors & 15; + // LordHavoc: only bothering to support white, green, red, yellow, blue + if (!teamplay) this.colormod = '0 0 0'; + else if (c == 0) this.colormod = '1.00 1.00 1.00'; + else if (c == 3) this.colormod = '0.10 1.73 0.10'; + else if (c == 4) this.colormod = '1.73 0.10 0.10'; + else if (c == 12) this.colormod = '1.22 1.22 0.10'; + else if (c == 13) this.colormod = '0.10 0.10 1.73'; + else this.colormod = '1 1 1'; +}*/ + +void respawn(entity this) +{ + if(this.alpha >= 0 && autocvar_g_respawn_ghosts) + { + this.solid = SOLID_NOT; + this.takedamage = DAMAGE_NO; + set_movetype(this, MOVETYPE_FLY); + this.velocity = '0 0 1' * autocvar_g_respawn_ghosts_speed; + this.avelocity = randomvec() * autocvar_g_respawn_ghosts_speed * 3 - randomvec() * autocvar_g_respawn_ghosts_speed * 3; + this.effects |= CSQCMODEL_EF_RESPAWNGHOST; + Send_Effect(EFFECT_RESPAWN_GHOST, this.origin, '0 0 0', 1); + if(autocvar_g_respawn_ghosts_maxtime) + SUB_SetFade (this, time + autocvar_g_respawn_ghosts_maxtime / 2 + random () * (autocvar_g_respawn_ghosts_maxtime - autocvar_g_respawn_ghosts_maxtime / 2), 1.5); + } + + CopyBody(this, 1); + + this.effects |= EF_NODRAW; // prevent another CopyBody + PutClientInServer(this); +} + +void play_countdown(entity this, float finished, Sound samp) +{ + TC(Sound, samp); + if(IS_REAL_CLIENT(this)) + if(floor(finished - time - frametime) != floor(finished - time)) + if(finished - time < 6) + sound (this, CH_INFO, samp, VOL_BASE, ATTEN_NORM); +} + +void player_powerups(entity this) +{ + // add a way to see what the items were BEFORE all of these checks for the mutator hook + int items_prev = this.items; + + if((this.items & IT_USING_JETPACK) && !IS_DEAD(this) && !gameover) + this.modelflags |= MF_ROCKET; + else + this.modelflags &= ~MF_ROCKET; + + this.effects &= ~(EF_RED | EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT | EF_FLAME | EF_NODEPTHTEST); + + if((this.alpha < 0 || IS_DEAD(this)) && !this.vehicle) // don't apply the flags if the player is gibbed + return; + + Fire_ApplyDamage(this); + Fire_ApplyEffect(this); + + if (!g_instagib) + { + if (this.items & ITEM_Strength.m_itemid) + { + play_countdown(this, this.strength_finished, SND_POWEROFF); + this.effects = this.effects | (EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT); + if (time > this.strength_finished) + { + this.items = this.items - (this.items & ITEM_Strength.m_itemid); + //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERDOWN_STRENGTH, this.netname); + Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERDOWN_STRENGTH); + } + } + else + { + if (time < this.strength_finished) + { + this.items = this.items | ITEM_Strength.m_itemid; + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_STRENGTH, this.netname); + Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERUP_STRENGTH); + } + } + if (this.items & ITEM_Shield.m_itemid) + { + play_countdown(this, this.invincible_finished, SND_POWEROFF); + this.effects = this.effects | (EF_RED | EF_ADDITIVE | EF_FULLBRIGHT); + if (time > this.invincible_finished) + { + this.items = this.items - (this.items & ITEM_Shield.m_itemid); + //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERDOWN_SHIELD, this.netname); + Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERDOWN_SHIELD); + } + } + else + { + if (time < this.invincible_finished) + { + this.items = this.items | ITEM_Shield.m_itemid; + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_SHIELD, this.netname); + Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERUP_SHIELD); + } + } + if (this.items & IT_SUPERWEAPON) + { + if (!(this.weapons & WEPSET_SUPERWEAPONS)) + { + this.superweapons_finished = 0; + this.items = this.items - (this.items & IT_SUPERWEAPON); + //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_LOST, this.netname); + Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_LOST); + } + else if (this.items & IT_UNLIMITED_SUPERWEAPONS) + { + // don't let them run out + } + else + { + play_countdown(this, this.superweapons_finished, SND_POWEROFF); + if (time > this.superweapons_finished) + { + this.items = this.items - (this.items & IT_SUPERWEAPON); + this.weapons &= ~WEPSET_SUPERWEAPONS; + //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_BROKEN, this.netname); + Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_BROKEN); + } + } + } + else if(this.weapons & WEPSET_SUPERWEAPONS) + { + if (time < this.superweapons_finished || (this.items & IT_UNLIMITED_SUPERWEAPONS)) + { + this.items = this.items | IT_SUPERWEAPON; + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_PICKUP, this.netname); + Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_PICKUP); + } + else + { + this.superweapons_finished = 0; + this.weapons &= ~WEPSET_SUPERWEAPONS; + } + } + else + { + this.superweapons_finished = 0; + } + } + + if(autocvar_g_nodepthtestplayers) + this.effects = this.effects | EF_NODEPTHTEST; + + if(autocvar_g_fullbrightplayers) + this.effects = this.effects | EF_FULLBRIGHT; + + if (time >= game_starttime) + if (time < this.spawnshieldtime) + this.effects = this.effects | (EF_ADDITIVE | EF_FULLBRIGHT); + + MUTATOR_CALLHOOK(PlayerPowerups, this, items_prev); +} + +float CalcRegen(float current, float stable, float regenfactor, float regenframetime) +{ + if(current > stable) + return current; + else if(current > stable - 0.25) // when close enough, "snap" + return stable; + else + return min(stable, current + (stable - current) * regenfactor * regenframetime); +} + +float CalcRot(float current, float stable, float rotfactor, float rotframetime) +{ + if(current < stable) + return current; + else if(current < stable + 0.25) // when close enough, "snap" + return stable; + else + return max(stable, current + (stable - current) * rotfactor * rotframetime); +} + +float CalcRotRegen(float current, float regenstable, float regenfactor, float regenlinear, float regenframetime, float rotstable, float rotfactor, float rotlinear, float rotframetime, float limit) +{ + if(current > rotstable) + { + if(rotframetime > 0) + { + current = CalcRot(current, rotstable, rotfactor, rotframetime); + current = max(rotstable, current - rotlinear * rotframetime); + } + } + else if(current < regenstable) + { + if(regenframetime > 0) + { + current = CalcRegen(current, regenstable, regenfactor, regenframetime); + current = min(regenstable, current + regenlinear * regenframetime); + } + } + + if(current > limit) + current = limit; + + return current; +} + +void player_regen(entity this) +{ + float max_mod, regen_mod, rot_mod, limit_mod; + max_mod = regen_mod = rot_mod = limit_mod = 1; + + float regen_health = autocvar_g_balance_health_regen; + float regen_health_linear = autocvar_g_balance_health_regenlinear; + float regen_health_rot = autocvar_g_balance_health_rot; + float regen_health_rotlinear = autocvar_g_balance_health_rotlinear; + float regen_health_stable = autocvar_g_balance_health_regenstable; + float regen_health_rotstable = autocvar_g_balance_health_rotstable; + bool mutator_returnvalue = MUTATOR_CALLHOOK(PlayerRegen, this, max_mod, regen_mod, rot_mod, limit_mod, regen_health, regen_health_linear, regen_health_rot, + regen_health_rotlinear, regen_health_stable, regen_health_rotstable); + max_mod = M_ARGV(1, float); + regen_mod = M_ARGV(2, float); + rot_mod = M_ARGV(3, float); + limit_mod = M_ARGV(4, float); + regen_health = M_ARGV(5, float); + regen_health_linear = M_ARGV(6, float); + regen_health_rot = M_ARGV(7, float); + regen_health_rotlinear = M_ARGV(8, float); + regen_health_stable = M_ARGV(9, float); + regen_health_rotstable = M_ARGV(10, float); + + + if(!mutator_returnvalue) + if(!STAT(FROZEN, this)) + { + float mina, maxa, limith, limita; + maxa = autocvar_g_balance_armor_rotstable; + mina = autocvar_g_balance_armor_regenstable; + limith = autocvar_g_balance_health_limit; + limita = autocvar_g_balance_armor_limit; + + regen_health_rotstable = regen_health_rotstable * max_mod; + regen_health_stable = regen_health_stable * max_mod; + limith = limith * limit_mod; + limita = limita * limit_mod; + + this.armorvalue = CalcRotRegen(this.armorvalue, mina, autocvar_g_balance_armor_regen, autocvar_g_balance_armor_regenlinear, regen_mod * frametime * (time > this.pauseregen_finished), maxa, autocvar_g_balance_armor_rot, autocvar_g_balance_armor_rotlinear, rot_mod * frametime * (time > this.pauserotarmor_finished), limita); + this.health = CalcRotRegen(this.health, regen_health_stable, regen_health, regen_health_linear, regen_mod * frametime * (time > this.pauseregen_finished), regen_health_rotstable, regen_health_rot, regen_health_rotlinear, rot_mod * frametime * (time > this.pauserothealth_finished), limith); + } + + // if player rotted to death... die! + // check this outside above checks, as player may still be able to rot to death + if(this.health < 1) + { + if(this.vehicle) + vehicles_exit(this.vehicle, VHEF_RELEASE); + if(this.event_damage) + this.event_damage(this, this, this, 1, DEATH_ROT.m_id, this.origin, '0 0 0'); + } + + if (!(this.items & IT_UNLIMITED_WEAPON_AMMO)) + { + float minf, maxf, limitf; + + maxf = autocvar_g_balance_fuel_rotstable; + minf = autocvar_g_balance_fuel_regenstable; + limitf = autocvar_g_balance_fuel_limit; + + this.ammo_fuel = CalcRotRegen(this.ammo_fuel, minf, autocvar_g_balance_fuel_regen, autocvar_g_balance_fuel_regenlinear, frametime * (time > this.pauseregen_finished) * ((this.items & ITEM_JetpackRegen.m_itemid) != 0), maxf, autocvar_g_balance_fuel_rot, autocvar_g_balance_fuel_rotlinear, frametime * (time > this.pauserotfuel_finished), limitf); + } +} + +bool zoomstate_set; +void SetZoomState(entity this, float z) +{ + if(z != this.zoomstate) + { + this.zoomstate = z; + ClientData_Touch(this); + } + zoomstate_set = true; +} + +void GetPressedKeys(entity this) +{ + MUTATOR_CALLHOOK(GetPressedKeys, this); + int keys = this.pressedkeys; + keys = BITSET(keys, KEY_FORWARD, this.movement.x > 0); + keys = BITSET(keys, KEY_BACKWARD, this.movement.x < 0); + keys = BITSET(keys, KEY_RIGHT, this.movement.y > 0); + keys = BITSET(keys, KEY_LEFT, this.movement.y < 0); + + keys = BITSET(keys, KEY_JUMP, PHYS_INPUT_BUTTON_JUMP(this)); + keys = BITSET(keys, KEY_CROUCH, PHYS_INPUT_BUTTON_CROUCH(this)); + keys = BITSET(keys, KEY_ATCK, PHYS_INPUT_BUTTON_ATCK(this)); + keys = BITSET(keys, KEY_ATCK2, PHYS_INPUT_BUTTON_ATCK2(this)); + this.pressedkeys = keys; +} + +/* +====================== +spectate mode routines +====================== +*/ + +void SpectateCopy(entity this, entity spectatee) +{ + TC(Client, this); TC(Client, spectatee); + + MUTATOR_CALLHOOK(SpectateCopy, spectatee, this); + PS(this) = PS(spectatee); + this.armortype = spectatee.armortype; + this.armorvalue = spectatee.armorvalue; + this.ammo_cells = spectatee.ammo_cells; + this.ammo_plasma = spectatee.ammo_plasma; + this.ammo_shells = spectatee.ammo_shells; + this.ammo_nails = spectatee.ammo_nails; + this.ammo_rockets = spectatee.ammo_rockets; + this.ammo_fuel = spectatee.ammo_fuel; + this.clip_load = spectatee.clip_load; + this.clip_size = spectatee.clip_size; + this.effects = spectatee.effects & EFMASK_CHEAP; // eat performance + this.health = spectatee.health; + this.impulse = 0; + this.items = spectatee.items; + this.last_pickup = spectatee.last_pickup; + this.hit_time = spectatee.hit_time; + this.strength_finished = spectatee.strength_finished; + this.invincible_finished = spectatee.invincible_finished; + this.pressedkeys = spectatee.pressedkeys; + this.weapons = spectatee.weapons; + this.vortex_charge = spectatee.vortex_charge; + this.vortex_chargepool_ammo = spectatee.vortex_chargepool_ammo; + this.hagar_load = spectatee.hagar_load; + this.arc_heat_percent = spectatee.arc_heat_percent; + this.minelayer_mines = spectatee.minelayer_mines; + this.punchangle = spectatee.punchangle; + this.view_ofs = spectatee.view_ofs; + this.velocity = spectatee.velocity; + this.dmg_take = spectatee.dmg_take; + this.dmg_save = spectatee.dmg_save; + this.dmg_inflictor = spectatee.dmg_inflictor; + this.v_angle = spectatee.v_angle; + this.angles = spectatee.v_angle; + STAT(FROZEN, this) = STAT(FROZEN, spectatee); + this.revive_progress = spectatee.revive_progress; + if(!PHYS_INPUT_BUTTON_USE(this) && STAT(CAMERA_SPECTATOR, this) != 2) + this.fixangle = true; + setorigin(this, spectatee.origin); + setsize(this, spectatee.mins, spectatee.maxs); + SetZoomState(this, spectatee.zoomstate); + + anticheat_spectatecopy(this, spectatee); + this.hud = spectatee.hud; + if(spectatee.vehicle) + { + this.angles = spectatee.v_angle; + + //this.fixangle = false; + //this.velocity = spectatee.vehicle.velocity; + this.vehicle_health = spectatee.vehicle_health; + this.vehicle_shield = spectatee.vehicle_shield; + this.vehicle_energy = spectatee.vehicle_energy; + this.vehicle_ammo1 = spectatee.vehicle_ammo1; + this.vehicle_ammo2 = spectatee.vehicle_ammo2; + this.vehicle_reload1 = spectatee.vehicle_reload1; + this.vehicle_reload2 = spectatee.vehicle_reload2; + + //msg_entity = this; + + // WriteByte (MSG_ONE, SVC_SETVIEWANGLES); + //WriteAngle(MSG_ONE, spectatee.v_angle.x); + // WriteAngle(MSG_ONE, spectatee.v_angle.y); + // WriteAngle(MSG_ONE, spectatee.v_angle.z); + + //WriteByte (MSG_ONE, SVC_SETVIEW); + // WriteEntity(MSG_ONE, this); + //makevectors(spectatee.v_angle); + //setorigin(this, spectatee.origin - v_forward * 400 + v_up * 300);*/ + } +} + +bool SpectateUpdate(entity this) +{ + if(!this.enemy) + return false; + + if(!IS_PLAYER(this.enemy) || this == this.enemy) + { + SetSpectatee(this, NULL); + return false; + } + + SpectateCopy(this, this.enemy); + + return true; +} + +bool SpectateSet(entity this) +{ + if(!IS_PLAYER(this.enemy)) + return false; + + ClientData_Touch(this.enemy); + + msg_entity = this; + WriteByte(MSG_ONE, SVC_SETVIEW); + WriteEntity(MSG_ONE, this.enemy); + set_movetype(this, MOVETYPE_NONE); + accuracy_resend(this); + + if(!SpectateUpdate(this)) + PutObserverInServer(this); + + return true; +} + +void SetSpectatee_status(entity this, int spectatee_num) +{ + int oldspectatee_status = this.spectatee_status; + this.spectatee_status = spectatee_num; + + if (this.spectatee_status != oldspectatee_status) + { + ClientData_Touch(this); + if (g_race || g_cts) race_InitSpectator(); + } +} + +void SetSpectatee(entity this, entity spectatee) +{ + entity old_spectatee = this.enemy; + + this.enemy = spectatee; + + // WEAPONTODO + // these are required to fix the spectator bug with arc + if(old_spectatee) + { + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + .entity weaponentity = weaponentities[slot]; + if(old_spectatee.(weaponentity).arc_beam) + old_spectatee.(weaponentity).arc_beam.SendFlags |= ARC_SF_SETTINGS; + } + } + if(this.enemy) + { + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + .entity weaponentity = weaponentities[slot]; + if(this.enemy.(weaponentity).arc_beam) + this.enemy.(weaponentity).arc_beam.SendFlags |= ARC_SF_SETTINGS; + } + } + + if (this.enemy) + SetSpectatee_status(this, etof(this.enemy)); + + // needed to update spectator list + if(old_spectatee) { ClientData_Touch(old_spectatee); } +} + +bool Spectate(entity this, entity pl) +{ + if(MUTATOR_CALLHOOK(SpectateSet, this, pl)) + return false; + pl = M_ARGV(1, entity); + + SetSpectatee(this, pl); + return SpectateSet(this); +} + +bool SpectateNext(entity this) +{ + entity ent = find(this.enemy, classname, STR_PLAYER); + + if (MUTATOR_CALLHOOK(SpectateNext, this, ent)) + ent = M_ARGV(1, entity); + else if (!ent) + ent = find(ent, classname, STR_PLAYER); + + if(ent) { SetSpectatee(this, ent); } + + return SpectateSet(this); +} + +bool SpectatePrev(entity this) +{ + // NOTE: chain order is from the highest to the lower entnum (unlike find) + entity ent = findchain(classname, STR_PLAYER); + if (!ent) // no player + return false; + + entity first = ent; + // skip players until current spectated player + if(this.enemy) + while(ent && ent != this.enemy) + ent = ent.chain; + + switch (MUTATOR_CALLHOOK(SpectatePrev, this, ent, first)) + { + case MUT_SPECPREV_FOUND: + ent = M_ARGV(1, entity); + break; + case MUT_SPECPREV_RETURN: + return true; + case MUT_SPECPREV_CONTINUE: + default: + { + if(ent.chain) + ent = ent.chain; + else + ent = first; + break; + } + } + + SetSpectatee(this, ent); + return SpectateSet(this); +} + +/* +============= +ShowRespawnCountdown() + +Update a respawn countdown display. +============= +*/ +void ShowRespawnCountdown(entity this) +{ + float number; + if(!IS_DEAD(this)) // just respawned? + return; + else + { + number = ceil(this.respawn_time - time); + if(number <= 0) + return; + if(number <= this.respawn_countdown) + { + this.respawn_countdown = number - 1; + if(ceil(this.respawn_time - (time + 0.5)) == number) // only say it if it is the same number even in 0.5s; to prevent overlapping sounds + { Send_Notification(NOTIF_ONE, this, MSG_ANNCE, Announcer_PickNumber(CNT_RESPAWN, number)); } + } + } +} + +.float caplayer; + +void LeaveSpectatorMode(entity this) +{ + if(this.caplayer) + return; + if(nJoinAllowed(this, this)) + { + if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || (this.wasplayer && autocvar_g_changeteam_banned) || this.team_forced > 0) + { + TRANSMUTE(Player, this); + + SetSpectatee(this, NULL); + + if(autocvar_g_campaign || autocvar_g_balance_teams) + { JoinBestTeam(this, false, true); } + + if(autocvar_g_campaign) + { campaign_bots_may_start = true; } + + Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_PREVENT_JOIN); + + PutClientInServer(this); + + if(IS_PLAYER(this)) { Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((teamplay && this.team != -1) ? APP_TEAM_ENT(this, INFO_JOIN_PLAY_TEAM) : INFO_JOIN_PLAY), this.netname); } + } + else + stuffcmd(this, "menu_showteamselect\n"); + } + else + { + // Player may not join because g_maxplayers is set + Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PREVENT); + } +} + +/** + * Determines whether the player is allowed to join. This depends on cvar + * g_maxplayers, if it isn't used this function always return true, otherwise + * it checks whether the number of currently playing players exceeds g_maxplayers. + * @return int number of free slots for players, 0 if none + */ +bool nJoinAllowed(entity this, entity ignore) +{ + if(!ignore) + // this is called that way when checking if anyone may be able to join (to build qcstatus) + // so report 0 free slots if restricted + { + if(autocvar_g_forced_team_otherwise == "spectate") + return false; + if(autocvar_g_forced_team_otherwise == "spectator") + return false; + } + + if(this.team_forced < 0) + return false; // forced spectators can never join + + // TODO simplify this + int totalClients = 0; + int currentlyPlaying = 0; + FOREACH_CLIENT(true, LAMBDA( + if(it != ignore) + ++totalClients; + if(IS_REAL_CLIENT(it)) + if(IS_PLAYER(it) || it.caplayer) + ++currentlyPlaying; + )); + + if (!autocvar_g_maxplayers) + return maxclients - totalClients; + + if(currentlyPlaying < autocvar_g_maxplayers) + return min(maxclients - totalClients, autocvar_g_maxplayers - currentlyPlaying); + + return false; +} + +/** + * Checks whether the client is an observer or spectator, if so, he will get kicked after + * g_maxplayers_spectator_blocktime seconds + */ +void checkSpectatorBlock(entity this) +{ + if(IS_SPEC(this) || IS_OBSERVER(this)) + if(!this.caplayer) + if(IS_REAL_CLIENT(this)) + { + if( time > (this.spectatortime + autocvar_g_maxplayers_spectator_blocktime) ) { + Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_QUIT_KICK_SPECTATING); + dropclient(this); + } + } +} + +void PrintWelcomeMessage(entity this) +{ + if(this.motd_actived_time == 0) + { + if (autocvar_g_campaign) { + if ((IS_PLAYER(this) && PHYS_INPUT_BUTTON_INFO(this)) || (!IS_PLAYER(this))) { + this.motd_actived_time = time; + Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, campaign_message); + } + } else { + if (PHYS_INPUT_BUTTON_INFO(this)) { + this.motd_actived_time = time; + Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, getwelcomemessage(this)); + } + } + } + else if(this.motd_actived_time > 0) // showing MOTD or campaign message + { + if (autocvar_g_campaign) { + if (PHYS_INPUT_BUTTON_INFO(this)) + this.motd_actived_time = time; + else if ((time - this.motd_actived_time > 2) && IS_PLAYER(this)) { // hide it some seconds after BUTTON_INFO has been released + this.motd_actived_time = 0; + Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD); + } + } else { + if (PHYS_INPUT_BUTTON_INFO(this)) + this.motd_actived_time = time; + else if (time - this.motd_actived_time > 2) { // hide it some seconds after BUTTON_INFO has been released + this.motd_actived_time = 0; + Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD); + } + } + } + else //if(this.motd_actived_time < 0) // just connected, motd is active + { + if(PHYS_INPUT_BUTTON_INFO(this)) // BUTTON_INFO hides initial MOTD + this.motd_actived_time = -2; // wait until BUTTON_INFO gets released + else if(this.motd_actived_time == -2 || IS_PLAYER(this) || IS_SPEC(this)) + { + // instanctly hide MOTD + this.motd_actived_time = 0; + Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD); + } + } +} + +void ObserverThink(entity this) +{ + if ( this.impulse ) + { + MinigameImpulse(this, this.impulse); + this.impulse = 0; + } + if (this.flags & FL_JUMPRELEASED) { + if (PHYS_INPUT_BUTTON_JUMP(this) && !this.version_mismatch) { + this.flags &= ~FL_JUMPRELEASED; + this.flags |= FL_SPAWNING; + } else if(PHYS_INPUT_BUTTON_ATCK(this) && !this.version_mismatch) { + this.flags &= ~FL_JUMPRELEASED; + if(SpectateNext(this)) { + TRANSMUTE(Spectator, this); + } + } else { + int preferred_movetype = ((!PHYS_INPUT_BUTTON_USE(this) ? this.cvar_cl_clippedspectating : !this.cvar_cl_clippedspectating) ? MOVETYPE_FLY_WORLDONLY : MOVETYPE_NOCLIP); + set_movetype(this, preferred_movetype); + } + } else { + if (!(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this))) { + this.flags |= FL_JUMPRELEASED; + if(this.flags & FL_SPAWNING) + { + this.flags &= ~FL_SPAWNING; + LeaveSpectatorMode(this); + return; + } + } + } +} + +void SpectatorThink(entity this) +{ + if ( this.impulse ) + { + if(MinigameImpulse(this, this.impulse)) + this.impulse = 0; + + if (this.impulse == IMP_weapon_drop.impulse) + { + STAT(CAMERA_SPECTATOR, this) = (STAT(CAMERA_SPECTATOR, this) + 1) % 3; + this.impulse = 0; + return; + } + } + if (this.flags & FL_JUMPRELEASED) { + if (PHYS_INPUT_BUTTON_JUMP(this) && !this.version_mismatch) { + this.flags &= ~FL_JUMPRELEASED; + this.flags |= FL_SPAWNING; + } else if(PHYS_INPUT_BUTTON_ATCK(this) || this.impulse == 10 || this.impulse == 15 || this.impulse == 18 || (this.impulse >= 200 && this.impulse <= 209)) { + this.flags &= ~FL_JUMPRELEASED; + if(SpectateNext(this)) { + TRANSMUTE(Spectator, this); + } else { + TRANSMUTE(Observer, this); + PutClientInServer(this); + } + this.impulse = 0; + } else if(this.impulse == 12 || this.impulse == 16 || this.impulse == 19 || (this.impulse >= 220 && this.impulse <= 229)) { + this.flags &= ~FL_JUMPRELEASED; + if(SpectatePrev(this)) { + TRANSMUTE(Spectator, this); + } else { + TRANSMUTE(Observer, this); + PutClientInServer(this); + } + this.impulse = 0; + } else if (PHYS_INPUT_BUTTON_ATCK2(this)) { + this.flags &= ~FL_JUMPRELEASED; + TRANSMUTE(Observer, this); + PutClientInServer(this); + } else { + if(!SpectateUpdate(this)) + PutObserverInServer(this); + } + } else { + if (!(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this))) { + this.flags |= FL_JUMPRELEASED; + if(this.flags & FL_SPAWNING) + { + this.flags &= ~FL_SPAWNING; + LeaveSpectatorMode(this); + return; + } + } + if(!SpectateUpdate(this)) + PutObserverInServer(this); + } + + this.flags |= FL_CLIENT | FL_NOTARGET; +} + +void vehicles_enter (entity pl, entity veh); +void PlayerUseKey(entity this) +{ + if (!IS_PLAYER(this)) + return; + + if(this.vehicle) + { + if(!gameover) + { + vehicles_exit(this.vehicle, VHEF_NORMAL); + return; + } + } + else if(autocvar_g_vehicles_enter) + { + if(!STAT(FROZEN, this)) + if(!IS_DEAD(this)) + if(!gameover) + { + entity head, closest_target = NULL; + head = WarpZone_FindRadius(this.origin, autocvar_g_vehicles_enter_radius, true); + + while(head) // find the closest acceptable target to enter + { + if(IS_VEHICLE(head)) + if(!IS_DEAD(head)) + if(!head.owner || ((head.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(head.owner, this))) + if(head.takedamage != DAMAGE_NO) + { + if(closest_target) + { + if(vlen2(this.origin - head.origin) < vlen2(this.origin - closest_target.origin)) + { closest_target = head; } + } + else { closest_target = head; } + } + + head = head.chain; + } + + if(closest_target) { vehicles_enter(this, closest_target); return; } + } + } + + // a use key was pressed; call handlers + MUTATOR_CALLHOOK(PlayerUseKey, this); +} + + +/* +============= +PlayerPreThink + +Called every frame for each client before the physics are run +============= +*/ +.float usekeypressed; +.float last_vehiclecheck; +.int items_added; +void PlayerPreThink (entity this) +{ + WarpZone_PlayerPhysics_FixVAngle(this); + + STAT(GAMESTARTTIME, this) = game_starttime; + STAT(ROUNDSTARTTIME, this) = round_starttime; + STAT(ALLOW_OLDVORTEXBEAM, this) = autocvar_g_allow_oldvortexbeam; + STAT(LEADLIMIT, this) = autocvar_leadlimit; + + STAT(WEAPONSINMAP, this) = weaponsInMap; + + if (frametime) { + // physics frames: update anticheat stuff + anticheat_prethink(this); + } + + if (blockSpectators && frametime) { + // WORKAROUND: only use dropclient in server frames (frametime set). + // Never use it in cl_movement frames (frametime zero). + checkSpectatorBlock(this); + } + + zoomstate_set = false; + + // Check for nameless players + if (isInvisibleString(this.netname)) { + this.netname = strzone(sprintf("Player#%d", this.playerid)); + // stuffcmd(this, strcat("name ", this.netname, "\n")); // maybe? + } + if (this.netname != this.netname_previous) { + if (autocvar_sv_eventlog) { + GameLogEcho(strcat(":name:", ftos(this.playerid), ":", this.netname)); + } + if (this.netname_previous) strunzone(this.netname_previous); + this.netname_previous = strzone(this.netname); + } + + // version nagging + if (this.version_nagtime && this.cvar_g_xonoticversion && time > this.version_nagtime) { + this.version_nagtime = 0; + if (strstrofs(this.cvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(this.cvar_g_xonoticversion, "autobuild", 0) >= 0) { + // git client + } else if (strstrofs(autocvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(autocvar_g_xonoticversion, "autobuild", 0) >= 0) { + // git server + Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_BETA, autocvar_g_xonoticversion, this.cvar_g_xonoticversion); + } else { + int r = vercmp(this.cvar_g_xonoticversion, autocvar_g_xonoticversion); + if (r < 0) { // old client + Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OUTDATED, autocvar_g_xonoticversion, this.cvar_g_xonoticversion); + } else if (r > 0) { // old server + Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OLD, autocvar_g_xonoticversion, this.cvar_g_xonoticversion); + } + } + } + + // GOD MODE info + if (!(this.flags & FL_GODMODE) && this.max_armorvalue) + { + Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_GODMODE_OFF, this.max_armorvalue); + this.max_armorvalue = 0; + } + + if (STAT(FROZEN, this) == 2) + { + this.revive_progress = bound(0, this.revive_progress + frametime * this.revive_speed, 1); + this.health = max(1, this.revive_progress * start_health); + this.iceblock.alpha = bound(0.2, 1 - this.revive_progress, 1); + + if (this.revive_progress >= 1) + Unfreeze(this); + } + else if (STAT(FROZEN, this) == 3) + { + this.revive_progress = bound(0, this.revive_progress - frametime * this.revive_speed, 1); + this.health = max(0, autocvar_g_nades_ice_health + (start_health-autocvar_g_nades_ice_health) * this.revive_progress ); + + if (this.health < 1) + { + if (this.vehicle) + vehicles_exit(this.vehicle, VHEF_RELEASE); + if(this.event_damage) + this.event_damage(this, this, this.frozen_by, 1, DEATH_NADE_ICE_FREEZE.m_id, this.origin, '0 0 0'); + } + else if (this.revive_progress <= 0) + Unfreeze(this); + } + + MUTATOR_CALLHOOK(PlayerPreThink, this); + + if(autocvar_g_vehicles_enter && (time > this.last_vehiclecheck) && !gameover && !this.vehicle) + if(IS_PLAYER(this) && !STAT(FROZEN, this) && !IS_DEAD(this)) + { + FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_vehicles_enter_radius, IS_VEHICLE(it), + { + if(!IS_DEAD(it) && it.takedamage != DAMAGE_NO) + if((it.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(it.owner, this)) + { + Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_GUNNER); + } + else if(!it.owner) + { + if(!it.team || SAME_TEAM(this, it)) + Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER); + else if(autocvar_g_vehicles_steal) + Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_STEAL); + } + }); + + this.last_vehiclecheck = time + 1; + } + + if(!this.cvar_cl_newusekeysupported) // FIXME remove this - it was a stupid idea to begin with, we can JUST use the button + { + if(PHYS_INPUT_BUTTON_USE(this) && !this.usekeypressed) + PlayerUseKey(this); + this.usekeypressed = PHYS_INPUT_BUTTON_USE(this); + } + + if (IS_REAL_CLIENT(this)) + PrintWelcomeMessage(this); + + if (IS_PLAYER(this)) { + CheckRules_Player(this); + + if (intermission_running) { + IntermissionThink(this); + return; + } + + if (timeout_status == TIMEOUT_ACTIVE) { + // don't allow the player to turn around while game is paused + // FIXME turn this into CSQC stuff + this.v_angle = this.lastV_angle; + this.angles = this.lastV_angle; + this.fixangle = true; + } + + if (frametime) player_powerups(this); + + if (IS_DEAD(this)) { + if (this.personal && g_race_qualifying) { + if (time > this.respawn_time) { + STAT(RESPAWN_TIME, this) = this.respawn_time = time + 1; // only retry once a second + respawn(this); + this.impulse = CHIMPULSE_SPEEDRUN.impulse; + } + } else { + if (frametime) player_anim(this); + bool button_pressed = (PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_ATCK2(this) || PHYS_INPUT_BUTTON_HOOK(this) || PHYS_INPUT_BUTTON_USE(this)); + + switch(this.deadflag) + { + case DEAD_DYING: + { + if ((this.respawn_flags & RESPAWN_FORCE) && !(this.respawn_time < this.respawn_time_max)) + this.deadflag = DEAD_RESPAWNING; + else if (!button_pressed || (this.respawn_flags & RESPAWN_FORCE)) + this.deadflag = DEAD_DEAD; + break; + } + case DEAD_DEAD: + { + if (button_pressed) + this.deadflag = DEAD_RESPAWNABLE; + else if (time >= this.respawn_time_max && (this.respawn_flags & RESPAWN_FORCE)) + this.deadflag = DEAD_RESPAWNING; + break; + } + case DEAD_RESPAWNABLE: + { + if (!button_pressed || (this.respawn_flags & RESPAWN_FORCE)) + this.deadflag = DEAD_RESPAWNING; + break; + } + case DEAD_RESPAWNING: + { + if (time > this.respawn_time) + { + this.respawn_time = time + 1; // only retry once a second + this.respawn_time_max = this.respawn_time; + respawn(this); + } + break; + } + } + + ShowRespawnCountdown(this); + + if (this.respawn_flags & RESPAWN_SILENT) + STAT(RESPAWN_TIME, this) = 0; + else if ((this.respawn_flags & RESPAWN_FORCE) && this.respawn_time < this.respawn_time_max) + { + if (time < this.respawn_time) + STAT(RESPAWN_TIME, this) = this.respawn_time; + else if (this.deadflag != DEAD_RESPAWNING) + STAT(RESPAWN_TIME, this) = -this.respawn_time_max; + } + else + STAT(RESPAWN_TIME, this) = this.respawn_time; + } + + // if respawning, invert stat_respawn_time to indicate this, the client translates it + if (this.deadflag == DEAD_RESPAWNING && STAT(RESPAWN_TIME, this) > 0) + STAT(RESPAWN_TIME, this) *= -1; + + return; + } + + this.prevorigin = this.origin; + + bool do_crouch = PHYS_INPUT_BUTTON_CROUCH(this); + if (this.hook.state) { + do_crouch = false; + } else if (this.waterlevel >= WATERLEVEL_SWIMMING) { + do_crouch = false; + } else if (this.vehicle) { + do_crouch = false; + } else if (STAT(FROZEN, this)) { + do_crouch = false; + } + + if (do_crouch) { + if (!this.crouch) { + this.crouch = true; + this.view_ofs = STAT(PL_CROUCH_VIEW_OFS, this); + setsize(this, STAT(PL_CROUCH_MIN, this), STAT(PL_CROUCH_MAX, this)); + // setanim(this, this.anim_duck, false, true, true); // this anim is BROKEN anyway + } + } else if (this.crouch) { + tracebox(this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), this.origin, false, this); + if (!trace_startsolid) { + this.crouch = false; + this.view_ofs = STAT(PL_VIEW_OFS, this); + setsize(this, STAT(PL_MIN, this), STAT(PL_MAX, this)); + } + } + + FixPlayermodel(this); + + // LordHavoc: allow firing on move frames (sub-ticrate), this gives better timing on slow servers + //if(frametime) + { + this.items &= ~this.items_added; + + //for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + //{ + //.entity weaponentity = weaponentities[slot]; + //W_WeaponFrame(this, weaponentity); + //} + .entity weaponentity = weaponentities[0]; // TODO + W_WeaponFrame(this, weaponentity); + + this.items_added = 0; + if (this.items & ITEM_Jetpack.m_itemid && (this.items & ITEM_JetpackRegen.m_itemid || this.ammo_fuel >= 0.01)) + this.items_added |= IT_FUEL; + + this.items |= this.items_added; + } + + player_regen(this); + + // WEAPONTODO: Add a weapon request for this + // rot vortex charge to the charge limit + if (WEP_CVAR(vortex, charge_rot_rate) && this.vortex_charge > WEP_CVAR(vortex, charge_limit) && this.vortex_charge_rottime < time) + this.vortex_charge = bound(WEP_CVAR(vortex, charge_limit), this.vortex_charge - WEP_CVAR(vortex, charge_rot_rate) * frametime / W_TICSPERFRAME, 1); + + if (frametime) player_anim(this); + + // secret status + secrets_setstatus(this); + + // monsters status + monsters_setstatus(this); + + this.dmg_team = max(0, this.dmg_team - autocvar_g_teamdamage_resetspeed * frametime); + } + else if (gameover) { + if (intermission_running) IntermissionThink(this); + return; + } + else if (IS_OBSERVER(this)) { + ObserverThink(this); + } + else if (IS_SPEC(this)) { + SpectatorThink(this); + } + + // WEAPONTODO: Add weapon request for this + if (!zoomstate_set) { + SetZoomState(this, + PHYS_INPUT_BUTTON_ZOOM(this) || PHYS_INPUT_BUTTON_ZOOMSCRIPT(this) + || (PHYS_INPUT_BUTTON_ATCK2(this) && PS(this).m_weapon == WEP_VORTEX) + || (PHYS_INPUT_BUTTON_ATCK2(this) && PS(this).m_weapon == WEP_RIFLE && WEP_CVAR(rifle, secondary) == 0) + ); + } + + if (this.teamkill_soundtime && time > this.teamkill_soundtime) + { + this.teamkill_soundtime = 0; + + entity e = this.teamkill_soundsource; + entity oldpusher = e.pusher; + e.pusher = this; + PlayerSound(e, playersound_teamshoot, CH_VOICE, VOL_BASEVOICE, VOICETYPE_LASTATTACKER_ONLY); + e.pusher = oldpusher; + } + + if (this.taunt_soundtime && time > this.taunt_soundtime) { + this.taunt_soundtime = 0; + PlayerSound(this, playersound_taunt, CH_VOICE, VOL_BASEVOICE, VOICETYPE_AUTOTAUNT); + } + + target_voicescript_next(this); + + // WEAPONTODO: Move into weaponsystem somehow + // if a player goes unarmed after holding a loaded weapon, empty his clip size and remove the crosshair ammo ring + if (PS(this).m_weapon == WEP_Null) + this.clip_load = this.clip_size = 0; +} + +void DrownPlayer(entity this) +{ + if(IS_DEAD(this)) + return; + + if (this.waterlevel != WATERLEVEL_SUBMERGED || this.vehicle) + { + if(this.air_finished < time) + PlayerSound(this, playersound_gasp, CH_PLAYER, VOL_BASE, VOICETYPE_PLAYERSOUND); + this.air_finished = time + autocvar_g_balance_contents_drowndelay; + this.dmg = 2; + } + else if (this.air_finished < time) + { // drown! + if (this.pain_finished < time) + { + Damage (this, NULL, NULL, autocvar_g_balance_contents_playerdamage_drowning * autocvar_g_balance_contents_damagerate, DEATH_DROWN.m_id, this.origin, '0 0 0'); + this.pain_finished = time + 0.5; + } + } +} + +.bool move_qcphysics; + +void Player_Physics(entity this) +{ + set_movetype(this, ((this.move_qcphysics) ? MOVETYPE_NONE : this.move_movetype)); + + if(!this.move_qcphysics) + return; + + int mt = this.move_movetype; + + if(mt == MOVETYPE_PUSH || mt == MOVETYPE_FAKEPUSH || mt == MOVETYPE_PHYSICS) + { + this.move_qcphysics = false; + set_movetype(this, mt); + return; + } + + if(!frametime && !this.pm_frametime) + return; + + Movetype_Physics_NoMatchTicrate(this, this.pm_frametime, true); + + this.pm_frametime = 0; +} + +/* +============= +PlayerPostThink + +Called every frame for each client after the physics are run +============= +*/ +.float idlekick_lasttimeleft; +void PlayerPostThink (entity this) +{ + Player_Physics(this); + + if (sv_maxidle > 0) + if (frametime) // WORKAROUND: only use dropclient in server frames (frametime set). Never use it in cl_movement frames (frametime zero). + if (IS_REAL_CLIENT(this)) + if (IS_PLAYER(this) || sv_maxidle_spectatorsareidle) + { + int totalClients = 0; + if(sv_maxidle_slots > 0) + { + FOREACH_CLIENT(IS_REAL_CLIENT(it) || sv_maxidle_slots_countbots, + { + ++totalClients; + }); + } + + if (sv_maxidle_slots > 0 && (maxclients - totalClients) > sv_maxidle_slots) + { /* do nothing */ } + else if (time - this.parm_idlesince < 1) // instead of (time == this.parm_idlesince) to support sv_maxidle <= 10 + { + if (this.idlekick_lasttimeleft) + { + this.idlekick_lasttimeleft = 0; + Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_IDLING); + } + } + else + { + float timeleft = ceil(sv_maxidle - (time - this.parm_idlesince)); + if (timeleft == min(10, sv_maxidle - 1)) { // - 1 to support sv_maxidle <= 10 + if (!this.idlekick_lasttimeleft) + Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_DISCONNECT_IDLING, timeleft); + } + if (timeleft <= 0) { + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_KICK_IDLING, this.netname); + dropclient(this); + return; + } + else if (timeleft <= 10) { + if (timeleft != this.idlekick_lasttimeleft) { + Send_Notification(NOTIF_ONE, this, MSG_ANNCE, Announcer_PickNumber(CNT_IDLE, timeleft)); + } + this.idlekick_lasttimeleft = timeleft; + } + } + } + + CheatFrame(this); + + //CheckPlayerJump(); + + if (IS_PLAYER(this)) { + DrownPlayer(this); + CheckRules_Player(this); + UpdateChatBubble(this); + if (this.impulse) ImpulseCommands(this); + if (intermission_running) return; // intermission or finale + GetPressedKeys(this); + } + + if (this.waypointsprite_attachedforcarrier) { + vector v = healtharmor_maxdamage(this.health, this.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id); + WaypointSprite_UpdateHealth(this.waypointsprite_attachedforcarrier, '1 0 0' * v); + } + + playerdemo_write(this); + + CSQCMODEL_AUTOUPDATE(this); +} diff --git a/qcsrc/server/client.qh b/qcsrc/server/client.qh new file mode 100644 index 0000000000..35ff6e961c --- /dev/null +++ b/qcsrc/server/client.qh @@ -0,0 +1,109 @@ +#pragma once + +void ClientState_attach(entity this); + +CLASS(Client, Object) + /** Client name */ + ATTRIB(Client, netname, string, this.netname); + ATTRIB(Client, colormap, int, this.colormap); + ATTRIB(Client, team, int, this.team); + ATTRIB(Client, clientcolors, int, this.clientcolors); + /** Client IP */ + ATTRIB(Client, netaddress, string, this.netaddress); + ATTRIB(Client, playermodel, string, this.playermodel); + ATTRIB(Client, playerskin, int, this.playerskin); + + /** fingerprint of CA key the player used to authenticate */ + ATTRIB(Client, crypto_keyfp, string, this.crypto_keyfp); + /** fingerprint of CA key the server used to authenticate to the player */ + ATTRIB(Client, crypto_mykeyfp, string, this.crypto_mykeyfp); + /** fingerprint of ID used by the player entity, or string_null if not identified */ + ATTRIB(Client, crypto_idfp, string, this.crypto_idfp); + /** set if the player's ID has been signed */ + ATTRIB(Client, crypto_idfp_signed, bool, this.crypto_idfp_signed); + /** the string "AES128" if encrypting, and string_null if plaintext */ + ATTRIB(Client, crypto_encryptmethod, string, this.crypto_encryptmethod); + /** the string "HMAC-SHA256" if signing, and string_null if plaintext */ + ATTRIB(Client, crypto_signmethod, string, this.crypto_signmethod); + + // custom + + ATTRIB(Client, playerid, int, this.playerid); + + METHOD(Client, m_unwind, bool(Client this)); + + STATIC_METHOD(Client, Add, void(Client this, int _team)); + STATIC_METHOD(Client, Remove, void(Client this)); + + INIT(Client) { + if (this.m_unwind(this)) return this; + make_impure(this); + this.classname = "player_joining"; + static int playerid_last; + this.playerid = ++playerid_last; + ClientState_attach(this); + } + DESTRUCTOR(Client) { + Client_Remove(this); + } + CONSTRUCTOR(Client, string name) { + CONSTRUCT(Client); + this.netname = name; + this.netaddress = "local"; + this.playermodel = "models/player/megaerebus.iqm"; + } +ENDCLASS(Client) + +CLASS(Observer, Client) + INIT(Observer) { + this.classname = STR_OBSERVER; + } + DESTRUCTOR(Observer) { } +ENDCLASS(Observer) + +CLASS(Spectator, Client) + INIT(Spectator) { + this.classname = STR_SPECTATOR; + } + DESTRUCTOR(Spectator) { } +ENDCLASS(Spectator) + +CLASS(Player, Client) + INIT(Player) { + this.classname = STR_PLAYER; + } + DESTRUCTOR(Player) { } +ENDCLASS(Player) + +METHOD(Client, m_unwind, bool(Client this)) +{ + TC(Client, this); + #define UNWIND(class) MACRO_BEGIN if (this.instanceOf##class) { METHOD_REFERENCE(class, dtorimpl)(this); } MACRO_END + switch (this.classname) { + case "Observer": + UNWIND(Spectator); + UNWIND(Player); + return true; + case "Spectator": + UNWIND(Observer); + UNWIND(Player); + return true; + case "Player": + UNWIND(Observer); + UNWIND(Spectator); + return true; + } + #undef UNWIND + return false; +} + +float c1, c2, c3, c4; + +void play_countdown(entity this, float finished, Sound samp); + +float CalcRotRegen(float current, float regenstable, float regenfactor, float regenlinear, float regenframetime, float rotstable, float rotfactor, float rotlinear, float rotframetime, float limit); + +bool Spectate(entity this, entity pl); + +#define SPECTATE_COPY() [[accumulate]] void SpectateCopy(entity this, entity spectatee) +#define SPECTATE_COPYFIELD(fld) SPECTATE_COPY() { this.(fld) = spectatee.(fld); } diff --git a/qcsrc/server/command/_mod.inc b/qcsrc/server/command/_mod.inc index fa15432311..2cabd69c7f 100644 --- a/qcsrc/server/command/_mod.inc +++ b/qcsrc/server/command/_mod.inc @@ -1,5 +1,4 @@ // generated file; do not modify -#include #include #include #ifdef SVQC @@ -8,4 +7,5 @@ #include #include #include +#include #include diff --git a/qcsrc/server/command/_mod.qh b/qcsrc/server/command/_mod.qh index 60e34ffe52..6a3b175a4b 100644 --- a/qcsrc/server/command/_mod.qh +++ b/qcsrc/server/command/_mod.qh @@ -1,8 +1,11 @@ // generated file; do not modify -#include #include #include +#ifdef SVQC + #include +#endif #include #include #include +#include #include diff --git a/qcsrc/server/command/all.qc b/qcsrc/server/command/all.qc deleted file mode 100644 index bc15eeb689..0000000000 --- a/qcsrc/server/command/all.qc +++ /dev/null @@ -1,2 +0,0 @@ -#include "all.qh" -#include diff --git a/qcsrc/server/command/all.qh b/qcsrc/server/command/all.qh deleted file mode 100644 index cde5ef367a..0000000000 --- a/qcsrc/server/command/all.qh +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include -REGISTRY(SERVER_COMMANDS, BITS(7)) -#define SERVER_COMMANDS_from(i) _SERVER_COMMANDS_from(i, NULL) -REGISTER_REGISTRY(SERVER_COMMANDS) -REGISTRY_SORT(SERVER_COMMANDS) - -#define SERVER_COMMAND(id, description) \ - CLASS(servercommand_##id, Command) \ - ATTRIB(servercommand_##id, m_name, string, #id); \ - ATTRIB(servercommand_##id, m_description, string, description); \ - ENDCLASS(servercommand_##id) \ - REGISTER(SERVER_COMMANDS, CMD_SV, id, m_id, NEW(servercommand_##id)); \ - METHOD(servercommand_##id, m_invokecmd, void(servercommand_##id this, int request, entity caller, int arguments, string command)) - -STATIC_INIT(SERVER_COMMANDS_aliases) { - FOREACH(SERVER_COMMANDS, true, LAMBDA(localcmd(sprintf("alias %1$s \"%2$s %1$s ${* ?}\"\n", it.m_name, "qc_cmd_sv")))); -} - -#include "sv_cmd.qh" - -#include "banning.qh" -#include "cmd.qh" -#include "common.qh" -#include "getreplies.qh" -#include "radarmap.qh" -#include "vote.qh" diff --git a/qcsrc/server/command/banning.qc b/qcsrc/server/command/banning.qc index d6968c262d..8a35bec292 100644 --- a/qcsrc/server/command/banning.qc +++ b/qcsrc/server/command/banning.qc @@ -1,10 +1,10 @@ #include "banning.qh" -#include +#include #include "banning.qh" #include "common.qh" -#include "../cl_player.qh" +#include "../player.qh" #include "../ipban.qh" #include diff --git a/qcsrc/server/command/cmd.qc b/qcsrc/server/command/cmd.qc index 13c883f36b..e23f9bc989 100644 --- a/qcsrc/server/command/cmd.qc +++ b/qcsrc/server/command/cmd.qc @@ -1,18 +1,18 @@ #include "cmd.qh" -#include +#include #include "common.qh" #include "vote.qh" #include "../campaign.qh" #include "../cheats.qh" -#include "../cl_player.qh" +#include "../player.qh" #include "../ipban.qh" #include "../mapvoting.qh" #include "../scores.qh" #include "../teamplay.qh" -#include "../mutators/all.qh" +#include "../mutators/_mod.qh" #ifdef SVQC #include diff --git a/qcsrc/server/command/common.qc b/qcsrc/server/command/common.qc index d980a0f4cf..357a33eba2 100644 --- a/qcsrc/server/command/common.qc +++ b/qcsrc/server/command/common.qc @@ -1,5 +1,5 @@ #include "common.qh" -#include +#include #include "common.qh" #include "../scores.qh" diff --git a/qcsrc/server/command/common.qh b/qcsrc/server/command/common.qh index 63b1c708f2..7fbbddf7d7 100644 --- a/qcsrc/server/command/common.qh +++ b/qcsrc/server/command/common.qh @@ -1,6 +1,6 @@ #pragma once -#include +#include REGISTRY(COMMON_COMMANDS, BITS(7)) #define COMMON_COMMANDS_from(i) _COMMON_COMMANDS_from(i, NULL) REGISTER_REGISTRY(COMMON_COMMANDS) @@ -21,8 +21,7 @@ STATIC_INIT(COMMON_COMMANDS_aliases) { #include "vote.qh" #include -#include -#include +#include // ============================================================ // Shared declarations for server commands, written by Samual diff --git a/qcsrc/server/command/getreplies.qc b/qcsrc/server/command/getreplies.qc index 93ce85a61a..2598724717 100644 --- a/qcsrc/server/command/getreplies.qc +++ b/qcsrc/server/command/getreplies.qc @@ -1,5 +1,5 @@ #include "getreplies.qh" -#include +#include #include "getreplies.qh" #include "../race.qh" diff --git a/qcsrc/server/command/radarmap.qc b/qcsrc/server/command/radarmap.qc index 71f5e270b1..acf2d40aee 100644 --- a/qcsrc/server/command/radarmap.qc +++ b/qcsrc/server/command/radarmap.qc @@ -1,5 +1,5 @@ #include "radarmap.qh" -#include +#include #include "radarmap.qh" #include "../g_world.qh" diff --git a/qcsrc/server/command/reg.qc b/qcsrc/server/command/reg.qc new file mode 100644 index 0000000000..c0af5b5e09 --- /dev/null +++ b/qcsrc/server/command/reg.qc @@ -0,0 +1 @@ +#include "reg.qh" diff --git a/qcsrc/server/command/reg.qh b/qcsrc/server/command/reg.qh new file mode 100644 index 0000000000..b135c04601 --- /dev/null +++ b/qcsrc/server/command/reg.qh @@ -0,0 +1,18 @@ +#pragma once + +REGISTRY(SERVER_COMMANDS, BITS(7)) +#define SERVER_COMMANDS_from(i) _SERVER_COMMANDS_from(i, NULL) +REGISTER_REGISTRY(SERVER_COMMANDS) +REGISTRY_SORT(SERVER_COMMANDS) + +#define SERVER_COMMAND(id, description) \ + CLASS(servercommand_##id, Command) \ + ATTRIB(servercommand_##id, m_name, string, #id); \ + ATTRIB(servercommand_##id, m_description, string, description); \ + ENDCLASS(servercommand_##id) \ + REGISTER(SERVER_COMMANDS, CMD_SV, id, m_id, NEW(servercommand_##id)); \ + METHOD(servercommand_##id, m_invokecmd, void(servercommand_##id this, int request, entity caller, int arguments, string command)) + +STATIC_INIT(SERVER_COMMANDS_aliases) { + FOREACH(SERVER_COMMANDS, true, LAMBDA(localcmd(sprintf("alias %1$s \"%2$s %1$s ${* ?}\"\n", it.m_name, "qc_cmd_sv")))); +} diff --git a/qcsrc/server/command/sv_cmd.qc b/qcsrc/server/command/sv_cmd.qc index bb59566d2a..a329ac9965 100644 --- a/qcsrc/server/command/sv_cmd.qc +++ b/qcsrc/server/command/sv_cmd.qc @@ -1,5 +1,5 @@ #include "sv_cmd.qh" -#include "all.qh" +#include "_mod.qh" #include "banning.qh" #include "cmd.qh" @@ -9,8 +9,8 @@ #include "../anticheat.qh" #include "../campaign.qh" -#include "../cl_client.qh" -#include "../cl_player.qh" +#include "../client.qh" +#include "../player.qh" #include "../g_world.qh" #include "../ipban.qh" #include "../playerdemo.qh" @@ -18,7 +18,7 @@ #include "../bot/api.qh" -#include "../mutators/all.qh" +#include "../mutators/_mod.qh" #include #include diff --git a/qcsrc/server/command/vote.qc b/qcsrc/server/command/vote.qc index 72bedd982c..361f128443 100644 --- a/qcsrc/server/command/vote.qc +++ b/qcsrc/server/command/vote.qc @@ -1,5 +1,5 @@ #include "vote.qh" -#include +#include #include "vote.qh" #include "common.qh" @@ -10,7 +10,7 @@ #include "../round_handler.qh" #include "../scores.qh" -#include "../mutators/all.qh" +#include "../mutators/_mod.qh" #include #include diff --git a/qcsrc/server/defs.qh b/qcsrc/server/defs.qh index 53aa317e68..a6bd1552d2 100644 --- a/qcsrc/server/defs.qh +++ b/qcsrc/server/defs.qh @@ -1,7 +1,7 @@ #pragma once float warmup_limit; -#include +#include #include #define INDEPENDENT_ATTACK_FINISHED 1 @@ -12,20 +12,6 @@ float g_footsteps, g_grappling_hook, g_instagib; float g_warmup_allguns; float g_warmup_allow_timeout; float warmup_stage; -PROPERTY(float, g_pickup_respawntime_weapon) -PROPERTY(float, g_pickup_respawntime_superweapon) -PROPERTY(float, g_pickup_respawntime_ammo) -PROPERTY(float, g_pickup_respawntime_short) -PROPERTY(float, g_pickup_respawntime_medium) -PROPERTY(float, g_pickup_respawntime_long) -PROPERTY(float, g_pickup_respawntime_powerup) -PROPERTY(float, g_pickup_respawntimejitter_weapon) -PROPERTY(float, g_pickup_respawntimejitter_superweapon) -PROPERTY(float, g_pickup_respawntimejitter_ammo) -PROPERTY(float, g_pickup_respawntimejitter_short) -PROPERTY(float, g_pickup_respawntimejitter_medium) -PROPERTY(float, g_pickup_respawntimejitter_long) -PROPERTY(float, g_pickup_respawntimejitter_powerup) float g_jetpack; float sv_clones; @@ -430,7 +416,6 @@ const int MIF_GUIDED_CONFUSABLE = MIF_GUIDED_HEAT | MIF_GUIDED_AI; #define MISSILE_IS_GUIDED(m) ((m.missile_flags & MIF_GUIDED_ALL) ? true : false) #define MISSILE_IS_TRACKING(m) ((m.missile_flags & MIF_GUIDED_TRACKING) ? true : false) - //// .entity player_stats; diff --git a/qcsrc/server/g_damage.qc b/qcsrc/server/g_damage.qc index d7e9b07c05..e3cada6c84 100644 --- a/qcsrc/server/g_damage.qc +++ b/qcsrc/server/g_damage.qc @@ -2,14 +2,14 @@ #include "bot/api.qh" #include "g_hook.qh" -#include "mutators/all.qh" +#include "mutators/_mod.qh" #include "scores.qh" #include "spawnpoints.qh" #include "../common/state.qh" #include "../common/physics/player.qh" #include "../common/t_items.qh" #include "../common/vehicles/all.qh" -#include "../common/items/all.qc" +#include "../common/items/_mod.qh" #include "../common/mutators/mutator/waypoints/waypointsprites.qh" #include "weapons/accuracy.qh" #include "weapons/csqcprojectile.qh" @@ -21,7 +21,7 @@ #include "../common/playerstats.qh" #include "../common/teams.qh" #include "../common/util.qh" -#include "../common/weapons/all.qh" +#include #include "../lib/csqcmodel/sv_model.qh" #include "../lib/warpzone/common.qh" @@ -111,6 +111,8 @@ void GiveFrags (entity attacker, entity targ, float f, int deathtype) UpdateFrags(attacker, f); } +.entity kh_next; + string AppendItemcodes(string s, entity player) { int w = PS(player).m_weapon.m_id; @@ -788,7 +790,7 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, int d else victim = targ; - if(IS_PLAYER(victim) || (IS_TURRET(victim) && victim.active == ACTIVE_ACTIVE) || IS_MONSTER(victim) || MUTATOR_CALLHOOK(PlayHitsound, victim)) + if(IS_PLAYER(victim) || (IS_TURRET(victim) && victim.active == ACTIVE_ACTIVE) || IS_MONSTER(victim) || MUTATOR_CALLHOOK(PlayHitsound, victim, attacker)) { if(DIFF_TEAM(victim, attacker) && !STAT(FROZEN, victim)) { diff --git a/qcsrc/server/g_damage.qh b/qcsrc/server/g_damage.qh index f11c82c84d..019c8fc9a8 100644 --- a/qcsrc/server/g_damage.qh +++ b/qcsrc/server/g_damage.qh @@ -7,7 +7,7 @@ #include #include #include - #include + #include #include "weapons/accuracy.qh" #include "weapons/csqcprojectile.qh" #include "weapons/selection.qh" @@ -17,7 +17,7 @@ #include "defs.qh" #include #include - #include "mutators/all.qh" + #include "mutators/_mod.qh" #include #include #include diff --git a/qcsrc/server/g_hook.qc b/qcsrc/server/g_hook.qc index 1b2662d6e4..46508a9191 100644 --- a/qcsrc/server/g_hook.qc +++ b/qcsrc/server/g_hook.qc @@ -5,7 +5,7 @@ #include "weapons/weaponsystem.qh" #include "weapons/selection.qh" #include "weapons/tracing.qh" -#include "cl_player.qh" +#include "player.qh" #include "command/common.qh" #include "round_handler.qh" #include "../common/state.qh" @@ -13,7 +13,7 @@ #include "../common/vehicles/all.qh" #include "../common/constants.qh" #include "../common/util.qh" -#include "../common/weapons/all.qh" +#include #include "../lib/warpzone/common.qh" #include "../lib/warpzone/server.qh" diff --git a/qcsrc/server/g_world.qc b/qcsrc/server/g_world.qc index 27fe5c1ef3..5b63aa4e0c 100644 --- a/qcsrc/server/g_world.qc +++ b/qcsrc/server/g_world.qc @@ -5,7 +5,7 @@ #include "bot/api.qh" #include "campaign.qh" #include "cheats.qh" -#include "cl_client.qh" +#include "client.qh" #include "command/common.qh" #include "command/getreplies.qh" #include "command/sv_cmd.qh" @@ -13,7 +13,7 @@ #include "g_hook.qh" #include "ipban.qh" #include "mapvoting.qh" -#include "mutators/all.qh" +#include "mutators/_mod.qh" #include "race.qh" #include "scores.qh" #include "teamplay.qh" @@ -32,8 +32,8 @@ #include "../common/triggers/trigger/secret.qh" #include "../common/triggers/target/music.qh" #include "../common/util.qh" -#include "../common/items/all.qh" -#include "../common/weapons/all.qh" +#include "../common/items/_mod.qh" +#include #include "../common/state.qh" const float LATENCY_THINKRATE = 10; @@ -1980,6 +1980,34 @@ string GotoMap(string m) return "Map switch will happen after scoreboard."; } +bool autocvar_sv_gameplayfix_multiplethinksperframe; +void RunThink(entity this) +{ + // don't let things stay in the past. + // it is possible to start that way by a trigger with a local time. + if(this.nextthink <= 0 || this.nextthink > time + frametime) + return; + + float oldtime = time; // do we need to save this? + + for (int iterations = 0; iterations < 128 && !wasfreed(this); iterations++) + { + time = max(oldtime, this.nextthink); + this.nextthink = 0; + + if(getthink(this)) + getthink(this)(this); + // mods often set nextthink to time to cause a think every frame, + // we don't want to loop in that case, so exit if the new nextthink is + // <= the time the qc was told, also exit if it is past the end of the + // frame + if(this.nextthink <= time || this.nextthink > oldtime + frametime || !autocvar_sv_gameplayfix_multiplethinksperframe) + break; + } + + time = oldtime; +} + bool autocvar_sv_freezenonclients; bool autocvar_sv_gameplayfix_delayprojectiles; void Physics_Frame() @@ -2008,6 +2036,13 @@ void Physics_Frame() if(it.move_qcphysics) Movetype_Physics_NoMatchTicrate(it, PHYS_INPUT_TIMELENGTH, false); + + if(it.movetype >= MOVETYPE_USER_FIRST && it.movetype <= MOVETYPE_USER_LAST) // these cases have no think handling + { + // handle thinking here + if (getthink(it) && it.nextthink > 0 && it.nextthink <= time + frametime) + RunThink(it); + } }); if(autocvar_sv_gameplayfix_delayprojectiles >= 0) diff --git a/qcsrc/server/impulse.qc b/qcsrc/server/impulse.qc new file mode 100644 index 0000000000..4660a433ba --- /dev/null +++ b/qcsrc/server/impulse.qc @@ -0,0 +1,606 @@ +#include "impulse.qh" +#include "round_handler.qh" + +#include "bot/api.qh" + +#include "weapons/throwing.qh" +#include "command/common.qh" +#include "cheats.qh" +#include "weapons/selection.qh" +#include "weapons/tracing.qh" +#include "weapons/weaponsystem.qh" + +#include + +#include "../common/minigames/sv_minigames.qh" + +#include +#include "../common/vehicles/sv_vehicles.qh" + +#include "../common/mutators/mutator/waypoints/waypointsprites.qh" + +.entity vehicle; + +#define IMPULSE(id) _IMPULSE(IMP_##id) +#define _IMPULSE(id) \ + void id##_handle(entity this); \ + STATIC_INIT_LATE(id) \ + { \ + id.impulse_handle = id##_handle; \ + } \ + void id##_handle(entity this) + +/** + * Impulse map: + * + * 0 reserved (no input) + * + * 99: loaded + * + * 140: moving clone + * 141: ctf speedrun + * 142: fixed clone + * 143: emergency teleport + * 148: unfairly eliminate + * + * TODO: + * 200 to 209: prev weapon shortcuts + * 210 to 219: best weapon shortcuts + * 220 to 229: next weapon shortcuts + * 230 to 253: individual weapons (up to 24) + */ + +// weapon switching impulses + +#define X(slot) \ + IMPULSE(weapon_group_##slot) \ + { \ + if (IS_DEAD(this)) \ + { \ + this.impulse = IMP_weapon_group_##slot.impulse; \ + return; \ + } \ + W_NextWeaponOnImpulse(this, slot); \ + } +X(1) +X(2) +X(3) +X(4) +X(5) +X(6) +X(7) +X(8) +X(9) +X(0) +#undef X + +// custom order weapon cycling + +#define X(slot, dir) \ + IMPULSE(weapon_priority_##slot##_##dir) \ + { \ + if (this.vehicle) return; \ + if (IS_DEAD(this)) \ + { \ + this.impulse = IMP_weapon_priority_##slot##_##dir.impulse; \ + return; \ + } \ + noref int prev = -1; \ + noref int best = 0; \ + noref int next = +1; \ + W_CycleWeapon(this, this.cvar_cl_weaponpriorities[slot], dir); \ + } +X(0, prev) +X(1, prev) +X(2, prev) +X(3, prev) +X(4, prev) +X(5, prev) +X(6, prev) +X(7, prev) +X(8, prev) +X(9, prev) + +X(0, best) +X(1, best) +X(2, best) +X(3, best) +X(4, best) +X(5, best) +X(6, best) +X(7, best) +X(8, best) +X(9, best) + +X(0, next) +X(1, next) +X(2, next) +X(3, next) +X(4, next) +X(5, next) +X(6, next) +X(7, next) +X(8, next) +X(9, next) +#undef X + +// direct weapons + +#define X(i) \ + IMPULSE(weapon_byid_##i) \ + { \ + if (this.vehicle) return; \ + if (IS_DEAD(this)) \ + { \ + this.impulse = IMP_weapon_byid_##i.impulse; \ + return; \ + } \ + W_SwitchWeapon(this, Weapons_from(WEP_FIRST + i)); \ + } +X(0) +X(1) +X(2) +X(3) +X(4) +X(5) +X(6) +X(7) +X(8) +X(9) +X(10) +X(11) +X(12) +X(13) +X(14) +X(15) +X(16) +X(17) +X(18) +X(19) +X(20) +X(21) +X(22) +X(23) +#undef X + +IMPULSE(weapon_next_byid) +{ + if (this.vehicle) return; + if (IS_DEAD(this)) + { + this.impulse = IMP_weapon_next_byid.impulse; + return; + } + W_NextWeapon(this, 0); +} + +IMPULSE(weapon_prev_byid) +{ + if (this.vehicle) return; + if (IS_DEAD(this)) + { + this.impulse = IMP_weapon_prev_byid.impulse; + return; + } + W_PreviousWeapon(this, 0); +} + +IMPULSE(weapon_next_bygroup) +{ + if (this.vehicle) return; + if (IS_DEAD(this)) + { + this.impulse = IMP_weapon_next_bygroup.impulse; + return; + } + W_NextWeapon(this, 1); +} + +IMPULSE(weapon_prev_bygroup) +{ + if (this.vehicle) return; + if (IS_DEAD(this)) + { + this.impulse = IMP_weapon_prev_bygroup.impulse; + return; + } + W_PreviousWeapon(this, 1); +} + +IMPULSE(weapon_next_bypriority) +{ + if (this.vehicle) return; + if (IS_DEAD(this)) + { + this.impulse = IMP_weapon_next_bypriority.impulse; + return; + } + W_NextWeapon(this, 2); +} + +IMPULSE(weapon_prev_bypriority) +{ + if (this.vehicle) return; + if (IS_DEAD(this)) + { + this.impulse = IMP_weapon_prev_bypriority.impulse; + return; + } + W_PreviousWeapon(this, 2); +} + +IMPULSE(weapon_last) +{ + if (this.vehicle) return; + if (IS_DEAD(this)) return; + W_LastWeapon(this); +} + +IMPULSE(weapon_best) +{ + if (this.vehicle) return; + if (IS_DEAD(this)) return; + W_SwitchWeapon(this, w_getbestweapon(this)); +} + +IMPULSE(weapon_drop) +{ + if (this.vehicle) return; + if (IS_DEAD(this)) return; + W_ThrowWeapon(this, weaponentities[0], W_CalculateProjectileVelocity(this, this.velocity, v_forward * 750, false), '0 0 0', true); +} + +IMPULSE(weapon_reload) +{ + if (this.vehicle) return; + if (IS_DEAD(this)) return; + if (forbidWeaponUse(this)) return; + Weapon w = PS(this).m_weapon; + entity actor = this; + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + .entity weaponentity = weaponentities[slot]; + w.wr_reload(w, actor, weaponentity); + } +} + +void ImpulseCommands(entity this) +{ + if (gameover) return; + + int imp = this.impulse; + if (!imp) return; + this.impulse = 0; + + if (MinigameImpulse(this, imp)) return; + + if (timeout_status == TIMEOUT_ACTIVE) return; // don't allow any impulses while the game is paused + + // allow only weapon change impulses when not in round time + if (round_handler_IsActive() && !round_handler_IsRoundStarted()) + { + #define X(id) case IMP_##id.impulse: + switch (imp) + { + X(weapon_group_0) + X(weapon_group_1) + X(weapon_group_2) + X(weapon_group_3) + X(weapon_group_4) + X(weapon_group_5) + X(weapon_group_6) + X(weapon_group_7) + X(weapon_group_8) + X(weapon_group_9) + X(weapon_next_byid) + X(weapon_prev_byid) + X(weapon_next_bygroup) + X(weapon_prev_bygroup) + X(weapon_next_bypriority) + X(weapon_prev_bypriority) + X(weapon_last) + X(weapon_best) + X(weapon_reload) + X(weapon_priority_0_prev) + X(weapon_priority_1_prev) + X(weapon_priority_2_prev) + X(weapon_priority_3_prev) + X(weapon_priority_4_prev) + X(weapon_priority_5_prev) + X(weapon_priority_6_prev) + X(weapon_priority_7_prev) + X(weapon_priority_8_prev) + X(weapon_priority_9_prev) + X(weapon_priority_0_next) + X(weapon_priority_1_next) + X(weapon_priority_2_next) + X(weapon_priority_3_next) + X(weapon_priority_4_next) + X(weapon_priority_5_next) + X(weapon_priority_6_next) + X(weapon_priority_7_next) + X(weapon_priority_8_next) + X(weapon_priority_9_next) + X(weapon_priority_0_best) + X(weapon_priority_1_best) + X(weapon_priority_2_best) + X(weapon_priority_3_best) + X(weapon_priority_4_best) + X(weapon_priority_5_best) + X(weapon_priority_6_best) + X(weapon_priority_7_best) + X(weapon_priority_8_best) + X(weapon_priority_9_best) + X(weapon_byid_0) + X(weapon_byid_1) + X(weapon_byid_2) + X(weapon_byid_3) + X(weapon_byid_4) + X(weapon_byid_5) + X(weapon_byid_6) + X(weapon_byid_7) + X(weapon_byid_8) + X(weapon_byid_9) + X(weapon_byid_10) + X(weapon_byid_11) + X(weapon_byid_12) + X(weapon_byid_13) + X(weapon_byid_14) + X(weapon_byid_15) + X(weapon_byid_16) + X(weapon_byid_17) + X(weapon_byid_18) + X(weapon_byid_19) + X(weapon_byid_20) + X(weapon_byid_21) + X(weapon_byid_22) + X(weapon_byid_23) + break; + default: return; + } +#undef X + } + + if (vehicle_impulse(this, imp)) return; + + if (CheatImpulse(this, imp)) return; + + FOREACH(IMPULSES, it.impulse == imp, { + void(entity) f = it.impulse_handle; + if (!f) continue; + f(this); + return; + }); +} + +IMPULSE(use) +{ + PlayerUseKey(this); +} + +IMPULSE(waypoint_personal_here) +{ + entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.origin, RADARICON_WAYPOINT); + if (wp) WaypointSprite_Ping(wp); + sprint(this, "personal waypoint spawned at location\n"); +} + +IMPULSE(waypoint_personal_crosshair) +{ + WarpZone_crosshair_trace(this); + entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, trace_endpos, RADARICON_WAYPOINT); + if (wp) WaypointSprite_Ping(wp); + sprint(this, "personal waypoint spawned at crosshair\n"); +} + +IMPULSE(waypoint_personal_death) +{ + if (!this.death_origin) return; + entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.death_origin, RADARICON_WAYPOINT); + if (wp) WaypointSprite_Ping(wp); + sprint(this, "personal waypoint spawned at death location\n"); +} + +IMPULSE(waypoint_here_follow) +{ + if (!teamplay) return; + if (IS_DEAD(this)) return; + if (!MUTATOR_CALLHOOK(HelpMePing, this)) + { + entity wp = WaypointSprite_Attach(WP_Helpme, this, true, RADARICON_HELPME); + if (!wp) WaypointSprite_HelpMePing(this.waypointsprite_attachedforcarrier); + else WaypointSprite_Ping(wp); + } + sprint(this, "HELP ME attached\n"); +} + +IMPULSE(waypoint_here_here) +{ + entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.origin, RADARICON_HERE); + if (wp) WaypointSprite_Ping(wp); + sprint(this, "HERE spawned at location\n"); +} + +IMPULSE(waypoint_here_crosshair) +{ + WarpZone_crosshair_trace(this); + entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, trace_endpos, RADARICON_HERE); + if (wp) WaypointSprite_Ping(wp); + sprint(this, "HERE spawned at crosshair\n"); +} + +IMPULSE(waypoint_here_death) +{ + if (!this.death_origin) return; + entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.death_origin, RADARICON_HERE); + if (wp) WaypointSprite_Ping(wp); + sprint(this, "HERE spawned at death location\n"); +} + +IMPULSE(waypoint_danger_here) +{ + entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.origin, RADARICON_DANGER); + if (wp) WaypointSprite_Ping(wp); + sprint(this, "DANGER spawned at location\n"); +} + +IMPULSE(waypoint_danger_crosshair) +{ + WarpZone_crosshair_trace(this); + entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, trace_endpos, RADARICON_DANGER); + if (wp) WaypointSprite_Ping(wp); + sprint(this, "DANGER spawned at crosshair\n"); +} + +IMPULSE(waypoint_danger_death) +{ + if (!this.death_origin) return; + entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.death_origin, RADARICON_DANGER); + if (wp) WaypointSprite_Ping(wp); + sprint(this, "DANGER spawned at death location\n"); +} + +IMPULSE(waypoint_clear_personal) +{ + WaypointSprite_ClearPersonal(this); + if (this.personal) + { + delete(this.personal); + this.personal = NULL; + } + sprint(this, "personal waypoint cleared\n"); +} + +IMPULSE(waypoint_clear) +{ + WaypointSprite_ClearOwned(this); + if (this.personal) + { + delete(this.personal); + this.personal = NULL; + } + sprint(this, "all waypoints cleared\n"); +} + +IMPULSE(navwaypoint_spawn) +{ + if (!autocvar_g_waypointeditor) return; + waypoint_schedulerelink(waypoint_spawn(this.origin, this.origin, 0)); + bprint(strcat("Waypoint spawned at ", vtos(this.origin), "\n")); +} + +IMPULSE(navwaypoint_remove) +{ + if (!autocvar_g_waypointeditor) return; + entity e = navigation_findnearestwaypoint(this, false); + if (!e) return; + if (e.wpflags & WAYPOINTFLAG_GENERATED) return; + bprint(strcat("Waypoint removed at ", vtos(e.origin), "\n")); + waypoint_remove(e); +} + +IMPULSE(navwaypoint_relink) +{ + if (!autocvar_g_waypointeditor) return; + waypoint_schedulerelinkall(); +} + +IMPULSE(navwaypoint_save) +{ + if (!autocvar_g_waypointeditor) return; + waypoint_saveall(); +} + +IMPULSE(navwaypoint_unreachable) +{ + if (!autocvar_g_waypointeditor) return; + IL_EACH(g_waypoints, true, + { + it.colormod = '0.5 0.5 0.5'; + it.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE); + }); + entity e2 = navigation_findnearestwaypoint(this, false); + navigation_markroutes(this, e2); + + int j, m; + + j = 0; + m = 0; + IL_EACH(g_waypoints, it.wpcost >= 10000000, + { + LOG_INFO("unreachable: ", etos(it), " ", vtos(it.origin), "\n"); + it.colormod_z = 8; + it.effects |= EF_NODEPTHTEST | EF_BLUE; + ++j; + ++m; + }); + if (j) LOG_INFOF("%d waypoints cannot be reached from here in any way (marked with blue light)\n", j); + navigation_markroutes_inverted(e2); + + j = 0; + IL_EACH(g_waypoints, it.wpcost >= 10000000, + { + LOG_INFO("cannot reach me: ", etos(it), " ", vtos(it.origin), "\n"); + it.colormod_x = 8; + if (!(it.effects & EF_NODEPTHTEST)) // not already reported before + ++m; + it.effects |= EF_NODEPTHTEST | EF_RED; + ++j; + }); + if (j) LOG_INFOF("%d waypoints cannot walk to here in any way (marked with red light)\n", j); + if (m) LOG_INFOF("%d waypoints have been marked total\n", m); + + j = 0; + FOREACH_ENTITY_CLASS("info_player_deathmatch", true, + { + vector org = it.origin; + tracebox(it.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - '0 0 512', MOVE_NOMONSTERS, NULL); + setorigin(it, trace_endpos); + if (navigation_findnearestwaypoint(it, false)) + { + setorigin(it, org); + it.effects &= ~EF_NODEPTHTEST; + it.model = ""; + } + else + { + setorigin(it, org); + LOG_INFO("spawn without waypoint: ", etos(it), " ", vtos(it.origin), "\n"); + it.effects |= EF_NODEPTHTEST; + _setmodel(it, this.model); + it.frame = this.frame; + it.skin = this.skin; + it.colormod = '8 0.5 8'; + setsize(it, '0 0 0', '0 0 0'); + ++j; + } + }); + if (j) LOG_INFOF("%d spawnpoints have no nearest waypoint (marked by player model)\n", j); + + j = 0; + FOREACH_ENTITY_FLAGS(flags, FL_ITEM, + { + it.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE); + it.colormod = '0.5 0.5 0.5'; + }); + FOREACH_ENTITY_FLAGS(flags, FL_ITEM, + { + if (navigation_findnearestwaypoint(it, false)) continue; + LOG_INFO("item without waypoint: ", etos(it), " ", vtos(it.origin), "\n"); + it.effects |= EF_NODEPTHTEST | EF_RED; + it.colormod_x = 8; + ++j; + }); + if (j) LOG_INFOF("%d items have no nearest waypoint and cannot be walked away from (marked with red light)\n", j); + + j = 0; + FOREACH_ENTITY_FLAGS(flags, FL_ITEM, + { + if (navigation_findnearestwaypoint(it, true)) continue; + LOG_INFO("item without waypoint: ", etos(it), " ", vtos(it.origin), "\n"); + it.effects |= EF_NODEPTHTEST | EF_BLUE; + it.colormod_z = 8; + ++j; + }); + if (j) LOG_INFOF("%d items have no nearest waypoint and cannot be walked to (marked with blue light)\n", j); +} diff --git a/qcsrc/server/impulse.qh b/qcsrc/server/impulse.qh new file mode 100644 index 0000000000..50edc2c9c5 --- /dev/null +++ b/qcsrc/server/impulse.qh @@ -0,0 +1,3 @@ +#pragma once + +void ImpulseCommands(entity this); diff --git a/qcsrc/server/matrix.qc b/qcsrc/server/matrix.qc index b7d26decf5..4d235da694 100644 --- a/qcsrc/server/matrix.qc +++ b/qcsrc/server/matrix.qc @@ -1,6 +1,6 @@ #include "matrix.qh" -#include "cl_player.qh" +#include "player.qh" var void MX_Handle(int buf, string ancestor) { diff --git a/qcsrc/server/miscfunctions.qc b/qcsrc/server/miscfunctions.qc index fb782ed5c6..58cd949297 100644 --- a/qcsrc/server/miscfunctions.qc +++ b/qcsrc/server/miscfunctions.qc @@ -4,12 +4,12 @@ #include "constants.qh" #include "g_hook.qh" #include "ipban.qh" -#include "mutators/all.qh" +#include "mutators/_mod.qh" #include "../common/t_items.qh" #include "weapons/accuracy.qh" #include "weapons/csqcprojectile.qh" #include "weapons/selection.qh" -#include "../common/command/generic.qh" +#include "../common/command/_mod.qh" #include "../common/constants.qh" #include "../common/deathtypes/all.qh" #include "../common/mapinfo.qh" @@ -19,10 +19,10 @@ #include "../common/triggers/subs.qh" #include "../common/util.qh" #include "../common/turrets/sv_turrets.qh" -#include "../common/weapons/all.qh" +#include #include "../common/vehicles/sv_vehicles.qh" #include "../common/vehicles/vehicle.qh" -#include "../common/items/all.qc" +#include "../common/items/_mod.qh" #include "../common/state.qh" #include "../common/effects/qc/globalsound.qh" #include "../lib/csqcmodel/sv_model.qh" @@ -97,7 +97,7 @@ void GameLogEcho(string s) } if (autocvar_sv_eventlog_console) { - LOG_INFO(s, "\n"); + dedicated_print(strcat(s, "\n")); } } diff --git a/qcsrc/server/mutators/_mod.inc b/qcsrc/server/mutators/_mod.inc index 3d2321896c..f0108dec37 100644 --- a/qcsrc/server/mutators/_mod.inc +++ b/qcsrc/server/mutators/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#include + +#include diff --git a/qcsrc/server/mutators/_mod.qh b/qcsrc/server/mutators/_mod.qh index 8feb1f37d8..9888c94666 100644 --- a/qcsrc/server/mutators/_mod.qh +++ b/qcsrc/server/mutators/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#include + +#include diff --git a/qcsrc/server/mutators/all.inc b/qcsrc/server/mutators/all.inc deleted file mode 100644 index 1a80b44094..0000000000 --- a/qcsrc/server/mutators/all.inc +++ /dev/null @@ -1,13 +0,0 @@ -#include "mutator/gamemode_assault.qc" -#include "mutator/gamemode_ca.qc" -#include "mutator/gamemode_ctf.qc" -#include "mutator/gamemode_cts.qc" -#include "mutator/gamemode_deathmatch.qc" -#include "mutator/gamemode_domination.qc" -#include "mutator/gamemode_freezetag.qc" -#include "mutator/gamemode_invasion.qc" -#include "mutator/gamemode_keepaway.qc" -#include "mutator/gamemode_keyhunt.qc" -#include "mutator/gamemode_lms.qc" -#include "mutator/gamemode_race.qc" -#include "mutator/gamemode_tdm.qc" diff --git a/qcsrc/server/mutators/all.qc b/qcsrc/server/mutators/all.qc deleted file mode 100644 index 78ba560265..0000000000 --- a/qcsrc/server/mutators/all.qc +++ /dev/null @@ -1,88 +0,0 @@ -#include "all.qh" -#if defined(CSQC) -#elif defined(MENUQC) -#elif defined(SVQC) - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include "../weapons/accuracy.qh" - #include "../weapons/common.qh" - #include "../weapons/csqcprojectile.qh" - #include "../weapons/hitplot.qh" - #include "../weapons/selection.qh" - #include "../weapons/spawning.qh" - #include "../weapons/throwing.qh" - #include "../weapons/tracing.qh" - #include "../weapons/weaponstats.qh" - #include "../weapons/weaponsystem.qh" - #include - #include "../autocvars.qh" - #include "../constants.qh" - #include "../defs.qh" - #include - #include - #include "all.qh" - #include - #include - #include "../campaign.qh" - #include - #include - #include "../command/common.qh" - #include "../command/banning.qh" - #include "../command/radarmap.qh" - #include "../command/vote.qh" - #include "../command/getreplies.qh" - #include "../command/cmd.qh" - #include "../command/sv_cmd.qh" - #include - #include - #include - #include "../anticheat.qh" - #include "../cheats.qh" - #include - #include "../portals.qh" - #include "../g_hook.qh" - #include "../scores.qh" - #include "../spawnpoints.qh" - #include "../mapvoting.qh" - #include "../ipban.qh" - #include "../race.qh" - #include "../antilag.qh" - #include "../playerdemo.qh" - #include "../round_handler.qh" - #include "../item_key.qh" - #include "../pathlib/pathlib.qh" - #include -#endif - -#include "all.qh" - -STATIC_INIT_LATE(Gametype) { - Gametype g = MapInfo_CurrentGametype(); - if (g) { - for (string _s = g.m_mutators; _s != ""; _s = cdr(_s)) { - string s = car(_s); - FOREACH(Mutators, it.m_name == s, LAMBDA(Mutator_Add(it); break)); - } - } -} - -#define IMPLEMENTATION -#include "all.inc" -#undef IMPLEMENTATION diff --git a/qcsrc/server/mutators/all.qh b/qcsrc/server/mutators/all.qh deleted file mode 100644 index 4a1f2b3533..0000000000 --- a/qcsrc/server/mutators/all.qh +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include "mutator.qh" -#include "gamemode.qh" - -#include "all.inc" diff --git a/qcsrc/server/mutators/events.qh b/qcsrc/server/mutators/events.qh index 0d53e054f4..ae867e9a3d 100644 --- a/qcsrc/server/mutators/events.qh +++ b/qcsrc/server/mutators/events.qh @@ -58,6 +58,7 @@ MUTATOR_HOOKABLE(PlayerDies, EV_PlayerDies); /** called when a player dies to e.g. remove stuff he was carrying */ #define EV_PlayHitsound(i, o) \ /** victim */ i(entity, MUTATOR_ARGV_0_entity) \ + /** attacker */ i(entity, MUTATOR_ARGV_1_entity) \ /**/ MUTATOR_HOOKABLE(PlayHitsound, EV_PlayHitsound); @@ -271,6 +272,15 @@ MUTATOR_HOOKABLE(MonsterMove, EV_MonsterMove); /** called when a monster looks for another target */ MUTATOR_HOOKABLE(MonsterFindTarget, EV_NO_ARGS); +/** + * called when validating a monster's target + */ +#define EV_MonsterValidTarget(i, o) \ + /** monster */ i(entity, MUTATOR_ARGV_0_entity) \ + /** target */ i(entity, MUTATOR_ARGV_1_entity) \ + /**/ +MUTATOR_HOOKABLE(MonsterValidTarget, EV_MonsterValidTarget); + /** called to change a random monster to a miniboss */ #define EV_MonsterCheckBossFlag(i, o) \ /** monster */ i(entity, MUTATOR_ARGV_0_entity) \ @@ -350,7 +360,7 @@ MUTATOR_HOOKABLE(W_DecreaseAmmo, EV_W_DecreaseAmmo); /**/ MUTATOR_HOOKABLE(W_Reload, EV_W_Reload); -/** called at the end of player_powerups() in cl_client.qc, used for manipulating the values which are set by powerup items. */ +/** called at the end of player_powerups() in client.qc, used for manipulating the values which are set by powerup items. */ #define EV_PlayerPowerups(i, o) \ /** player */ i(entity, MUTATOR_ARGV_0_entity) \ /** old items */ i(int, MUTATOR_ARGV_1_int) \ @@ -499,7 +509,7 @@ MUTATOR_HOOKABLE(BotShouldAttack, EV_BotShouldAttack); MUTATOR_HOOKABLE(PortalTeleport, EV_PortalTeleport); /** - * called whenever a player uses impulse 33 (help me) in cl_impulse.qc + * called whenever a player uses impulse 33 (help me) in impulse.qc * normally help me ping uses .waypointsprite_attachedforcarrier, * but if your mutator uses something different then you can handle it * in a special manner using this hook @@ -837,3 +847,19 @@ MUTATOR_HOOKABLE(Player_ChangeTeam, EV_Player_ChangeTeam); /** data */ i(string, MUTATOR_ARGV_2_string) \ /**/ MUTATOR_HOOKABLE(URI_GetCallback, EV_URI_GetCallback); + +/** + * return true to prevent weapon use for a player + */ + #define EV_ForbidWeaponUse(i, o) \ + /** player */ i(entity, MUTATOR_ARGV_0_entity) \ + /**/ +MUTATOR_HOOKABLE(ForbidWeaponUse, EV_ForbidWeaponUse); + +/** called when a player spawns as player, after shared setup, before his weapon is chosen (so items may be changed in here) */ +#define EV_CopyBody(i, o) \ + /** player */ i(entity, MUTATOR_ARGV_0_entity) \ + /** newly created clone */ i(entity, MUTATOR_ARGV_1_entity) \ + /** keepvelocity? */ i(bool, MUTATOR_ARGV_2_bool) \ + /**/ +MUTATOR_HOOKABLE(CopyBody, EV_CopyBody); diff --git a/qcsrc/server/mutators/gamemode.qh b/qcsrc/server/mutators/gamemode.qh index 1e60b203e3..ba3e48f17c 100644 --- a/qcsrc/server/mutators/gamemode.qh +++ b/qcsrc/server/mutators/gamemode.qh @@ -1,23 +1,87 @@ #pragma once -#include -#include -#include -#include -#include #include #include #include #include #include -#include +#include "mutator.qh" -#include +// TODO: trim +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include - +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include #include #include diff --git a/qcsrc/server/mutators/loader.qc b/qcsrc/server/mutators/loader.qc new file mode 100644 index 0000000000..1784e72ecd --- /dev/null +++ b/qcsrc/server/mutators/loader.qc @@ -0,0 +1,11 @@ +#include "loader.qh" + +STATIC_INIT_LATE(Gametype) { + Gametype g = MapInfo_CurrentGametype(); + if (g) { + for (string _s = g.m_mutators; _s != ""; _s = cdr(_s)) { + string s = car(_s); + FOREACH(Mutators, it.m_name == s, LAMBDA(Mutator_Add(it); break)); + } + } +} diff --git a/qcsrc/server/mutators/loader.qh b/qcsrc/server/mutators/loader.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/server/mutators/loader.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/server/mutators/mutator.qh b/qcsrc/server/mutators/mutator.qh index a63321a36a..9c13e1bb63 100644 --- a/qcsrc/server/mutators/mutator.qh +++ b/qcsrc/server/mutators/mutator.qh @@ -2,9 +2,9 @@ #include -#include -#include -#include +#include +#include +#include #include #include #include @@ -13,8 +13,7 @@ #include -#include -#include +#include #include #include diff --git a/qcsrc/server/mutators/mutator/gamemode_assault.qc b/qcsrc/server/mutators/mutator/gamemode_assault.qc index 092e07b798..5814e7cb74 100644 --- a/qcsrc/server/mutators/mutator/gamemode_assault.qc +++ b/qcsrc/server/mutators/mutator/gamemode_assault.qc @@ -1,70 +1,5 @@ #include "gamemode_assault.qh" -#ifndef GAMEMODE_ASSAULT_H -#define GAMEMODE_ASSAULT_H -void assault_ScoreRules(); -void ActivateTeamplay(); - -REGISTER_MUTATOR(as, false) -{ - ActivateTeamplay(); - have_team_spawns = -1; // request team spawns - - MUTATOR_ONADD - { - if (time > 1) // game loads at time 1 - error("This is a game type and it cannot be added at runtime."); - assault_ScoreRules(); - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // we actually cannot roll back assault_Initialize here - // BUT: we don't need to! If this gets called, adding always - // succeeds. - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This is a game type and it cannot be removed at runtime."); - return -1; - } - - return 0; -} - -// sprites -.entity assault_decreaser; -.entity assault_sprite; - -// legacy bot defs -const int HAVOCBOT_AST_ROLE_NONE = 0; -const int HAVOCBOT_AST_ROLE_DEFENSE = 2; -const int HAVOCBOT_AST_ROLE_OFFENSE = 4; - -.int havocbot_role_flags; -.float havocbot_attack_time; - -.void(entity this) havocbot_role; -.void(entity this) havocbot_previous_role; - -void(entity this) havocbot_role_ast_defense; -void(entity this) havocbot_role_ast_offense; -.entity havocbot_ast_target; - -void(entity bot) havocbot_ast_reset_role; - -void(entity this, float ratingscale, vector org, float sradius) havocbot_goalrating_items; -void(entity this, float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers; - -// scoreboard stuff -const float ST_ASSAULT_OBJECTIVES = 1; - -// predefined spawnfuncs -void target_objective_decrease_activate(entity this); -#endif - -#ifdef IMPLEMENTATION .entity sprite; // random functions @@ -676,5 +611,3 @@ void assault_ScoreRules() ScoreInfo_SetLabel_PlayerScore(SP_ASSAULT_OBJECTIVES, "objectives", SFL_SORT_PRIO_PRIMARY); ScoreRules_basics_end(); } - -#endif diff --git a/qcsrc/server/mutators/mutator/gamemode_assault.qh b/qcsrc/server/mutators/mutator/gamemode_assault.qh index 399830dad2..1d53fcc680 100644 --- a/qcsrc/server/mutators/mutator/gamemode_assault.qh +++ b/qcsrc/server/mutators/mutator/gamemode_assault.qh @@ -1,3 +1,64 @@ #pragma once #include "../gamemode.qh" + +void assault_ScoreRules(); +void ActivateTeamplay(); + +REGISTER_MUTATOR(as, false) +{ + ActivateTeamplay(); + have_team_spawns = -1; // request team spawns + + MUTATOR_ONADD + { + if (time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + assault_ScoreRules(); + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back assault_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This is a game type and it cannot be removed at runtime."); + return -1; + } + + return 0; +} + +// sprites +.entity assault_decreaser; +.entity assault_sprite; + +// legacy bot defs +const int HAVOCBOT_AST_ROLE_NONE = 0; +const int HAVOCBOT_AST_ROLE_DEFENSE = 2; +const int HAVOCBOT_AST_ROLE_OFFENSE = 4; + +.int havocbot_role_flags; +.float havocbot_attack_time; + +.void(entity this) havocbot_role; +.void(entity this) havocbot_previous_role; + +void(entity this) havocbot_role_ast_defense; +void(entity this) havocbot_role_ast_offense; +.entity havocbot_ast_target; + +void(entity bot) havocbot_ast_reset_role; + +void(entity this, float ratingscale, vector org, float sradius) havocbot_goalrating_items; +void(entity this, float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers; + +// scoreboard stuff +const float ST_ASSAULT_OBJECTIVES = 1; + +// predefined spawnfuncs +void target_objective_decrease_activate(entity this); diff --git a/qcsrc/server/mutators/mutator/gamemode_ca.qc b/qcsrc/server/mutators/mutator/gamemode_ca.qc index 209c8b8567..75c9d60084 100644 --- a/qcsrc/server/mutators/mutator/gamemode_ca.qc +++ b/qcsrc/server/mutators/mutator/gamemode_ca.qc @@ -1,79 +1,5 @@ #include "gamemode_ca.qh" -#ifndef GAMEMODE_CA_H -#define GAMEMODE_CA_H -int autocvar_g_ca_point_limit; -int autocvar_g_ca_point_leadlimit; -float autocvar_g_ca_round_timelimit; -bool autocvar_g_ca_team_spawns; -int autocvar_g_ca_teams; -int autocvar_g_ca_teams_override; -float autocvar_g_ca_warmup; - - -int ca_teams; -bool allowed_to_spawn; - -const int ST_CA_ROUNDS = 1; - -bool CA_CheckTeams(); -bool CA_CheckWinner(); -void CA_RoundStart(); -bool ca_isEliminated(entity e); - -void SetLimits(int fraglimit_override, int leadlimit_override, float timelimit_override, float qualifying_override); - -REGISTER_MUTATOR(ca, false) -{ - MUTATOR_ONADD - { - // game loads at time 1 - if (time > 1) error("This is a game type and it cannot be added at runtime."); - - allowed_to_spawn = true; - - ca_teams = autocvar_g_ca_teams_override; - if (ca_teams < 2) ca_teams = autocvar_g_ca_teams; - ca_teams = bound(2, ca_teams, 4); - - int teams = 0; - if(ca_teams >= 1) teams |= BIT(0); - if(ca_teams >= 2) teams |= BIT(1); - if(ca_teams >= 3) teams |= BIT(2); - if(ca_teams >= 4) teams |= BIT(3); - - ca_teams = teams; // now set it? - - ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, true); - ScoreInfo_SetLabel_TeamScore(ST_CA_ROUNDS, "rounds", SFL_SORT_PRIO_PRIMARY); - ScoreRules_basics_end(); - - round_handler_Spawn(CA_CheckTeams, CA_CheckWinner, CA_RoundStart); - round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit); - - EliminatedPlayers_Init(ca_isEliminated); - - ActivateTeamplay(); - SetLimits(autocvar_g_ca_point_limit, autocvar_g_ca_point_leadlimit, autocvar_timelimit_override, -1); - - if (autocvar_g_ca_team_spawns) - have_team_spawns = -1; // request team spawns - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This is a game type and it cannot be removed at runtime."); - return -1; - } - - return 0; -} - -// should be removed in the future, as other code should not have to care -.float caplayer; // 0.5 if scheduled to join the next round -#endif - -#ifdef IMPLEMENTATION float autocvar_g_ca_damage2score_multiplier; bool autocvar_g_ca_spectate_enemies; @@ -531,5 +457,3 @@ MUTATOR_HOOKFUNCTION(ca, SetWeaponArena) // most weapons arena if (M_ARGV(0, string) == "0" || M_ARGV(0, string) == "") M_ARGV(0, string) = "most"; } - -#endif diff --git a/qcsrc/server/mutators/mutator/gamemode_ca.qh b/qcsrc/server/mutators/mutator/gamemode_ca.qh index 399830dad2..8009feb8a5 100644 --- a/qcsrc/server/mutators/mutator/gamemode_ca.qh +++ b/qcsrc/server/mutators/mutator/gamemode_ca.qh @@ -1,3 +1,73 @@ #pragma once #include "../gamemode.qh" + +int autocvar_g_ca_point_limit; +int autocvar_g_ca_point_leadlimit; +float autocvar_g_ca_round_timelimit; +bool autocvar_g_ca_team_spawns; +int autocvar_g_ca_teams; +int autocvar_g_ca_teams_override; +float autocvar_g_ca_warmup; + + +int ca_teams; +bool allowed_to_spawn; + +const int ST_CA_ROUNDS = 1; + +bool CA_CheckTeams(); +bool CA_CheckWinner(); +void CA_RoundStart(); +bool ca_isEliminated(entity e); + +void SetLimits(int fraglimit_override, int leadlimit_override, float timelimit_override, float qualifying_override); + +REGISTER_MUTATOR(ca, false) +{ + MUTATOR_ONADD + { + // game loads at time 1 + if (time > 1) error("This is a game type and it cannot be added at runtime."); + + allowed_to_spawn = true; + + ca_teams = autocvar_g_ca_teams_override; + if (ca_teams < 2) ca_teams = autocvar_g_ca_teams; + ca_teams = bound(2, ca_teams, 4); + + int teams = 0; + if(ca_teams >= 1) teams |= BIT(0); + if(ca_teams >= 2) teams |= BIT(1); + if(ca_teams >= 3) teams |= BIT(2); + if(ca_teams >= 4) teams |= BIT(3); + + ca_teams = teams; // now set it? + + ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, true); + ScoreInfo_SetLabel_TeamScore(ST_CA_ROUNDS, "rounds", SFL_SORT_PRIO_PRIMARY); + ScoreRules_basics_end(); + + round_handler_Spawn(CA_CheckTeams, CA_CheckWinner, CA_RoundStart); + round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit); + + EliminatedPlayers_Init(ca_isEliminated); + + ActivateTeamplay(); + SetLimits(autocvar_g_ca_point_limit, autocvar_g_ca_point_leadlimit, autocvar_timelimit_override, -1); + + if (autocvar_g_ca_team_spawns) + have_team_spawns = -1; // request team spawns + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This is a game type and it cannot be removed at runtime."); + return -1; + } + + return 0; +} + +// should be removed in the future, as other code should not have to care +.float caplayer; // 0.5 if scheduled to join the next round diff --git a/qcsrc/server/mutators/mutator/gamemode_ctf.qc b/qcsrc/server/mutators/mutator/gamemode_ctf.qc index 9b16e4e96d..017fb265b9 100644 --- a/qcsrc/server/mutators/mutator/gamemode_ctf.qc +++ b/qcsrc/server/mutators/mutator/gamemode_ctf.qc @@ -1,6 +1,5 @@ #include "gamemode_ctf.qh" -#ifdef IMPLEMENTATION #ifndef CSQC void ctf_Initialize(); @@ -2658,5 +2657,3 @@ void ctf_Initialize() InitializeEntity(NULL, ctf_DelayedInit, INITPRIO_GAMETYPE); } - -#endif diff --git a/qcsrc/server/mutators/mutator/gamemode_cts.qc b/qcsrc/server/mutators/mutator/gamemode_cts.qc index a712e033df..33030f2f9c 100644 --- a/qcsrc/server/mutators/mutator/gamemode_cts.qc +++ b/qcsrc/server/mutators/mutator/gamemode_cts.qc @@ -1,47 +1,6 @@ #include "gamemode_cts.qh" #include -#ifndef GAMEMODE_CTS_H -#define GAMEMODE_CTS_H - -void cts_Initialize(); - -REGISTER_MUTATOR(cts, false) -{ - MUTATOR_ONADD - { - if (time > 1) // game loads at time 1 - error("This is a game type and it cannot be added at runtime."); - - g_race_qualifying = true; - independent_players = 1; - SetLimits(0, 0, autocvar_timelimit_override, -1); - - cts_Initialize(); - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // we actually cannot roll back cts_Initialize here - // BUT: we don't need to! If this gets called, adding always - // succeeds. - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This is a game type and it cannot be removed at runtime."); - return -1; - } - - return 0; -} - -// scores -const float ST_CTS_LAPS = 1; -#endif - -#ifdef IMPLEMENTATION - #include float autocvar_g_cts_finish_kill_delay; @@ -438,5 +397,3 @@ void cts_Initialize() { cts_ScoreRules(); } - -#endif diff --git a/qcsrc/server/mutators/mutator/gamemode_cts.qh b/qcsrc/server/mutators/mutator/gamemode_cts.qh index 399830dad2..2a18fe915d 100644 --- a/qcsrc/server/mutators/mutator/gamemode_cts.qh +++ b/qcsrc/server/mutators/mutator/gamemode_cts.qh @@ -1,3 +1,39 @@ #pragma once #include "../gamemode.qh" +#include + +void cts_Initialize(); + +REGISTER_MUTATOR(cts, false) +{ + MUTATOR_ONADD + { + if (time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + + g_race_qualifying = true; + independent_players = 1; + SetLimits(0, 0, autocvar_timelimit_override, -1); + + cts_Initialize(); + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back cts_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This is a game type and it cannot be removed at runtime."); + return -1; + } + + return 0; +} + +// scores +const float ST_CTS_LAPS = 1; diff --git a/qcsrc/server/mutators/mutator/gamemode_deathmatch.qc b/qcsrc/server/mutators/mutator/gamemode_deathmatch.qc index 8663703513..9590027d3f 100644 --- a/qcsrc/server/mutators/mutator/gamemode_deathmatch.qc +++ b/qcsrc/server/mutators/mutator/gamemode_deathmatch.qc @@ -1,37 +1,7 @@ #include "gamemode_deathmatch.qh" -#ifndef GAMEMODE_DEATHMATCH_H -#define GAMEMODE_DEATHMATCH_H -REGISTER_MUTATOR(dm, false) -{ - MUTATOR_ONADD - { - if (time > 1) // game loads at time 1 - error("This is a game type and it cannot be added at runtime."); - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // we actually cannot roll back dm_Initialize here - // BUT: we don't need to! If this gets called, adding always - // succeeds. - } - - MUTATOR_ONREMOVE - { - error("This is a game type and it cannot be removed at runtime."); - return -1; - } - - return 0; -} - -#endif - -#ifdef IMPLEMENTATION MUTATOR_HOOKFUNCTION(dm, Scores_CountFragsRemaining) { // announce remaining frags return true; } -#endif diff --git a/qcsrc/server/mutators/mutator/gamemode_deathmatch.qh b/qcsrc/server/mutators/mutator/gamemode_deathmatch.qh index 399830dad2..d3cc197eaf 100644 --- a/qcsrc/server/mutators/mutator/gamemode_deathmatch.qh +++ b/qcsrc/server/mutators/mutator/gamemode_deathmatch.qh @@ -1,3 +1,27 @@ #pragma once #include "../gamemode.qh" + +REGISTER_MUTATOR(dm, false) +{ + MUTATOR_ONADD + { + if (time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back dm_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + error("This is a game type and it cannot be removed at runtime."); + return -1; + } + + return 0; +} diff --git a/qcsrc/server/mutators/mutator/gamemode_domination.qc b/qcsrc/server/mutators/mutator/gamemode_domination.qc index 7c1e811847..6ad5d48499 100644 --- a/qcsrc/server/mutators/mutator/gamemode_domination.qc +++ b/qcsrc/server/mutators/mutator/gamemode_domination.qc @@ -1,67 +1,4 @@ #include "gamemode_domination.qh" -#ifndef GAMEMODE_DOMINATION_H -#define GAMEMODE_DOMINATION_H - -#define autocvar_g_domination_point_limit cvar("g_domination_point_limit") -bool autocvar_g_domination_roundbased; -int autocvar_g_domination_roundbased_point_limit; -int autocvar_g_domination_point_leadlimit; - -void dom_Initialize(); - -REGISTER_MUTATOR(dom, false) -{ - MUTATOR_ONADD - { - if (time > 1) // game loads at time 1 - error("This is a game type and it cannot be added at runtime."); - dom_Initialize(); - - int fraglimit_override = autocvar_g_domination_point_limit; - if (autocvar_g_domination_roundbased && autocvar_g_domination_roundbased_point_limit) - fraglimit_override = autocvar_g_domination_roundbased_point_limit; - - ActivateTeamplay(); - SetLimits(fraglimit_override, autocvar_g_domination_point_leadlimit, autocvar_timelimit_override, -1); - have_team_spawns = -1; // request team spawns - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This is a game type and it cannot be removed at runtime."); - return -1; - } - - return 0; -} - -// score rule declarations -const float ST_DOM_TICKS = 1; -const float ST_DOM_CAPS = 1; - -// pps: points per second -.float dom_total_pps = _STAT(DOM_TOTAL_PPS); -.float dom_pps_red = _STAT(DOM_PPS_RED); -.float dom_pps_blue = _STAT(DOM_PPS_BLUE); -.float dom_pps_yellow = _STAT(DOM_PPS_YELLOW); -.float dom_pps_pink = _STAT(DOM_PPS_PINK); -float total_pps; -float pps_red; -float pps_blue; -float pps_yellow; -float pps_pink; - -// capture declarations -.float enemy_playerid; -.entity sprite; -.float captime; - -// misc globals -float domination_roundbased; -float domination_teams; -#endif - -#ifdef IMPLEMENTATION #include @@ -706,5 +643,3 @@ void dom_Initialize() g_domination = true; InitializeEntity(NULL, dom_DelayedInit, INITPRIO_GAMETYPE); } - -#endif diff --git a/qcsrc/server/mutators/mutator/gamemode_domination.qh b/qcsrc/server/mutators/mutator/gamemode_domination.qh index 399830dad2..e9eb2c2d9d 100644 --- a/qcsrc/server/mutators/mutator/gamemode_domination.qh +++ b/qcsrc/server/mutators/mutator/gamemode_domination.qh @@ -1,3 +1,63 @@ #pragma once #include "../gamemode.qh" + +#define autocvar_g_domination_point_limit cvar("g_domination_point_limit") +bool autocvar_g_domination_roundbased; +int autocvar_g_domination_roundbased_point_limit; +int autocvar_g_domination_point_leadlimit; + +void dom_Initialize(); + +REGISTER_MUTATOR(dom, false) +{ + MUTATOR_ONADD + { + if (time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + dom_Initialize(); + + int fraglimit_override = autocvar_g_domination_point_limit; + if (autocvar_g_domination_roundbased && autocvar_g_domination_roundbased_point_limit) + fraglimit_override = autocvar_g_domination_roundbased_point_limit; + + ActivateTeamplay(); + SetLimits(fraglimit_override, autocvar_g_domination_point_leadlimit, autocvar_timelimit_override, -1); + have_team_spawns = -1; // request team spawns + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This is a game type and it cannot be removed at runtime."); + return -1; + } + + return 0; +} + +// score rule declarations +const float ST_DOM_TICKS = 1; +const float ST_DOM_CAPS = 1; + +// pps: points per second +.float dom_total_pps = _STAT(DOM_TOTAL_PPS); +.float dom_pps_red = _STAT(DOM_PPS_RED); +.float dom_pps_blue = _STAT(DOM_PPS_BLUE); +.float dom_pps_yellow = _STAT(DOM_PPS_YELLOW); +.float dom_pps_pink = _STAT(DOM_PPS_PINK); +float total_pps; +float pps_red; +float pps_blue; +float pps_yellow; +float pps_pink; + +// capture declarations +.float enemy_playerid; +.entity sprite; +.float captime; + +// misc globals +float domination_roundbased; +float domination_teams; + +void AnimateDomPoint(entity this); diff --git a/qcsrc/server/mutators/mutator/gamemode_freezetag.qc b/qcsrc/server/mutators/mutator/gamemode_freezetag.qc index a875e19ab3..602a55b6b5 100644 --- a/qcsrc/server/mutators/mutator/gamemode_freezetag.qc +++ b/qcsrc/server/mutators/mutator/gamemode_freezetag.qc @@ -1,58 +1,4 @@ #include "gamemode_freezetag.qh" -#ifndef GAMEMODE_FREEZETAG_H -#define GAMEMODE_FREEZETAG_H - -int autocvar_g_freezetag_point_limit; -int autocvar_g_freezetag_point_leadlimit; -bool autocvar_g_freezetag_team_spawns; -void freezetag_Initialize(); - -REGISTER_MUTATOR(ft, false) -{ - MUTATOR_ONADD - { - if (time > 1) // game loads at time 1 - error("This is a game type and it cannot be added at runtime."); - freezetag_Initialize(); - - ActivateTeamplay(); - SetLimits(autocvar_g_freezetag_point_limit, autocvar_g_freezetag_point_leadlimit, autocvar_timelimit_override, -1); - - if (autocvar_g_freezetag_team_spawns) - have_team_spawns = -1; // request team spawns - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // we actually cannot roll back freezetag_Initialize here - // BUT: we don't need to! If this gets called, adding always - // succeeds. - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This is a game type and it cannot be removed at runtime."); - return -1; - } - - return 0; -} - -.float freezetag_frozen_time; -.float freezetag_frozen_timeout; -const float ICE_MAX_ALPHA = 1; -const float ICE_MIN_ALPHA = 0.1; -float freezetag_teams; - -.float reviving; // temp var - -float autocvar_g_freezetag_revive_extra_size; -float autocvar_g_freezetag_revive_speed; -bool autocvar_g_freezetag_revive_nade; -float autocvar_g_freezetag_revive_nade_health; - -#endif -#ifdef IMPLEMENTATION float autocvar_g_freezetag_frozen_maxtime; float autocvar_g_freezetag_revive_clearspeed; @@ -151,6 +97,9 @@ float freezetag_getWinnerTeam() return -1; // no player left } +void nades_Clear(entity); +void nades_GiveBonus(entity player, float score); + float freezetag_CheckWinner() { if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0) @@ -637,5 +586,3 @@ void freezetag_Initialize() EliminatedPlayers_Init(freezetag_isEliminated); } - -#endif diff --git a/qcsrc/server/mutators/mutator/gamemode_freezetag.qh b/qcsrc/server/mutators/mutator/gamemode_freezetag.qh index 399830dad2..fda0737dd9 100644 --- a/qcsrc/server/mutators/mutator/gamemode_freezetag.qh +++ b/qcsrc/server/mutators/mutator/gamemode_freezetag.qh @@ -1,3 +1,52 @@ #pragma once #include "../gamemode.qh" + +int autocvar_g_freezetag_point_limit; +int autocvar_g_freezetag_point_leadlimit; +bool autocvar_g_freezetag_team_spawns; +void freezetag_Initialize(); + +REGISTER_MUTATOR(ft, false) +{ + MUTATOR_ONADD + { + if (time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + freezetag_Initialize(); + + ActivateTeamplay(); + SetLimits(autocvar_g_freezetag_point_limit, autocvar_g_freezetag_point_leadlimit, autocvar_timelimit_override, -1); + + if (autocvar_g_freezetag_team_spawns) + have_team_spawns = -1; // request team spawns + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back freezetag_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This is a game type and it cannot be removed at runtime."); + return -1; + } + + return 0; +} + +.float freezetag_frozen_time; +.float freezetag_frozen_timeout; +const float ICE_MAX_ALPHA = 1; +const float ICE_MIN_ALPHA = 0.1; +float freezetag_teams; + +.float reviving; // temp var + +float autocvar_g_freezetag_revive_extra_size; +float autocvar_g_freezetag_revive_speed; +bool autocvar_g_freezetag_revive_nade; +float autocvar_g_freezetag_revive_nade_health; diff --git a/qcsrc/server/mutators/mutator/gamemode_invasion.qc b/qcsrc/server/mutators/mutator/gamemode_invasion.qc index 184cf33304..1d9dc66201 100644 --- a/qcsrc/server/mutators/mutator/gamemode_invasion.qc +++ b/qcsrc/server/mutators/mutator/gamemode_invasion.qc @@ -1,66 +1,4 @@ #include "gamemode_invasion.qh" -#ifndef GAMEMODE_INVASION_H -#define GAMEMODE_INVASION_H - -#define autocvar_g_invasion_point_limit cvar("g_invasion_point_limit") -int autocvar_g_invasion_teams; -bool autocvar_g_invasion_team_spawns; -bool g_invasion; -void invasion_Initialize(); - -REGISTER_MUTATOR(inv, false) -{ - MUTATOR_ONADD - { - if (time > 1) // game loads at time 1 - error("This is a game type and it cannot be added at runtime."); - g_invasion = true; - invasion_Initialize(); - - cvar_settemp("g_monsters", "1"); - - SetLimits(autocvar_g_invasion_point_limit, autocvar_leadlimit_override, autocvar_timelimit_override, -1); - if (autocvar_g_invasion_teams >= 2) - { - ActivateTeamplay(); - if (autocvar_g_invasion_team_spawns) - have_team_spawns = -1; // request team spawns - } - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // we actually cannot roll back invasion_Initialize here - // BUT: we don't need to! If this gets called, adding always - // succeeds. - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This is a game type and it cannot be removed at runtime."); - return -1; - } - - return 0; -} - -float inv_numspawned; -float inv_maxspawned; -float inv_roundcnt; -float inv_maxrounds; -float inv_numkilled; -float inv_lastcheck; -float inv_maxcurrent; - -float invasion_teams; -float inv_monsters_perteam[17]; - -float inv_monsterskill; - -const float ST_INV_KILLS = 1; -#endif - -#ifdef IMPLEMENTATION #include #include @@ -544,5 +482,3 @@ void invasion_Initialize() InitializeEntity(NULL, invasion_DelayedInit, INITPRIO_GAMETYPE); } - -#endif diff --git a/qcsrc/server/mutators/mutator/gamemode_invasion.qh b/qcsrc/server/mutators/mutator/gamemode_invasion.qh index 399830dad2..e934f8745a 100644 --- a/qcsrc/server/mutators/mutator/gamemode_invasion.qh +++ b/qcsrc/server/mutators/mutator/gamemode_invasion.qh @@ -1,3 +1,60 @@ #pragma once #include "../gamemode.qh" + +#define autocvar_g_invasion_point_limit cvar("g_invasion_point_limit") +int autocvar_g_invasion_teams; +bool autocvar_g_invasion_team_spawns; +bool g_invasion; +void invasion_Initialize(); + +REGISTER_MUTATOR(inv, false) +{ + MUTATOR_ONADD + { + if (time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + g_invasion = true; + invasion_Initialize(); + + cvar_settemp("g_monsters", "1"); + + SetLimits(autocvar_g_invasion_point_limit, autocvar_leadlimit_override, autocvar_timelimit_override, -1); + if (autocvar_g_invasion_teams >= 2) + { + ActivateTeamplay(); + if (autocvar_g_invasion_team_spawns) + have_team_spawns = -1; // request team spawns + } + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back invasion_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This is a game type and it cannot be removed at runtime."); + return -1; + } + + return 0; +} + +float inv_numspawned; +float inv_maxspawned; +float inv_roundcnt; +float inv_maxrounds; +float inv_numkilled; +float inv_lastcheck; +float inv_maxcurrent; + +float invasion_teams; +float inv_monsters_perteam[17]; + +float inv_monsterskill; + +const float ST_INV_KILLS = 1; diff --git a/qcsrc/server/mutators/mutator/gamemode_keepaway.qc b/qcsrc/server/mutators/mutator/gamemode_keepaway.qc index da43b84deb..a430ab5368 100644 --- a/qcsrc/server/mutators/mutator/gamemode_keepaway.qc +++ b/qcsrc/server/mutators/mutator/gamemode_keepaway.qc @@ -1,44 +1,4 @@ #include "gamemode_keepaway.qh" -#ifndef GAMEMODE_KEEPAWAY_H -#define GAMEMODE_KEEPAWAY_H - -void ka_Initialize(); - -REGISTER_MUTATOR(ka, false) -{ - MUTATOR_ONADD - { - if (time > 1) // game loads at time 1 - error("This is a game type and it cannot be added at runtime."); - ka_Initialize(); - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // we actually cannot roll back ka_Initialize here - // BUT: we don't need to! If this gets called, adding always - // succeeds. - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This is a game type and it cannot be removed at runtime."); - return -1; - } - - return false; -} - - -entity ka_ball; - -void(entity this) havocbot_role_ka_carrier; -void(entity this) havocbot_role_ka_collector; - -void ka_DropEvent(entity plyr); -#endif - -#ifdef IMPLEMENTATION int autocvar_g_keepaway_ballcarrier_effects; float autocvar_g_keepaway_ballcarrier_damage; @@ -471,6 +431,7 @@ MUTATOR_HOOKFUNCTION(ka, DropSpecialItems) ka_DropEvent(frag_target); } +.bool pushable; // ============== // Initialization @@ -513,5 +474,3 @@ void ka_Initialize() // run at the start of a match, initiates game mode ka_ScoreRules(); ka_SpawnBall(); } - -#endif diff --git a/qcsrc/server/mutators/mutator/gamemode_keepaway.qh b/qcsrc/server/mutators/mutator/gamemode_keepaway.qh index 399830dad2..a13ab83a55 100644 --- a/qcsrc/server/mutators/mutator/gamemode_keepaway.qh +++ b/qcsrc/server/mutators/mutator/gamemode_keepaway.qh @@ -1,3 +1,38 @@ #pragma once #include "../gamemode.qh" + +void ka_Initialize(); + +REGISTER_MUTATOR(ka, false) +{ + MUTATOR_ONADD + { + if (time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + ka_Initialize(); + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back ka_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This is a game type and it cannot be removed at runtime."); + return -1; + } + + return false; +} + + +entity ka_ball; + +void(entity this) havocbot_role_ka_carrier; +void(entity this) havocbot_role_ka_collector; + +void ka_DropEvent(entity plyr); diff --git a/qcsrc/server/mutators/mutator/gamemode_keyhunt.qc b/qcsrc/server/mutators/mutator/gamemode_keyhunt.qc index 5f6b5226d4..86d76e4312 100644 --- a/qcsrc/server/mutators/mutator/gamemode_keyhunt.qc +++ b/qcsrc/server/mutators/mutator/gamemode_keyhunt.qc @@ -1,58 +1,4 @@ #include "gamemode_keyhunt.qh" -#ifndef GAMEMODE_KEYHUNT_H -#define GAMEMODE_KEYHUNT_H - -#define autocvar_g_keyhunt_point_limit cvar("g_keyhunt_point_limit") -int autocvar_g_keyhunt_point_leadlimit; -bool autocvar_g_keyhunt_team_spawns; -void kh_Initialize(); - -REGISTER_MUTATOR(kh, false) -{ - MUTATOR_ONADD - { - if (time > 1) // game loads at time 1 - error("This is a game type and it cannot be added at runtime."); - kh_Initialize(); - - ActivateTeamplay(); - SetLimits(autocvar_g_keyhunt_point_limit, autocvar_g_keyhunt_point_leadlimit, autocvar_timelimit_override, -1); - if (autocvar_g_keyhunt_team_spawns) - have_team_spawns = -1; // request team spawns - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // we actually cannot roll back kh_Initialize here - // BUT: we don't need to! If this gets called, adding always - // succeeds. - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This is a game type and it cannot be removed at runtime."); - return -1; - } - - return 0; -} - -#define FOR_EACH_KH_KEY(v) for(v = kh_worldkeylist; v; v = v.kh_worldkeynext ) - -// ALL OF THESE should be removed in the future, as other code should not have to care - -// used by bots: -float kh_tracking_enabled; -.entity kh_next; -float kh_Key_AllOwnedByWhichTeam(); - -USING(kh_Think_t, void()); -void kh_StartRound(); -void kh_Controller_SetThink(float t, kh_Think_t func); - -#endif - -#ifdef IMPLEMENTATION float autocvar_g_balance_keyhunt_damageforcescale; float autocvar_g_balance_keyhunt_delay_collect; @@ -570,6 +516,8 @@ void kh_FinishRound() // runs when a team captures the keys kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_round, kh_StartRound); } +void nades_GiveBonus(entity player, float score); + void kh_WinnerTeam(float teem) // runs when a team wins // Samual: Teem?.... TEEM?!?! what the fuck is wrong with you people { // all key carriers get some points @@ -1392,5 +1340,3 @@ MUTATOR_HOOKFUNCTION(kh, reset_map_global) { kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_round + (game_starttime - time), kh_StartRound); } - -#endif diff --git a/qcsrc/server/mutators/mutator/gamemode_keyhunt.qh b/qcsrc/server/mutators/mutator/gamemode_keyhunt.qh index 399830dad2..9a98df98fd 100644 --- a/qcsrc/server/mutators/mutator/gamemode_keyhunt.qh +++ b/qcsrc/server/mutators/mutator/gamemode_keyhunt.qh @@ -1,3 +1,51 @@ #pragma once #include "../gamemode.qh" + +#define autocvar_g_keyhunt_point_limit cvar("g_keyhunt_point_limit") +int autocvar_g_keyhunt_point_leadlimit; +bool autocvar_g_keyhunt_team_spawns; +void kh_Initialize(); + +REGISTER_MUTATOR(kh, false) +{ + MUTATOR_ONADD + { + if (time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + kh_Initialize(); + + ActivateTeamplay(); + SetLimits(autocvar_g_keyhunt_point_limit, autocvar_g_keyhunt_point_leadlimit, autocvar_timelimit_override, -1); + if (autocvar_g_keyhunt_team_spawns) + have_team_spawns = -1; // request team spawns + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back kh_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This is a game type and it cannot be removed at runtime."); + return -1; + } + + return 0; +} + +#define FOR_EACH_KH_KEY(v) for(v = kh_worldkeylist; v; v = v.kh_worldkeynext ) + +// ALL OF THESE should be removed in the future, as other code should not have to care + +// used by bots: +float kh_tracking_enabled; +.entity kh_next; +float kh_Key_AllOwnedByWhichTeam(); + +USING(kh_Think_t, void()); +void kh_StartRound(); +void kh_Controller_SetThink(float t, kh_Think_t func); diff --git a/qcsrc/server/mutators/mutator/gamemode_lms.qc b/qcsrc/server/mutators/mutator/gamemode_lms.qc index afd107bc43..608517fbff 100644 --- a/qcsrc/server/mutators/mutator/gamemode_lms.qc +++ b/qcsrc/server/mutators/mutator/gamemode_lms.qc @@ -1,49 +1,8 @@ #include "gamemode_lms.qh" -#ifndef GAMEMODE_LMS_H -#define GAMEMODE_LMS_H - -#define autocvar_g_lms_lives_override cvar("g_lms_lives_override") -void lms_Initialize(); - -REGISTER_MUTATOR(lms, false) -{ - MUTATOR_ONADD - { - if (time > 1) // game loads at time 1 - error("This is a game type and it cannot be added at runtime."); - lms_Initialize(); - - SetLimits(((!autocvar_g_lms_lives_override) ? -1 : autocvar_g_lms_lives_override), 0, autocvar_timelimit_override, -1); - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // we actually cannot roll back lms_Initialize here - // BUT: we don't need to! If this gets called, adding always - // succeeds. - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This is a game type and it cannot be removed at runtime."); - return -1; - } - - return 0; -} - -// lives related defs -float lms_lowest_lives; -float lms_next_place; -float LMS_NewPlayerLives(); - -#endif - -#ifdef IMPLEMENTATION #include #include -#include +#include int autocvar_g_lms_extra_lives; bool autocvar_g_lms_join_anytime; @@ -408,6 +367,3 @@ void lms_Initialize() lms_ScoreRules(); } - - -#endif diff --git a/qcsrc/server/mutators/mutator/gamemode_lms.qh b/qcsrc/server/mutators/mutator/gamemode_lms.qh index 399830dad2..7bf012668e 100644 --- a/qcsrc/server/mutators/mutator/gamemode_lms.qh +++ b/qcsrc/server/mutators/mutator/gamemode_lms.qh @@ -1,3 +1,38 @@ #pragma once #include "../gamemode.qh" + +#define autocvar_g_lms_lives_override cvar("g_lms_lives_override") +void lms_Initialize(); + +REGISTER_MUTATOR(lms, false) +{ + MUTATOR_ONADD + { + if (time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + lms_Initialize(); + + SetLimits(((!autocvar_g_lms_lives_override) ? -1 : autocvar_g_lms_lives_override), 0, autocvar_timelimit_override, -1); + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back lms_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This is a game type and it cannot be removed at runtime."); + return -1; + } + + return 0; +} + +// lives related defs +float lms_lowest_lives; +float lms_next_place; +float LMS_NewPlayerLives(); diff --git a/qcsrc/server/mutators/mutator/gamemode_race.qc b/qcsrc/server/mutators/mutator/gamemode_race.qc index 04561db369..756d975b15 100644 --- a/qcsrc/server/mutators/mutator/gamemode_race.qc +++ b/qcsrc/server/mutators/mutator/gamemode_race.qc @@ -1,42 +1,5 @@ #include "gamemode_race.qh" -#ifndef GAMEMODE_RACE_H -#define GAMEMODE_RACE_H - -void rc_SetLimits(); -void race_Initialize(); - -REGISTER_MUTATOR(rc, false) -{ - MUTATOR_ONADD - { - if (time > 1) // game loads at time 1 - error("This is a game type and it cannot be added at runtime."); - - rc_SetLimits(); - race_Initialize(); - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // we actually cannot roll back race_Initialize here - // BUT: we don't need to! If this gets called, adding always - // succeeds. - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This is a game type and it cannot be removed at runtime."); - return -1; - } - - return 0; -} - -#endif - -#ifdef IMPLEMENTATION - #include #define autocvar_g_race_laps_limit cvar("g_race_laps_limit") @@ -505,5 +468,3 @@ void rc_SetLimits() g_race_qualifying = 0; SetLimits(fraglimit_override, leadlimit_override, timelimit_override, qualifying_override); } - -#endif diff --git a/qcsrc/server/mutators/mutator/gamemode_race.qh b/qcsrc/server/mutators/mutator/gamemode_race.qh index 399830dad2..ec71a62d17 100644 --- a/qcsrc/server/mutators/mutator/gamemode_race.qh +++ b/qcsrc/server/mutators/mutator/gamemode_race.qh @@ -1,3 +1,33 @@ #pragma once #include "../gamemode.qh" + +void rc_SetLimits(); +void race_Initialize(); + +REGISTER_MUTATOR(rc, false) +{ + MUTATOR_ONADD + { + if (time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + + rc_SetLimits(); + race_Initialize(); + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back race_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This is a game type and it cannot be removed at runtime."); + return -1; + } + + return 0; +} diff --git a/qcsrc/server/mutators/mutator/gamemode_tdm.qc b/qcsrc/server/mutators/mutator/gamemode_tdm.qc index 8fdfc83433..d906c1988c 100644 --- a/qcsrc/server/mutators/mutator/gamemode_tdm.qc +++ b/qcsrc/server/mutators/mutator/gamemode_tdm.qc @@ -1,45 +1,5 @@ #include "gamemode_tdm.qh" -#ifndef GAMEMODE_TDM_H -#define GAMEMODE_TDM_H -int autocvar_g_tdm_point_limit; -int autocvar_g_tdm_point_leadlimit; -bool autocvar_g_tdm_team_spawns; -void tdm_DelayedInit(entity this); - -REGISTER_MUTATOR(tdm, false) -{ - MUTATOR_ONADD - { - if (time > 1) // game loads at time 1 - error("This is a game type and it cannot be added at runtime."); - InitializeEntity(NULL, tdm_DelayedInit, INITPRIO_GAMETYPE); - - ActivateTeamplay(); - SetLimits(autocvar_g_tdm_point_limit, autocvar_g_tdm_point_leadlimit, autocvar_timelimit_override, -1); - if (autocvar_g_tdm_team_spawns) - have_team_spawns = -1; // request team spawns - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // we actually cannot roll back tdm_Initialize here - // BUT: we don't need to! If this gets called, adding always - // succeeds. - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This is a game type and it cannot be removed at runtime."); - return -1; - } - - return 0; -} - -#endif - -#ifdef IMPLEMENTATION int autocvar_g_tdm_teams; int autocvar_g_tdm_teams_override; @@ -108,5 +68,3 @@ MUTATOR_HOOKFUNCTION(tdm, Scores_CountFragsRemaining) // announce remaining frags return true; } - -#endif diff --git a/qcsrc/server/mutators/mutator/gamemode_tdm.qh b/qcsrc/server/mutators/mutator/gamemode_tdm.qh index 399830dad2..e7efbae7f5 100644 --- a/qcsrc/server/mutators/mutator/gamemode_tdm.qh +++ b/qcsrc/server/mutators/mutator/gamemode_tdm.qh @@ -1,3 +1,38 @@ #pragma once #include "../gamemode.qh" + +int autocvar_g_tdm_point_limit; +int autocvar_g_tdm_point_leadlimit; +bool autocvar_g_tdm_team_spawns; +void tdm_DelayedInit(entity this); + +REGISTER_MUTATOR(tdm, false) +{ + MUTATOR_ONADD + { + if (time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + InitializeEntity(NULL, tdm_DelayedInit, INITPRIO_GAMETYPE); + + ActivateTeamplay(); + SetLimits(autocvar_g_tdm_point_limit, autocvar_g_tdm_point_leadlimit, autocvar_timelimit_override, -1); + if (autocvar_g_tdm_team_spawns) + have_team_spawns = -1; // request team spawns + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back tdm_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This is a game type and it cannot be removed at runtime."); + return -1; + } + + return 0; +} diff --git a/qcsrc/server/pathlib/_all.inc b/qcsrc/server/pathlib/_all.inc deleted file mode 100644 index 7a06615bf3..0000000000 --- a/qcsrc/server/pathlib/_all.inc +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef DEBUGPATHING - #define DEBUGPATHING 0 -#endif - -#include "costs.qc" -#include "expandnode.qc" -#include "main.qc" -#include "movenode.qc" -#include "path_waypoint.qc" -#include "utility.qc" -#if DEBUGPATHING - #include "debug.qc" -#endif diff --git a/qcsrc/server/pathlib/costs.qc b/qcsrc/server/pathlib/costs.qc index 6b89bb6402..fdb95d2f60 100644 --- a/qcsrc/server/pathlib/costs.qc +++ b/qcsrc/server/pathlib/costs.qc @@ -1,5 +1,4 @@ #include "costs.qh" -#include "pathlib.qh" float pathlib_g_static(entity parent,vector to, float static_cost) { diff --git a/qcsrc/server/pathlib/costs.qh b/qcsrc/server/pathlib/costs.qh index 6f70f09bee..811c031aff 100644 --- a/qcsrc/server/pathlib/costs.qh +++ b/qcsrc/server/pathlib/costs.qh @@ -1 +1,2 @@ #pragma once +#include "pathlib.qh" diff --git a/qcsrc/server/pathlib/debug.qc b/qcsrc/server/pathlib/debug.qc index 0a350df2c2..b84ae6414e 100644 --- a/qcsrc/server/pathlib/debug.qc +++ b/qcsrc/server/pathlib/debug.qc @@ -1,5 +1,6 @@ #include "debug.qh" -#include "pathlib.qh" + +#if DEBUGPATHING MODEL(SQUARE, "models/pathlib/square.md3"); MODEL(SQUARE_GOOD, "models/pathlib/goodsquare.md3"); @@ -119,3 +120,5 @@ void pathlib_showedge(vector where,float _lifetime,float rot) //e.angles_x += 90; } + +#endif diff --git a/qcsrc/server/pathlib/debug.qh b/qcsrc/server/pathlib/debug.qh index 6f70f09bee..811c031aff 100644 --- a/qcsrc/server/pathlib/debug.qh +++ b/qcsrc/server/pathlib/debug.qh @@ -1 +1,2 @@ #pragma once +#include "pathlib.qh" diff --git a/qcsrc/server/pathlib/pathlib.qh b/qcsrc/server/pathlib/pathlib.qh index 339a2e7e0e..4ae0c2c86d 100644 --- a/qcsrc/server/pathlib/pathlib.qh +++ b/qcsrc/server/pathlib/pathlib.qh @@ -1,5 +1,9 @@ #pragma once +#ifndef DEBUGPATHING + #define DEBUGPATHING 0 +#endif + .entity pathlib_list; .entity path_next; .entity path_prev; diff --git a/qcsrc/server/player.qc b/qcsrc/server/player.qc new file mode 100644 index 0000000000..f2ceecf9ee --- /dev/null +++ b/qcsrc/server/player.qc @@ -0,0 +1,964 @@ +#include "player.qh" + +#include "bot/api.qh" +#include "cheats.qh" +#include "g_damage.qh" +#include "g_subs.qh" +#include "miscfunctions.qh" +#include "portals.qh" +#include "teamplay.qh" +#include "weapons/throwing.qh" +#include "command/common.qh" +#include "../common/state.qh" +#include "../common/anim.qh" +#include "../common/animdecide.qh" +#include "../common/csqcmodel_settings.qh" +#include "../common/deathtypes/all.qh" +#include "../common/triggers/subs.qh" +#include "../common/playerstats.qh" +#include "../lib/csqcmodel/sv_model.qh" + +#include "../common/minigames/sv_minigames.qh" + +#include "../common/physics/player.qh" +#include "../common/effects/qc/all.qh" +#include "../common/mutators/mutator/waypoints/waypointsprites.qh" +#include "../common/triggers/include.qh" + +#include "weapons/weaponstats.qh" + +#include "../common/animdecide.qh" + +void Drop_Special_Items(entity player) +{ + // called when the player has become stuck or frozen + // so objective items aren't stuck with the player + + MUTATOR_CALLHOOK(DropSpecialItems, player); +} + +void CopyBody_Think(entity this) +{ + if(this.CopyBody_nextthink && time > this.CopyBody_nextthink) + { + this.CopyBody_think(this); + if(wasfreed(this)) + return; + this.CopyBody_nextthink = this.nextthink; + this.CopyBody_think = getthink(this); + setthink(this, CopyBody_Think); + } + CSQCMODEL_AUTOUPDATE(this); + this.nextthink = time; +} +void CopyBody(entity this, float keepvelocity) +{ + if (this.effects & EF_NODRAW) + return; + entity clone = new(body); + clone.enemy = this; + clone.lip = this.lip; + clone.colormap = this.colormap; + clone.iscreature = this.iscreature; + clone.teleportable = this.teleportable; + clone.damagedbycontents = this.damagedbycontents; + clone.angles = this.angles; + clone.v_angle = this.v_angle; + clone.avelocity = this.avelocity; + clone.damageforcescale = this.damageforcescale; + clone.effects = this.effects; + clone.glowmod = this.glowmod; + clone.event_damage = this.event_damage; + clone.anim_state = this.anim_state; + clone.anim_time = this.anim_time; + clone.anim_lower_action = this.anim_lower_action; + clone.anim_lower_time = this.anim_lower_time; + clone.anim_upper_action = this.anim_upper_action; + clone.anim_upper_time = this.anim_upper_time; + clone.anim_implicit_state = this.anim_implicit_state; + clone.anim_implicit_time = this.anim_implicit_time; + clone.anim_lower_implicit_action = this.anim_lower_implicit_action; + clone.anim_lower_implicit_time = this.anim_lower_implicit_time; + clone.anim_upper_implicit_action = this.anim_upper_implicit_action; + clone.anim_upper_implicit_time = this.anim_upper_implicit_time; + clone.dphitcontentsmask = this.dphitcontentsmask; + clone.death_time = this.death_time; + clone.pain_finished = this.pain_finished; + clone.health = this.health; + clone.armorvalue = this.armorvalue; + clone.armortype = this.armortype; + clone.model = this.model; + clone.modelindex = this.modelindex; + clone.skin = this.skin; + clone.species = this.species; + clone.move_qcphysics = false; // don't run gamecode logic on clones, too many + set_movetype(clone, this.move_movetype); + clone.solid = this.solid; + clone.ballistics_density = this.ballistics_density; + clone.takedamage = this.takedamage; + setcefc(clone, getcefc(this)); + clone.uncustomizeentityforclient = this.uncustomizeentityforclient; + clone.uncustomizeentityforclient_set = this.uncustomizeentityforclient_set; + if (keepvelocity == 1) + clone.velocity = this.velocity; + clone.oldvelocity = clone.velocity; + clone.alpha = this.alpha; + clone.fade_time = this.fade_time; + clone.fade_rate = this.fade_rate; + //clone.weapon = this.weapon; + setorigin(clone, this.origin); + setsize(clone, this.mins, this.maxs); + clone.prevorigin = this.origin; + clone.reset = SUB_Remove; + clone._ps = this._ps; + + Drag_MoveDrag(this, clone); + + if(clone.colormap <= maxclients && clone.colormap > 0) + clone.colormap = 1024 + this.clientcolors; + + CSQCMODEL_AUTOINIT(clone); + clone.CopyBody_nextthink = this.nextthink; + clone.CopyBody_think = getthink(this); + clone.nextthink = time; + setthink(clone, CopyBody_Think); + // "bake" the current animation frame for clones (they don't get clientside animation) + animdecide_load_if_needed(clone); + animdecide_setframes(clone, false, frame, frame1time, frame2, frame2time); + + MUTATOR_CALLHOOK(CopyBody, this, clone, keepvelocity); +} + +void player_setupanimsformodel(entity this) +{ + // load animation info + animdecide_load_if_needed(this); + animdecide_setstate(this, 0, false); +} + +void player_anim(entity this) +{ + int deadbits = (this.anim_state & (ANIMSTATE_DEAD1 | ANIMSTATE_DEAD2)); + if(IS_DEAD(this)) { + if (!deadbits) { + // Decide on which death animation to use. + if(random() < 0.5) + deadbits = ANIMSTATE_DEAD1; + else + deadbits = ANIMSTATE_DEAD2; + } + } else { + // Clear a previous death animation. + deadbits = 0; + } + int animbits = deadbits; + if(STAT(FROZEN, this)) + animbits |= ANIMSTATE_FROZEN; + if(this.move_movetype == MOVETYPE_FOLLOW) + animbits |= ANIMSTATE_FOLLOW; + if(this.crouch) + animbits |= ANIMSTATE_DUCK; + animdecide_setstate(this, animbits, false); + animdecide_setimplicitstate(this, IS_ONGROUND(this)); +} + +void PlayerCorpseDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) +{ + float take, save; + vector v; + Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, this, attacker); + + // damage resistance (ignore most of the damage from a bullet or similar) + damage = max(damage - 5, 1); + + v = healtharmor_applydamage(this.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, damage); + take = v.x; + save = v.y; + + if(sound_allowed(MSG_BROADCAST, attacker)) + { + if (save > 10) + sound (this, CH_SHOTS, SND_ARMORIMPACT, VOL_BASE, ATTEN_NORM); + else if (take > 30) + sound (this, CH_SHOTS, SND_BODYIMPACT2, VOL_BASE, ATTEN_NORM); + else if (take > 10) + sound (this, CH_SHOTS, SND_BODYIMPACT1, VOL_BASE, ATTEN_NORM); + } + + if (take > 50) + Violence_GibSplash_At(hitloc, force * -0.1, 3, 1, this, attacker); + if (take > 100) + Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, this, attacker); + + this.armorvalue = this.armorvalue - save; + this.health = this.health - take; + // pause regeneration for 5 seconds + this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_health_regen); + + this.dmg_save = this.dmg_save + save;//max(save - 10, 0); + this.dmg_take = this.dmg_take + take;//max(take - 10, 0); + this.dmg_inflictor = inflictor; + + if (this.health <= -autocvar_sv_gibhealth && this.alpha >= 0) + { + // don't use any animations as a gib + this.frame = 0; + // view just above the floor + this.view_ofs = '0 0 4'; + + Violence_GibSplash(this, 1, 1, attacker); + this.alpha = -1; + this.solid = SOLID_NOT; // restore later + this.takedamage = DAMAGE_NO; // restore later + this.damagedbycontents = false; + } +} + +void calculate_player_respawn_time(entity this) +{ + if(g_ca) + return; + + float gametype_setting_tmp; + float sdelay_max = GAMETYPE_DEFAULTED_SETTING(respawn_delay_max); + float sdelay_small = GAMETYPE_DEFAULTED_SETTING(respawn_delay_small); + float sdelay_large = GAMETYPE_DEFAULTED_SETTING(respawn_delay_large); + float sdelay_small_count = GAMETYPE_DEFAULTED_SETTING(respawn_delay_small_count); + float sdelay_large_count = GAMETYPE_DEFAULTED_SETTING(respawn_delay_large_count); + float waves = GAMETYPE_DEFAULTED_SETTING(respawn_waves); + + float pcount = 1; // Include myself whether or not team is already set right and I'm a "player". + if (teamplay) + { + FOREACH_CLIENT(IS_PLAYER(it) && it != this, LAMBDA( + if(it.team == this.team) + ++pcount; + )); + if (sdelay_small_count == 0) + sdelay_small_count = 1; + if (sdelay_large_count == 0) + sdelay_large_count = 1; + } + else + { + FOREACH_CLIENT(IS_PLAYER(it) && it != this, LAMBDA( + ++pcount; + )); + if (sdelay_small_count == 0) + { + if (g_cts) + { + // Players play independently. No point in requiring enemies. + sdelay_small_count = 1; + } + else + { + // Players play AGAINST each other. Enemies required. + sdelay_small_count = 2; + } + } + if (sdelay_large_count == 0) + { + if (g_cts) + { + // Players play independently. No point in requiring enemies. + sdelay_large_count = 1; + } + else + { + // Players play AGAINST each other. Enemies required. + sdelay_large_count = 2; + } + } + } + + float sdelay; + + if (pcount <= sdelay_small_count) + sdelay = sdelay_small; + else if (pcount >= sdelay_large_count) + sdelay = sdelay_large; + else // NOTE: this case implies sdelay_large_count > sdelay_small_count. + sdelay = sdelay_small + (sdelay_large - sdelay_small) * (pcount - sdelay_small_count) / (sdelay_large_count - sdelay_small_count); + + if(waves) + this.respawn_time = ceil((time + sdelay) / waves) * waves; + else + this.respawn_time = time + sdelay; + + if(sdelay < sdelay_max) + this.respawn_time_max = time + sdelay_max; + else + this.respawn_time_max = this.respawn_time; + + if((sdelay + waves >= 5.0) && (this.respawn_time - time > 1.75)) + this.respawn_countdown = 10; // first number to count down from is 10 + else + this.respawn_countdown = -1; // do not count down + + if(autocvar_g_forced_respawn) + this.respawn_flags = this.respawn_flags | RESPAWN_FORCE; +} + +void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) +{ + float take, save, dh, da; + vector v; + float valid_damage_for_weaponstats; + float excess; + + dh = max(this.health, 0); + da = max(this.armorvalue, 0); + + if(!DEATH_ISSPECIAL(deathtype)) + { + damage *= sqrt(bound(1.0, this.cvar_cl_handicap, 100.0)); + if(this != attacker) + damage /= sqrt(bound(1.0, attacker.cvar_cl_handicap, 100.0)); + } + + if(DEATH_ISWEAPON(deathtype, WEP_TUBA)) + { + // tuba causes blood to come out of the ears + vector ear1, ear2; + vector d; + float f; + ear1 = this.origin; + ear1_z += 0.125 * this.view_ofs.z + 0.875 * this.maxs.z; // 7/8 + ear2 = ear1; + makevectors(this.angles); + ear1 += v_right * -10; + ear2 += v_right * +10; + d = inflictor.origin - this.origin; + if (d) + f = (d * v_right) / vlen(d); // this is cos of angle of d and v_right! + else + f = 0; // Assum ecenter. + force = v_right * vlen(force); + Violence_GibSplash_At(ear1, force * -1, 2, bound(0, damage, 25) / 2 * (0.5 - 0.5 * f), this, attacker); + Violence_GibSplash_At(ear2, force, 2, bound(0, damage, 25) / 2 * (0.5 + 0.5 * f), this, attacker); + if(f > 0) + { + hitloc = ear1; + force = force * -1; + } + else + { + hitloc = ear2; + // force is already good + } + } + else + Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, this, attacker); + + + v = healtharmor_applydamage(this.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, damage); + take = v.x; + save = v.y; + + if(attacker == this) + { + // don't reset pushltime for this damage as it may be an attempt to + // escape a lava pit or similar + //this.pushltime = 0; + this.istypefrag = 0; + } + else if(IS_PLAYER(attacker)) + { + this.pusher = attacker; + this.pushltime = time + autocvar_g_maxpushtime; + this.istypefrag = PHYS_INPUT_BUTTON_CHAT(this); + } + else if(time < this.pushltime) + { + attacker = this.pusher; + this.pushltime = max(this.pushltime, time + 0.6); + } + else + { + this.pushltime = 0; + this.istypefrag = 0; + } + + if (time < this.spawnshieldtime && autocvar_g_spawnshield_blockdamage < 1) + { + vector v = healtharmor_applydamage(this.armorvalue, max(0, autocvar_g_spawnshield_blockdamage), deathtype, damage); + take = v.x; + save = v.y; + } + + MUTATOR_CALLHOOK(PlayerDamage_SplitHealthArmor, inflictor, attacker, this, force, take, save, deathtype, damage); + take = bound(0, M_ARGV(4, float), this.health); + save = bound(0, M_ARGV(5, float), this.armorvalue); + excess = max(0, damage - take - save); + + if(sound_allowed(MSG_BROADCAST, attacker)) + { + if (save > 10) + sound (this, CH_SHOTS, SND_ARMORIMPACT, VOL_BASE, ATTEN_NORM); + else if (take > 30) + sound (this, CH_SHOTS, SND_BODYIMPACT2, VOL_BASE, ATTEN_NORM); + else if (take > 10) + sound (this, CH_SHOTS, SND_BODYIMPACT1, VOL_BASE, ATTEN_NORM); // FIXME possibly remove them? + } + + if (take > 50) + Violence_GibSplash_At(hitloc, force * -0.1, 3, 1, this, attacker); + if (take > 100) + Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, this, attacker); + + if (time >= this.spawnshieldtime || autocvar_g_spawnshield_blockdamage < 1) + { + if (!(this.flags & FL_GODMODE)) + { + this.armorvalue = this.armorvalue - save; + this.health = this.health - take; + // pause regeneration for 5 seconds + if(take) + this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_health_regen); + + if (time > this.pain_finished) //Don't switch pain sequences like crazy + { + this.pain_finished = time + 0.5; //Supajoe + + if(autocvar_sv_gentle < 1) { + if(this.classname != "body") // pain anim is BORKED on our ZYMs, FIXME remove this once we have good models + { + if (!this.animstate_override) + { + if (random() > 0.5) + animdecide_setaction(this, ANIMACTION_PAIN1, true); + else + animdecide_setaction(this, ANIMACTION_PAIN2, true); + } + } + + if(sound_allowed(MSG_BROADCAST, attacker)) + if((this.health < 2 * WEP_CVAR_PRI(blaster, damage) * autocvar_g_balance_selfdamagepercent + 1) || !(DEATH_WEAPONOF(deathtype).spawnflags & WEP_FLAG_CANCLIMB) || attacker != this) // WEAPONTODO: create separate limit for pain notification with laser + if(this.health > 1) + // exclude pain sounds for laserjumps as long as you aren't REALLY low on health and would die of the next two + { + if(deathtype == DEATH_FALL.m_id) + PlayerSound(this, playersound_fall, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); + else if(this.health > 75) + PlayerSound(this, playersound_pain100, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); + else if(this.health > 50) + PlayerSound(this, playersound_pain75, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); + else if(this.health > 25) + PlayerSound(this, playersound_pain50, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); + else + PlayerSound(this, playersound_pain25, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); + } + } + } + + // throw off bot aim temporarily + float shake; + if(IS_BOT_CLIENT(this) && this.health >= 1) + { + shake = damage * 5 / (bound(0,skill,100) + 1); + this.v_angle_x = this.v_angle.x + (random() * 2 - 1) * shake; + this.v_angle_y = this.v_angle.y + (random() * 2 - 1) * shake; + this.v_angle_x = bound(-90, this.v_angle.x, 90); + } + } + else + this.max_armorvalue += (save + take); + } + this.dmg_save = this.dmg_save + save;//max(save - 10, 0); + this.dmg_take = this.dmg_take + take;//max(take - 10, 0); + this.dmg_inflictor = inflictor; + + if (this != attacker) { + float realdmg = damage - excess; + if (IS_PLAYER(attacker)) { + PlayerScore_Add(attacker, SP_DMG, realdmg); + } + if (IS_PLAYER(this)) { + PlayerScore_Add(this, SP_DMGTAKEN, realdmg); + } + } + + bool abot = (IS_BOT_CLIENT(attacker)); + bool vbot = (IS_BOT_CLIENT(this)); + + valid_damage_for_weaponstats = 0; + Weapon awep = WEP_Null; + + if(vbot || IS_REAL_CLIENT(this)) + if(abot || IS_REAL_CLIENT(attacker)) + if(attacker && this != attacker) + if(DIFF_TEAM(this, attacker)) + { + if(DEATH_ISSPECIAL(deathtype)) + awep = PS(attacker).m_weapon; + else + awep = DEATH_WEAPONOF(deathtype); + valid_damage_for_weaponstats = 1; + } + + dh = dh - max(this.health, 0); + da = da - max(this.armorvalue, 0); + if(valid_damage_for_weaponstats) + { + WeaponStats_LogDamage(awep.m_id, abot, PS(this).m_weapon.m_id, vbot, dh + da); + } + if (dh + da) + { + MUTATOR_CALLHOOK(PlayerDamaged, attacker, this, dh, da, hitloc, deathtype); + } + + if (this.health < 1) + { + float defer_ClientKill_Now_TeamChange; + defer_ClientKill_Now_TeamChange = false; + + if(this.alivetime) + { + PS_GR_P_ADDVAL(this, PLAYERSTATS_ALIVETIME, time - this.alivetime); + this.alivetime = 0; + } + + if(valid_damage_for_weaponstats) + WeaponStats_LogKill(awep.m_id, abot, PS(this).m_weapon.m_id, vbot); + + if(autocvar_sv_gentle < 1) + if(sound_allowed(MSG_BROADCAST, attacker)) + { + if(deathtype == DEATH_DROWN.m_id) + PlayerSound(this, playersound_drown, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); + else + PlayerSound(this, playersound_death, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); + } + + // get rid of kill indicator + if(this.killindicator) + { + delete(this.killindicator); + this.killindicator = NULL; + if(this.killindicator_teamchange) + defer_ClientKill_Now_TeamChange = true; + + if(this.classname == "body") + if(deathtype == DEATH_KILL.m_id) + { + // for the lemmings fans, a small harmless explosion + Send_Effect(EFFECT_ROCKET_EXPLODE, this.origin, '0 0 0', 1); + } + } + + // print an obituary message + if(this.classname != "body") + Obituary (attacker, inflictor, this, deathtype); + + // increment frag counter for used weapon type + Weapon w = DEATH_WEAPONOF(deathtype); + if(w != WEP_Null) + if(accuracy_isgooddamage(attacker, this)) + attacker.accuracy.(accuracy_frags[w.m_id-1]) += 1; + + MUTATOR_CALLHOOK(PlayerDies, inflictor, attacker, this, deathtype, damage); + excess = M_ARGV(4, float); + + Weapon wep = PS(this).m_weapon; + /*for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + .entity weaponentity = weaponentities[slot]; + wep.wr_playerdeath(wep, this, weaponentity); + }*/ + .entity weaponentity = weaponentities[0]; // TODO: unhardcode + wep.wr_playerdeath(wep, this, weaponentity); + + RemoveGrapplingHook(this); + + Portal_ClearAllLater(this); + + this.fixangle = true; + + if(defer_ClientKill_Now_TeamChange) + ClientKill_Now_TeamChange(this); // can turn player into spectator + + // player could have been miraculously resuscitated ;) + // e.g. players in freezetag get frozen, they don't really die + if(this.health >= 1 || !(IS_PLAYER(this) || this.classname == "body")) + return; + + // when we get here, player actually dies + + Unfreeze(this); // remove any icy remains + this.health = 0; // Unfreeze resets health, so we need to set it back + + // clear waypoints + WaypointSprite_PlayerDead(this); + // throw a weapon + SpawnThrownWeapon(this, this.origin + (this.mins + this.maxs) * 0.5, PS(this).m_switchweapon.m_id); + + // become fully visible + this.alpha = default_player_alpha; + // make the corpse upright (not tilted) + this.angles_x = 0; + this.angles_z = 0; + // don't spin + this.avelocity = '0 0 0'; + // view from the floor + this.view_ofs = '0 0 -8'; + // toss the corpse + set_movetype(this, MOVETYPE_TOSS); + // shootable corpse + this.solid = SOLID_CORPSE; + this.ballistics_density = autocvar_g_ballistics_density_corpse; + // don't stick to the floor + UNSET_ONGROUND(this); + // dying animation + this.deadflag = DEAD_DYING; + + // when to allow respawn + calculate_player_respawn_time(this); + + this.death_time = time; + if (random() < 0.5) + animdecide_setstate(this, this.anim_state | ANIMSTATE_DEAD1, true); + else + animdecide_setstate(this, this.anim_state | ANIMSTATE_DEAD2, true); + if (this.maxs.z > 5) + { + this.maxs_z = 5; + setsize(this, this.mins, this.maxs); + } + // set damage function to corpse damage + this.event_damage = PlayerCorpseDamage; + // call the corpse damage function just in case it wants to gib + this.event_damage(this, inflictor, attacker, excess, deathtype, hitloc, force); + + // set up to fade out later + SUB_SetFade (this, time + 6 + random (), 1); + // reset body think wrapper broken by SUB_SetFade + if(this.classname == "body" && getthink(this) != CopyBody_Think) { + this.CopyBody_think = getthink(this); + this.CopyBody_nextthink = this.nextthink; + setthink(this, CopyBody_Think); + this.nextthink = time; + } + + if(autocvar_sv_gentle > 0 || autocvar_ekg || this.classname == "body") { + // remove corpse + // clones don't run any animation code any more, so we must gib them when they die :( + PlayerCorpseDamage(this, inflictor, attacker, autocvar_sv_gibhealth+1.0, deathtype, hitloc, force); + } + + // reset fields the weapons may use just in case + FOREACH(Weapons, it != WEP_Null, LAMBDA( + it.wr_resetplayer(it, this); + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + ATTACK_FINISHED_FOR(this, it.m_id, slot) = 0; + } + )); + } +} + +void MoveToTeam(entity client, int team_colour, int type) +{ + int lockteams_backup = lockteams; // backup any team lock + lockteams = 0; // disable locked teams + TeamchangeFrags(client); // move the players frags + SetPlayerColors(client, team_colour - 1); // set the players colour + Damage(client, client, client, 100000, DEATH_AUTOTEAMCHANGE.m_id, client.origin, '0 0 0'); // kill the player + lockteams = lockteams_backup; // restore the team lock + LogTeamchange(client.playerid, client.team, type); +} + +/** print(), but only print if the server is not local */ +void dedicated_print(string input) +{ + if (server_is_dedicated) print(input); +} + +/** + * message "": do not say, just test flood control + * return value: + * 1 = accept + * 0 = reject + * -1 = fake accept + */ +int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodcontrol) +{ + if (!teamsay && !privatesay) if (substring(msgin, 0, 1) == " ") + msgin = substring(msgin, 1, -1); // work around DP say bug (say_team does not have this!) + + msgin = formatmessage(source, msgin); + + string colorstr; + if (!IS_PLAYER(source)) + colorstr = "^0"; // black for spectators + else if(teamplay) + colorstr = Team_ColorCode(source.team); + else + { + colorstr = ""; + teamsay = false; + } + + if(intermission_running) + teamsay = false; + + if (!source) { + colorstr = ""; + teamsay = false; + } + + if(msgin != "") + msgin = trigger_magicear_processmessage_forallears(source, teamsay, privatesay, msgin); + + /* + * using bprint solves this... me stupid + // how can we prevent the message from appearing in a listen server? + // for now, just give "say" back and only handle say_team + if(!teamsay) + { + clientcommand(source, strcat("say ", msgin)); + return; + } + */ + + string namestr = ""; + if (source) + namestr = autocvar_g_chat_teamcolors ? playername(source) : source.netname; + + string colorprefix = (strdecolorize(namestr) == namestr) ? "^3" : "^7"; + + string msgstr, cmsgstr; + string privatemsgprefix = string_null; + int privatemsgprefixlen = 0; + if (msgin == "") { + msgstr = cmsgstr = ""; + } else { + if(privatesay) + { + msgstr = strcat("\{1}\{13}* ", colorprefix, namestr, "^3 tells you: ^7"); + privatemsgprefixlen = strlen(msgstr); + msgstr = strcat(msgstr, msgin); + cmsgstr = strcat(colorstr, colorprefix, namestr, "^3 tells you:\n^7", msgin); + if(autocvar_g_chat_teamcolors) + privatemsgprefix = strcat("\{1}\{13}* ^3You tell ", playername(privatesay), ": ^7"); + else + privatemsgprefix = strcat("\{1}\{13}* ^3You tell ", privatesay.netname, ": ^7"); + } + else if(teamsay) + { + if(strstrofs(msgin, "/me", 0) >= 0) + { + //msgin = strreplace("/me", "", msgin); + //msgin = substring(msgin, 3, strlen(msgin)); + msgin = strreplace("/me", strcat(colorstr, "(", colorprefix, namestr, colorstr, ")^7"), msgin); + msgstr = strcat("\{1}\{13}^4* ", "^7", msgin); + } + else + msgstr = strcat("\{1}\{13}", colorstr, "(", colorprefix, namestr, colorstr, ") ^7", msgin); + cmsgstr = strcat(colorstr, "(", colorprefix, namestr, colorstr, ")\n^7", msgin); + } + else + { + if(strstrofs(msgin, "/me", 0) >= 0) + { + //msgin = strreplace("/me", "", msgin); + //msgin = substring(msgin, 3, strlen(msgin)); + msgin = strreplace("/me", strcat(colorprefix, namestr), msgin); + msgstr = strcat("\{1}^4* ", "^7", msgin); + } + else { + msgstr = "\{1}"; + msgstr = strcat(msgstr, (namestr != "") ? strcat(colorprefix, namestr, "^7: ") : "^7"); + msgstr = strcat(msgstr, msgin); + } + cmsgstr = ""; + } + msgstr = strcat(strreplace("\n", " ", msgstr), "\n"); // newlines only are good for centerprint + } + + string fullmsgstr = msgstr; + string fullcmsgstr = cmsgstr; + + // FLOOD CONTROL + int flood = 0; + var .float flood_field = floodcontrol_chat; + if(floodcontrol) + { + float flood_spl; + float flood_burst; + float flood_lmax; + float lines; + if(privatesay) + { + flood_spl = autocvar_g_chat_flood_spl_tell; + flood_burst = autocvar_g_chat_flood_burst_tell; + flood_lmax = autocvar_g_chat_flood_lmax_tell; + flood_field = floodcontrol_chattell; + } + else if(teamsay) + { + flood_spl = autocvar_g_chat_flood_spl_team; + flood_burst = autocvar_g_chat_flood_burst_team; + flood_lmax = autocvar_g_chat_flood_lmax_team; + flood_field = floodcontrol_chatteam; + } + else + { + flood_spl = autocvar_g_chat_flood_spl; + flood_burst = autocvar_g_chat_flood_burst; + flood_lmax = autocvar_g_chat_flood_lmax; + flood_field = floodcontrol_chat; + } + flood_burst = max(0, flood_burst - 1); + // to match explanation in default.cfg, a value of 3 must allow three-line bursts and not four! + + // do flood control for the default line size + if(msgstr != "") + { + getWrappedLine_remaining = msgstr; + msgstr = ""; + lines = 0; + while(getWrappedLine_remaining && (!flood_lmax || lines <= flood_lmax)) + { + msgstr = strcat(msgstr, " ", getWrappedLineLen(82.4289758859709, strlennocol)); // perl averagewidth.pl < gfx/vera-sans.width + ++lines; + } + msgstr = substring(msgstr, 1, strlen(msgstr) - 1); + + if(getWrappedLine_remaining != "") + { + msgstr = strcat(msgstr, "\n"); + flood = 2; + } + + if (time >= source.(flood_field)) + { + source.(flood_field) = max(time - flood_burst * flood_spl, source.(flood_field)) + lines * flood_spl; + } + else + { + flood = 1; + msgstr = fullmsgstr; + } + } + else + { + if (time >= source.(flood_field)) + source.(flood_field) = max(time - flood_burst * flood_spl, source.(flood_field)) + flood_spl; + else + flood = 1; + } + + if (timeout_status == TIMEOUT_ACTIVE) // when game is paused, no flood protection + source.(flood_field) = flood = 0; + } + + string sourcemsgstr, sourcecmsgstr; + if(flood == 2) // cannot happen for empty msgstr + { + if(autocvar_g_chat_flood_notify_flooder) + { + sourcemsgstr = strcat(msgstr, "\n^3FLOOD CONTROL: ^7message too long, trimmed\n"); + sourcecmsgstr = ""; + } + else + { + sourcemsgstr = fullmsgstr; + sourcecmsgstr = fullcmsgstr; + } + cmsgstr = ""; + } + else + { + sourcemsgstr = msgstr; + sourcecmsgstr = cmsgstr; + } + + if (!privatesay && source && !IS_PLAYER(source)) + { + if (!intermission_running) + if(teamsay || (autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !(warmup_stage || gameover))) + teamsay = -1; // spectators + } + + if(flood) + LOG_INFO("NOTE: ", playername(source), "^7 is flooding.\n"); + + // build sourcemsgstr by cutting off a prefix and replacing it by the other one + if(privatesay) + sourcemsgstr = strcat(privatemsgprefix, substring(sourcemsgstr, privatemsgprefixlen, -1)); + + int ret; + if(source.muted) + { + // always fake the message + ret = -1; + } + else if(flood == 1) + { + if (autocvar_g_chat_flood_notify_flooder) + { + sprint(source, strcat("^3FLOOD CONTROL: ^7wait ^1", ftos(source.(flood_field) - time), "^3 seconds\n")); + ret = 0; + } + else + ret = -1; + } + else + { + ret = 1; + } + + if(sourcemsgstr != "" && ret != 0) + { + if(ret < 0) // faked message, because the player is muted + { + sprint(source, sourcemsgstr); + if(sourcecmsgstr != "" && !privatesay) + centerprint(source, sourcecmsgstr); + } + else if(privatesay) // private message, between 2 people only + { + sprint(source, sourcemsgstr); + sprint(privatesay, msgstr); + if (!autocvar_g_chat_tellprivacy) { dedicated_print(msgstr); } // send to server console too if "tellprivacy" is disabled + if(cmsgstr != "") + centerprint(privatesay, cmsgstr); + } + else if ( teamsay && source.active_minigame ) + { + sprint(source, sourcemsgstr); + dedicated_print(msgstr); // send to server console too + FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && it.active_minigame == source.active_minigame, sprint(it, msgstr)); + } + else if(teamsay > 0) // team message, only sent to team mates + { + sprint(source, sourcemsgstr); + dedicated_print(msgstr); // send to server console too + if(sourcecmsgstr != "") + centerprint(source, sourcecmsgstr); + FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && it != source && it.team == source.team, { + sprint(it, msgstr); + if(cmsgstr != "") + centerprint(it, cmsgstr); + }); + } + else if(teamsay < 0) // spectator message, only sent to spectators + { + sprint(source, sourcemsgstr); + dedicated_print(msgstr); // send to server console too + FOREACH_CLIENT(!IS_PLAYER(it) && IS_REAL_CLIENT(it) && it != source, sprint(it, msgstr)); + } + else + { + if (source) { + sprint(source, sourcemsgstr); + dedicated_print(msgstr); // send to server console too + MX_Say(strcat(playername(source), "^7: ", msgin)); + } + FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source, sprint(it, msgstr)); + } + } + + return ret; +} diff --git a/qcsrc/server/player.qh b/qcsrc/server/player.qh new file mode 100644 index 0000000000..3e2b860162 --- /dev/null +++ b/qcsrc/server/player.qh @@ -0,0 +1,41 @@ +#pragma once + +.entity pusher; +.float pushltime; +.float istypefrag; + +.float CopyBody_nextthink; +.void(entity this) CopyBody_think; +void CopyBody_Think(entity this); +void CopyBody(entity this, float keepvelocity); + +void dedicated_print(string input); + +void player_setupanimsformodel(entity this); + +void player_anim(entity this); + +void PlayerCorpseDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force); + +// g__str: +// If 0, default is used. +// If <0, 0 is used. +// Otherwise, g_str (default value) is used. +// For consistency, negative values there are mapped to zero too. +#define GAMETYPE_DEFAULTED_SETTING(str) \ + ((gametype_setting_tmp = cvar(strcat("g_", GetGametype(), "_" #str))), \ + (gametype_setting_tmp < 0) ? 0 \ + : (gametype_setting_tmp == 0 || autocvar_g_respawn_delay_forced) ? max(0, autocvar_g_##str) \ + : gametype_setting_tmp) + +void calculate_player_respawn_time(entity this); + +void ClientKill_Now_TeamChange(entity this); + +void MoveToTeam(entity client, float team_colour, float type); + +void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force); + +/** to be used by `prvm_edictset server playernumber muted 1` */ +.float muted; +int Say(entity source, float teamsay, entity privatesay, string msgin, float floodcontrol); diff --git a/qcsrc/server/portals.qc b/qcsrc/server/portals.qc index 8a40b11184..12083e423c 100644 --- a/qcsrc/server/portals.qc +++ b/qcsrc/server/portals.qc @@ -1,14 +1,14 @@ #include "portals.qh" #include "g_hook.qh" -#include "mutators/all.qh" +#include "mutators/_mod.qh" #include "../common/constants.qh" #include "../common/deathtypes/all.qh" #include "../common/notifications/all.qh" #include "../common/triggers/teleporters.qh" #include "../common/triggers/subs.qh" #include "../common/util.qh" -#include "../common/weapons/all.qh" +#include #include "../lib/csqcmodel/sv_model.qh" #include "../lib/warpzone/anglestransform.qh" #include "../lib/warpzone/util_server.qh" diff --git a/qcsrc/server/progs.inc b/qcsrc/server/progs.inc index c49b604e69..1000a5105b 100644 --- a/qcsrc/server/progs.inc +++ b/qcsrc/server/progs.inc @@ -1,34 +1,11 @@ -#ifndef DEBUGPATHING - #define DEBUGPATHING 0 -#endif - #include #if XONOTIC - -#include "_all.qh" - -#include "../server/_mod.inc" -#include "bot/_mod.inc" -#include "command/_mod.inc" -#include "mutators/_mod.inc" -#include "pathlib/_all.inc" -#include "weapons/_mod.inc" - -#include -#include - -#include - -#include -#include -#include -#include - +#include #endif -#include +#include -#if BUILD_MOD -#include "../../mod/server/progs.inc" +#ifdef BUILD_MOD +#include #endif diff --git a/qcsrc/server/race.qc b/qcsrc/server/race.qc index b315d6cad4..fc5ba4d61b 100644 --- a/qcsrc/server/race.qc +++ b/qcsrc/server/race.qc @@ -1,6 +1,6 @@ #include "race.qh" -#include "cl_client.qh" +#include "client.qh" #include "portals.qh" #include "scores.qh" #include "spawnpoints.qh" diff --git a/qcsrc/server/scores.qc b/qcsrc/server/scores.qc index e3d5fd9d72..94bea2b44a 100644 --- a/qcsrc/server/scores.qc +++ b/qcsrc/server/scores.qc @@ -1,7 +1,7 @@ #include "scores.qh" #include "command/common.qh" -#include "mutators/all.qh" +#include "mutators/_mod.qh" #include "../common/playerstats.qh" #include "../common/teams.qh" diff --git a/qcsrc/server/scores_rules.qc b/qcsrc/server/scores_rules.qc index d3aceac50b..718ebd2658 100644 --- a/qcsrc/server/scores_rules.qc +++ b/qcsrc/server/scores_rules.qc @@ -1,6 +1,6 @@ #include "scores_rules.qh" -#include "cl_client.qh" +#include "client.qh" #include "scores.qh" int ScoreRules_teams; diff --git a/qcsrc/server/spawnpoints.qc b/qcsrc/server/spawnpoints.qc index 958b0bd6e3..567217933f 100644 --- a/qcsrc/server/spawnpoints.qc +++ b/qcsrc/server/spawnpoints.qc @@ -1,6 +1,6 @@ #include "spawnpoints.qh" -#include "mutators/all.qh" +#include "mutators/_mod.qh" #include "g_world.qh" #include "race.qh" #include "../common/constants.qh" diff --git a/qcsrc/server/steerlib.qc b/qcsrc/server/steerlib.qc index 8064cd9ae1..5312eb8140 100644 --- a/qcsrc/server/steerlib.qc +++ b/qcsrc/server/steerlib.qc @@ -7,10 +7,11 @@ /** Uniform pull towards a point **/ -vector steerlib_pull(entity this, vector point) +#define steerlib_pull(ent,point) normalize(point - (ent).origin) +/*vector steerlib_pull(entity this, vector point) { return normalize(point - this.origin); -} +}*/ /** Uniform push from a point @@ -339,24 +340,30 @@ vector steerlib_traceavoid_flat(entity this, float pitch, float length, vector v return normalize(leftwish + rightwish + frontwish); } -float beamsweep_badpoint(vector point,float waterok) +bool beamsweep_badpoint(vector point, bool waterok) { - float pc,pc2; - if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY) - return 1; - - pc = pointcontents(point); - pc2 = pointcontents(point - '0 0 1'); - - switch(pc) + return true; + + int pc = pointcontents(point); + int pc2 = pointcontents(point - '0 0 1'); + + if(pc == CONTENT_EMPTY && pc2 == CONTENT_SOLID) + return false; + if(pc == CONTENT_EMPTY && pc2 == CONTENT_WATER && waterok) + return false; + if(pc == CONTENT_WATER && waterok) + return false; + return true; + + /*switch(pc) { case CONTENT_SOLID: break; case CONTENT_SLIME: break; case CONTENT_LAVA: break; case CONTENT_SKY: - return 1; + return true; case CONTENT_EMPTY: if (pc2 == CONTENT_SOLID) @@ -375,14 +382,14 @@ float beamsweep_badpoint(vector point,float waterok) break; } - return 1; + return true;*/ } //#define BEAMSTEER_VISUAL float beamsweep(entity this, vector from, vector dir,float length, float step,float step_up, float step_down) { float i; - vector a,b,u,d; + vector a, b, u, d; u = '0 0 1' * step_up; d = '0 0 1' * step_down; @@ -464,8 +471,8 @@ vector steerlib_beamsteer(entity this, vector dir, float length, float step, flo if(bm_left + bm_right < 0.15) { - vr = normalize((v_forward*-1) + v_right * 0.75); - vl = normalize((v_forward*-1) - v_right * 0.75); + vr = normalize((v_forward*-1) + v_right * 0.90); + vl = normalize((v_forward*-1) - v_right * 0.90); bm_right = beamsweep(this, this.origin, vr, length, step, step_up, step_down); bm_left = beamsweep(this, this.origin, vl, length, step, step_up, step_down); diff --git a/qcsrc/server/steerlib.qh b/qcsrc/server/steerlib.qh index fa21610f04..4beb69f632 100644 --- a/qcsrc/server/steerlib.qh +++ b/qcsrc/server/steerlib.qh @@ -4,4 +4,4 @@ vector steerlib_arrive(entity this, vector point, float maximal_distance); vector steerlib_attract2(entity this, vector point, float min_influense, float max_distance, float max_influense); -vector steerlib_pull(entity this, vector point); +//vector steerlib_pull(entity this, vector point); diff --git a/qcsrc/server/sv_main.qc b/qcsrc/server/sv_main.qc index 4a72bb1aee..1bb6974eb0 100644 --- a/qcsrc/server/sv_main.qc +++ b/qcsrc/server/sv_main.qc @@ -8,7 +8,7 @@ #include "command/common.qh" -#include "mutators/all.qh" +#include "mutators/_mod.qh" #include "weapons/csqcprojectile.qh" #include "../common/constants.qh" @@ -18,7 +18,7 @@ #include "../common/util.qh" #include "../common/vehicles/all.qh" -#include "../common/weapons/all.qh" +#include #include "../lib/csqcmodel/sv_model.qh" diff --git a/qcsrc/server/t_quake.qc b/qcsrc/server/t_quake.qc index 8589ecc7df..dac8f19300 100644 --- a/qcsrc/server/t_quake.qc +++ b/qcsrc/server/t_quake.qc @@ -1,6 +1,6 @@ #include "t_quake.qh" -#include "../common/weapons/all.qh" +#include spawnfunc(weapon_electro); spawnfunc(weapon_hagar); diff --git a/qcsrc/server/t_quake3.qc b/qcsrc/server/t_quake3.qc index 1bdb49765d..1e71d33445 100644 --- a/qcsrc/server/t_quake3.qc +++ b/qcsrc/server/t_quake3.qc @@ -1,6 +1,6 @@ #include "t_quake3.qh" -#include "../common/weapons/all.qh" +#include spawnfunc(weapon_crylink); spawnfunc(weapon_electro); diff --git a/qcsrc/server/teamplay.qc b/qcsrc/server/teamplay.qc index 56f85bd468..de7f2f9926 100644 --- a/qcsrc/server/teamplay.qc +++ b/qcsrc/server/teamplay.qc @@ -1,6 +1,6 @@ #include "teamplay.qh" -#include "cl_client.qh" +#include "client.qh" #include "race.qh" #include "scores.qh" #include "scores_rules.qh" @@ -9,10 +9,10 @@ #include "command/vote.qh" -#include "mutators/all.qh" +#include "mutators/_mod.qh" #include "../common/deathtypes/all.qh" -#include "../common/gamemodes/all.qh" +#include "../common/gamemodes/_mod.qh" #include "../common/teams.qh" void TeamchangeFrags(entity e) @@ -158,6 +158,16 @@ string getwelcomemessage(entity this) return s; } +void setcolor(entity this, int clr) +{ +#if 0 + this.clientcolors = clr; + this.team = (clr & 15) + 1; +#else + builtin_setcolor(this, clr); +#endif +} + void SetPlayerColors(entity pl, float _color) { /*string s; @@ -473,8 +483,10 @@ float FindSmallestTeam(entity pl, float ignore_pl) { if(autocvar_g_campaign && pl && IS_REAL_CLIENT(pl)) return 1; // special case for campaign and player joining - else - error(sprintf("Too few teams available for %s\n", MapInfo_Type_ToString(MapInfo_CurrentGametype()))); + else if(totalteams == 1) // single team + LOG_TRACEF("Only 1 team available for %s, you may need to fix your map", MapInfo_Type_ToString(MapInfo_CurrentGametype())); + else // no teams, major no no + error(sprintf("No teams available for %s\n", MapInfo_Type_ToString(MapInfo_CurrentGametype()))); } // count how many players are in each team @@ -540,7 +552,7 @@ int JoinBestTeam(entity this, bool only_return_best, bool forcebestteam) SetPlayerColors(this, selectedteam - 1); // when JoinBestTeam is called by client.qc/ClientKill_Now_TeamChange the players team is -1 and thus skipped - // when JoinBestTeam is called by cl_client.qc/ClientConnect the player_id is 0 the log attempt is rejected + // when JoinBestTeam is called by client.qc/ClientConnect the player_id is 0 the log attempt is rejected LogTeamchange(this.playerid, this.team, 99); } return selectedteam; diff --git a/qcsrc/server/teamplay.qh b/qcsrc/server/teamplay.qh index f34c6d59df..127ac7a6d3 100644 --- a/qcsrc/server/teamplay.qh +++ b/qcsrc/server/teamplay.qh @@ -50,3 +50,5 @@ int JoinBestTeam(entity this, bool only_return_best, bool forcebestteam); //void() ctf_playerchanged; void ShufflePlayerOutOfTeam (float source_team); + +void setcolor(entity this, int clr); diff --git a/qcsrc/server/tests.qh b/qcsrc/server/tests.qh index be6445b48e..e6d6f66a0d 100644 --- a/qcsrc/server/tests.qh +++ b/qcsrc/server/tests.qh @@ -1,11 +1,11 @@ #pragma once #include "autocvars.qh" -#include "cl_client.qh" -#include "command/all.qh" +#include "client.qh" +#include "command/_mod.qh" #include "weapons/common.qh" #include "weapons/selection.qh" #include #include -#include +#include #include diff --git a/qcsrc/server/weapons/accuracy.qc b/qcsrc/server/weapons/accuracy.qc index 9efc4ce6a0..ed9006bb25 100644 --- a/qcsrc/server/weapons/accuracy.qc +++ b/qcsrc/server/weapons/accuracy.qc @@ -1,10 +1,10 @@ #include "accuracy.qh" -#include "../mutators/all.qh" +#include "../mutators/_mod.qh" #include #include #include -#include +#include int accuracy_byte(float n, float d) { diff --git a/qcsrc/server/weapons/common.qc b/qcsrc/server/weapons/common.qc index 954f82564f..03031c1580 100644 --- a/qcsrc/server/weapons/common.qc +++ b/qcsrc/server/weapons/common.qc @@ -5,8 +5,8 @@ #include #include #include -#include -#include +#include +#include void W_GiveWeapon(entity e, int wep) { diff --git a/qcsrc/server/weapons/csqcprojectile.qc b/qcsrc/server/weapons/csqcprojectile.qc index caa367e5d7..a8a893e217 100644 --- a/qcsrc/server/weapons/csqcprojectile.qc +++ b/qcsrc/server/weapons/csqcprojectile.qc @@ -5,7 +5,7 @@ #include "../command/common.qh" #include -#include +#include .float csqcprojectile_type; diff --git a/qcsrc/server/weapons/hitplot.qc b/qcsrc/server/weapons/hitplot.qc index ec1fd089b1..372f7357b4 100644 --- a/qcsrc/server/weapons/hitplot.qc +++ b/qcsrc/server/weapons/hitplot.qc @@ -2,7 +2,7 @@ #include "../antilag.qh" #include "../g_subs.qh" -#include +#include #include vector W_HitPlotUnnormalizedUntransform(vector screenforward, vector screenright, vector screenup, vector v) diff --git a/qcsrc/server/weapons/selection.qc b/qcsrc/server/weapons/selection.qc index ff5eaa59a4..8c6c0eaac6 100644 --- a/qcsrc/server/weapons/selection.qc +++ b/qcsrc/server/weapons/selection.qc @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include diff --git a/qcsrc/server/weapons/spawning.qc b/qcsrc/server/weapons/spawning.qc index faec22f307..bb204a4b54 100644 --- a/qcsrc/server/weapons/spawning.qc +++ b/qcsrc/server/weapons/spawning.qc @@ -1,9 +1,9 @@ #include "spawning.qh" #include "weaponsystem.qh" -#include "../mutators/all.qh" +#include "../mutators/_mod.qh" #include -#include +#include string W_Apply_Weaponreplace(string in) { diff --git a/qcsrc/server/weapons/throwing.qc b/qcsrc/server/weapons/throwing.qc index 7cc7580c17..c06b90ec2d 100644 --- a/qcsrc/server/weapons/throwing.qc +++ b/qcsrc/server/weapons/throwing.qc @@ -1,7 +1,7 @@ #include "throwing.qh" #include "weaponsystem.qh" -#include "../mutators/all.qh" +#include "../mutators/_mod.qh" #include #include "../g_damage.qh" #include @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include void thrown_wep_think(entity this) diff --git a/qcsrc/server/weapons/tracing.qc b/qcsrc/server/weapons/tracing.qc index f773180fea..176b69e6b6 100644 --- a/qcsrc/server/weapons/tracing.qc +++ b/qcsrc/server/weapons/tracing.qc @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include diff --git a/qcsrc/server/weapons/weaponstats.qc b/qcsrc/server/weapons/weaponstats.qc index 2b4be7e917..2ffb1c1ec8 100644 --- a/qcsrc/server/weapons/weaponstats.qc +++ b/qcsrc/server/weapons/weaponstats.qc @@ -2,7 +2,7 @@ #include "../g_world.qh" -#include +#include void WeaponStats_Init() { diff --git a/qcsrc/server/weapons/weaponsystem.qc b/qcsrc/server/weapons/weaponsystem.qc index c0ed81e681..13f32ee14d 100644 --- a/qcsrc/server/weapons/weaponsystem.qc +++ b/qcsrc/server/weapons/weaponsystem.qc @@ -3,7 +3,7 @@ #include "selection.qh" #include "../command/common.qh" -#include "../mutators/all.qh" +#include "../mutators/_mod.qh" #include "../round_handler.qh" #include #include @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include @@ -412,6 +412,7 @@ bool forbidWeaponUse(entity player) if (gameover) return true; if (STAT(FROZEN, player)) return true; if (player.weapon_blocked) return true; + if (MUTATOR_CALLHOOK(ForbidWeaponUse, player)) return true; return false; } diff --git a/qcsrc/server/weapons/weaponsystem.qh b/qcsrc/server/weapons/weaponsystem.qh index d0ba634121..b7ca597046 100644 --- a/qcsrc/server/weapons/weaponsystem.qh +++ b/qcsrc/server/weapons/weaponsystem.qh @@ -8,7 +8,7 @@ void CL_SpawnWeaponentity(entity e, .entity weaponentity); vector CL_Weapon_GetShotOrg(float wpn); -float forbidWeaponUse(entity player); +bool forbidWeaponUse(entity player); void W_AttachToShotorg(entity actor, .entity weaponentity, entity flash, vector offset); diff --git a/qcsrc/tools/auto-super.pl b/qcsrc/tools/auto-super.pl new file mode 100644 index 0000000000..00926d0617 --- /dev/null +++ b/qcsrc/tools/auto-super.pl @@ -0,0 +1,101 @@ +my %classoffile = (); +my %classes = (); +my %baseclass = (); +my %methods = (); +my %attrs = (); +my %methodnames = (); +my %old2new = (); + +print STDERR "Scanning...\n"; +for my $f(@ARGV) +{ + open my $fh, '<', $f; + while(<$fh>) + { + if(/^CLASS\(([^)]*)\)(?:\s*EXTENDS\(([^)]*)\))?/) + { + $classes{$1} = defined($2) ? $2 : "Object"; + $classoffile{$f} = $1; + } + if(/^\s*METHOD\(([^),]*),\s*([^),]*)/) + { + $methods{$1}{$2} = $1; + $methodnames{"$1"."_"."$2"} = $f; + $old2new{"$2$1"} = "$1"."_"."$2"; + } + if(/^\s*ATTRIB(?:ARRAY)?\(([^),]*),\s*([^),]*)/) + { + $attrs{$1}{$2} = $1; + } + } + close $fh; +} + +# propagate down methods etc. +print STDERR "Propagating...\n"; +for my $class(keys %classes) +{ + print STDERR "$class"; + my $base = $class; + for(;;) + { + $base = $classes{$base}; + last if not defined $base; + print STDERR " -> $base"; + while(my ($method, $definingclass) = each %{$methods{$base}}) + { + $methods{$class}{$method} = $definingclass + if not defined $methods{$class}{$method}; + } + while(my ($attr, $definingclass) = each %{$attrs{$base}}) + { + $attrs{$class}{$attr} = $definingclass + if not defined $attrs{$class}{$attr}; + } + } + print STDERR "\n"; +} + +# change all calls to base method to super, complain about skipping +print STDERR "Fixing...\n"; +for my $f(@ARGV) +{ + open my $fh, '<', $f; + my $s = do { undef local $/; <$fh>; }; + my $s0 = $s; + close $fh; + + my $class = $classoffile{$f}; + my $base = $classes{$class}; + next if not defined $base; + + for(keys %old2new) + { + $s =~ s/\b$_\b/$old2new{$_}/g; + } + + my @methods_super = map { [ $methods{$base}{$_} . "_" . $_, "SUPER($class).$_" ]; } keys %{$methods{$base}}; + for(@methods_super) + { + my ($search, $replace) = @$_; + my $n = ($s =~ s/\b$search\b/$replace/g); + print STDERR "[$f] $search -> $replace... $n replacements\n" + if $n; + } + + for(grep { $methodnames{$_} ne $f } keys %methodnames) + { + if($s =~ /\b$_\b/) + { + print STDERR "[$f] calls non-super external method directly: $_\n"; + } + } + + if($s ne $s0) + { + print STDERR "Rewriting $f...\n"; + open my $fh, '>', $f; + print $fh $s; + close $fh; + } +} diff --git a/qcsrc/tools/compilationunits.sh b/qcsrc/tools/compilationunits.sh index 2fa17825ec..ebb232105f 100755 --- a/qcsrc/tools/compilationunits.sh +++ b/qcsrc/tools/compilationunits.sh @@ -11,7 +11,6 @@ declare -a QCCDEFS=( -DNDEBUG=1 -DXONOTIC=1 -DWATERMARK="\"$(git describe --tags --dirty='~')\"" - -DDEBUGPATHING=0 ) QCCDEFS="${QCCDEFS[@]}" @@ -35,23 +34,22 @@ QCCFLAGS="${QCCFLAGS[@]} ${NOWARN[@]}" cd .. function check1() { - declare -l base="${1}" - MODE=${2} - declare -l file="${3}" + declare -l prog="${1}" + declare -l file="${2}" + MODE=${prog} qpp ${file} test.dat \ - -include lib/_all.inc -include ${base}/_all.qh \ - -I. ${QCCIDENT} ${QCCDEFS} -D${MODE} > ${WORKDIR}/${MODE}.qc - qcc ${QCCFLAGS} -o ../${WORKDIR}/test.dat ../${WORKDIR}/${MODE}.qc >/dev/null + -include lib/_all.inc -include ${prog}/_all.qh \ + -I. ${QCCIDENT} ${QCCDEFS} > ${WORKDIR}/${prog}.qc + qcc ${QCCFLAGS} -o ../${WORKDIR}/test.dat ../${WORKDIR}/${prog}.qc >/dev/null } function check() { - declare -l base="${1}" - MODE=${2} - find ${base} -type f -name '*.qc' -print0 | sort -z | while read -r -d '' file; do - check1 ${base} ${MODE} ${file} + declare -l prog="${1}" + find ${prog} -type f -name '*.qc' -print0 | sort -z | while read -r -d '' file; do + check1 ${prog} ${file} done } -check client CSQC -check server SVQC -check menu MENUQC +check client +check server +check menu diff --git a/qcsrc/tools/genmod.sh b/qcsrc/tools/genmod.sh index 2c34e67134..9a3ba10987 100755 --- a/qcsrc/tools/genmod.sh +++ b/qcsrc/tools/genmod.sh @@ -12,21 +12,36 @@ function genmod() { echo '// generated file; do not modify' > ${MOD}.inc echo '// generated file; do not modify' > ${MOD}.qh for f in $(ls | sort -k 1,1 -t .); do - if [[ "$f" == cl_* ]]; then if [[ -f "${f#cl_}" ]]; then continue; fi; fi - if [[ "$f" == sv_* ]]; then if [[ -f "${f#sv_}" ]]; then continue; fi; fi - if [[ "$f" == ui_* ]]; then if [[ -f "${f#ui_}" ]]; then continue; fi; fi + if [[ "$f" == cl_* ]]; then f="${f#cl_}"; if [[ -f "$f" ]]; then continue; fi + elif [[ "$f" == sv_* ]]; then f="${f#sv_}"; if [[ -f "$f" ]]; then continue; fi + elif [[ "$f" == ui_* ]]; then f="${f#ui_}"; if [[ -f "$f" ]]; then continue; fi + fi if [[ "$f" == *.qc ]]; then - echo "#include <${CTX}$f>" >> ${MOD}.inc - echo "#include <${CTX}${f%.qc}.qh>" >> ${MOD}.qh + if [[ -f "$f" ]]; then echo -e "#include <${CTX}$f>" >> ${MOD}.inc; fi + if [[ -f "${f%.qc}.qh" ]]; then echo -e "#include <${CTX}${f%.qc}.qh>" >> ${MOD}.qh; fi if [[ -f "cl_$f" ]]; then echo -e "#ifdef CSQC\n #include <${CTX}cl_$f>\n#endif" >> ${MOD}.inc; fi + if [[ -f "cl_${f%.qc}.qh" ]]; then echo -e "#ifdef CSQC\n #include <${CTX}cl_${f%.qc}.qh>\n#endif" >> ${MOD}.qh; fi if [[ -f "sv_$f" ]]; then echo -e "#ifdef SVQC\n #include <${CTX}sv_$f>\n#endif" >> ${MOD}.inc; fi + if [[ -f "sv_${f%.qc}.qh" ]]; then echo -e "#ifdef SVQC\n #include <${CTX}sv_${f%.qc}.qh>\n#endif" >> ${MOD}.qh; fi if [[ -f "ui_$f" ]]; then echo -e "#ifdef MENUQC\n #include <${CTX}ui_$f>\n#endif" >> ${MOD}.inc; fi + if [[ -f "ui_${f%.qc}.qh" ]]; then echo -e "#ifdef MENUQC\n #include <${CTX}ui_${f%.qc}.qh>\n#endif" >> ${MOD}.qh; fi fi done - # echo >> ${MOD} + declare -l rec=1 + if [[ -f "_all.inc" ]]; then rec=0; fi for f in *; do if [ -d "$f" ]; then (cd -- "$f" && genmod) - # echo "#include \"$f/MOD\"" >> ${MOD} + if [[ $rec == 1 ]]; then + rec=2 + echo >> ${MOD}.inc + echo >> ${MOD}.qh + fi + if [[ $rec != 0 ]]; then + declare -l mod=_mod + if [[ -f "$f/_all.inc" ]]; then mod=_all; fi + echo "#include <${CTX}$f/${mod}.inc>" >> ${MOD}.inc + echo "#include <${CTX}$f/${mod}.qh>" >> ${MOD}.qh + fi fi; done } diff --git a/qcsrc/tools/headerstyle.sh b/qcsrc/tools/headerstyle.sh index daf20ebf3b..924083166f 100755 --- a/qcsrc/tools/headerstyle.sh +++ b/qcsrc/tools/headerstyle.sh @@ -17,7 +17,7 @@ function check() { find "$base" -type f -name '*.qc' -print0 | sort -z | while read -r -d '' file; do echo "$file" declare -l file_h="${file%.qc}.qh" - if [ ! -f "$file_h" ]; then echo "#pragma once" > "$file_h"; fi + if [[ ! -f "$file_h" ]]; then echo "#pragma once" > "$file_h"; fi include=$(basename "$file") include="${include%.qc}.qh" @@ -33,3 +33,4 @@ function check() { check client check server check menu +check common diff --git a/qcsrc/tools/qcc.sh b/qcsrc/tools/qcc.sh index b115c196ec..9951ec2fd7 100755 --- a/qcsrc/tools/qcc.sh +++ b/qcsrc/tools/qcc.sh @@ -12,15 +12,23 @@ QCCFLAGS=${QCCFLAGS} function qpp() { IN=$1 OUT=$2 - >&2 echo + ${CPP} ${@:3} ${IN} + case ${MODE} in + client) DEFS="-DGAMEQC -DCSQC" + ;; + menu) DEFS="-DMENUQC" + ;; + server) DEFS="-DGAMEQC -DSVQC" + ;; + esac + >&2 echo + ${CPP} ${@:3} ${DEFS} ${IN} set +e # additional information - ${CPP} ${@:3} \ + ${CPP} ${@:3} ${DEFS} \ -dM 1>${WORKDIR}/${MODE}_macros.txt \ -H 2>${WORKDIR}/${MODE}_includes.txt \ ${IN} # main step - ${CPP} ${@:3} -MMD -MP -MT ${OUT} -Wall -Wundef -Werror ${IN} -o ${WORKDIR}/${MODE}.txt + ${CPP} ${@:3} ${DEFS} -MMD -MP -MT ${OUT} -Wall -Wundef -Werror ${IN} -o ${WORKDIR}/${MODE}.txt err=$? set -e if [ ${err} -ne 0 ]; then return ${err}; fi @@ -37,16 +45,6 @@ $(return >/dev/null 2>&1) || { MODE=$1 OUT=$2 IN=$3 - - case ${MODE} in - client) PROG=CSQC - ;; - menu) PROG=MENUQC - ;; - server) PROG=SVQC - ;; - esac - - qpp ${IN} ${OUT} -I. ${QCCIDENT} ${QCCDEFS} -D${PROG} > ${WORKDIR}/${MODE}.qc + qpp ${IN} ${OUT} -I. ${QCCIDENT} ${QCCDEFS} > ${WORKDIR}/${MODE}.qc qcc ${QCCFLAGS} -o ${OUT} ../${WORKDIR}/${MODE}.qc }