]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge remote-tracking branch 'origin/master' into divVerent/csad
authorSamual Lenks <samual@xonotic.org>
Sat, 9 Feb 2013 02:20:02 +0000 (21:20 -0500)
committerSamual Lenks <samual@xonotic.org>
Sat, 9 Feb 2013 02:20:02 +0000 (21:20 -0500)
49 files changed:
_hud_common.cfg
bf.cfg [new file with mode: 0644]
defaultXonotic.cfg
qcsrc/Makefile
qcsrc/client/Main.qc
qcsrc/client/View.qc
qcsrc/client/autocvars.qh
qcsrc/menu/xonotic/dialog_settings_input.c
qcsrc/menu/xonotic/keybinder.c
qcsrc/server/attic/domination.qc [new file with mode: 0644]
qcsrc/server/autocvars.qh
qcsrc/server/bot/havocbot/roles.qc
qcsrc/server/bot/waypoints.qc
qcsrc/server/cl_client.qc
qcsrc/server/cl_physics.qc
qcsrc/server/cl_weapons.qc
qcsrc/server/command/sv_cmd.qc
qcsrc/server/command/vote.qc
qcsrc/server/domination.qc [deleted file]
qcsrc/server/g_world.qc
qcsrc/server/miscfunctions.qc
qcsrc/server/mutators/base.qc
qcsrc/server/mutators/base.qh
qcsrc/server/mutators/gamemode_ctf.qc
qcsrc/server/mutators/gamemode_domination.qc [new file with mode: 0644]
qcsrc/server/mutators/gamemode_domination.qh [new file with mode: 0644]
qcsrc/server/mutators/gamemode_freezetag.qc
qcsrc/server/mutators/gamemode_keepaway.qc
qcsrc/server/mutators/gamemode_keyhunt.qc
qcsrc/server/mutators/gamemode_nexball.qc
qcsrc/server/mutators/gamemode_onslaught.qc
qcsrc/server/mutators/mutator_dodging.qc
qcsrc/server/mutators/mutator_new_toys.qc
qcsrc/server/mutators/mutator_nix.qc
qcsrc/server/mutators/mutator_physical_items.qc [new file with mode: 0644]
qcsrc/server/mutators/mutator_superspec.qc
qcsrc/server/mutators/mutators.qh
qcsrc/server/mutators/sandbox.qc
qcsrc/server/playerstats.qc
qcsrc/server/playerstats.qh
qcsrc/server/progs.src
qcsrc/server/race.qc
qcsrc/server/scores.qc
qcsrc/server/scores.qh
qcsrc/server/scores_rules.qc
qcsrc/server/t_items.qc
qcsrc/server/teamplay.qc
qcsrc/server/vehicles/vehicles_def.qh
qcsrc/server/w_minstanex.qc

index 5179bb2abfcddf226f547c457d0877e39d78f5b7..ba88a2a4fa6fedcd4f2343caeb2e8b1445b1893e 100644 (file)
@@ -35,6 +35,7 @@ alias hud_panel_radar_maximized "cl_cmd hud radar"
 // other hud cvars
 seta hud_showbinds 1   "what to show in the HUD to indicate certain keys to press: 0 display commands, 1 bound keys, 2 both"
 seta hud_showbinds_limit 2     "maximum number of bound keys to show for a command. 0 for unlimited"
+set _hud_showbinds_reload 0    "set it to 1 to reload binds if you changed any. It is reset to 0 automatically"
 
 seta hud_colorflash_alpha 0.5 "starting alpha of the color flash"
 
diff --git a/bf.cfg b/bf.cfg
new file mode 100644 (file)
index 0000000..6445def
--- /dev/null
+++ b/bf.cfg
@@ -0,0 +1,98 @@
+// DP console is TURING COMPLETE!
+
+alias _bf_vcall "${$1} ${2- ?}"
+
+// number system
+// Xon RPN: set $_bf_zero 0
+// Xon RPN: alias _bf_inc "rpn /$1 $2 1 add def"
+// Xon RPN: alias _bf_dec "rpn /$1 $2 1 sub def"
+
+// unary
+// decimal | unary
+//       0 | 0
+//       1 | +
+//       2 | ++
+//      -1 | -
+//      -2 | --
+alias _bf_zero "set $1 0"
+alias _bf_one "set $1 +"
+alias _bf_minus_one "set $1 -"
+alias _bf_return_3 "set $1 $3"
+alias _bf_return_4 "set $1 $4"
+set _bf_zero "0"
+alias _bf_inc_loop1 "set _bf_tmp_$3 _bf_inc_loop2; set _bf_tmp_+$2 _bf_return_3; _bf_vcall _bf_tmp_$3 $*"
+alias _bf_inc_loop2 "set _bf_tmp_$2 _bf_inc_loop3; set _bf_tmp_-$4 _bf_return_4; _bf_vcall _bf_tmp_$2 $*"
+alias _bf_inc_loop3 "_bf_inc_loop1 $1 $2 +$3 -$4"
+alias _bf_inc "set _bf_tmp_$2 _bf_inc_loop1; set _bf_tmp_0 _bf_one; set _bf_tmp_- _bf_zero; _bf_vcall _bf_tmp_$2 $1 $2 + -"
+alias _bf_dec_loop1 "set _bf_tmp_$3 _bf_dec_loop2; set _bf_tmp_-$2 _bf_return_3; _bf_vcall _bf_tmp_$3 $*"
+alias _bf_dec_loop2 "set _bf_tmp_$2 _bf_dec_loop3; set _bf_tmp_+$4 _bf_return_4; _bf_vcall _bf_tmp_$2 $*"
+alias _bf_dec_loop3 "_bf_dec_loop1 $1 $2 -$3 +$4"
+alias _bf_dec "set _bf_tmp_$2 _bf_dec_loop1; set _bf_tmp_0 _bf_minus_one; set _bf_tmp_+ _bf_zero; _bf_vcall _bf_tmp_$2 $1 $2 - +"
+// end of unary
+
+// interpreter state
+set bf_input ""
+alias _bf_clearstate "set _bf_left \"\"; set _bf_right \"\"; set _bf_register $_bf_zero; set _bf_execstack \"\""
+alias _bf_dumpstate "echo rev($_bf_left) < $_bf_register > $_bf_right"
+
+// a STACK!
+alias _bf_pop_get "set $2 \"${3 ?}\"; set $1 \"${4- ?}\""
+alias _bf_pop_dispatch "_bf_pop_get $1 $2 $_bf_popstack_"
+// usage: _bf_pop stackvar outvar defaultvalue
+alias _bf_pop "set _bf_popstack_ \"${$1 ?}\"; set \"_bf_popstack_${$1 ?}\" \"${3 ?}\"; _bf_pop_dispatch $*"
+// usage: _bf_push stackvar value
+alias _bf_push "set $1 \"$2 ${$1 ?}\""
+
+// skip mode: skip until matching ] (1 on _bf_execstack), then continue executing
+alias _bf_skip_ "echo PROGRAMM FELL OFF THE EDGE"
+alias _bf_skip_+ "_bf_skip_${* ?}"
+alias _bf_skip_- "_bf_skip_${* ?}"
+alias _bf_skip_< "_bf_skip_${* ?}"
+alias _bf_skip_> "_bf_skip_${* ?}"
+alias _bf_skip_. "_bf_skip_${* ?}"
+alias _bf_skip_, "_bf_skip_${* ?}"
+alias _bf_skip_endloop_0 "_bf_skip_${* ?}" // continue skipping
+alias _bf_skip_endloop_1 "bf_${* ?}" // back to execution
+alias _bf_skip_endloop_dispatch "_bf_skip_endloop_$_bf_stackval ${* ?}"
+alias _bf_skip_] "_bf_pop _bf_execstack _bf_stackval; _bf_skip_endloop_dispatch ${* ?}"
+alias _bf_skip_[ "_bf_push _bf_execstack 0; _bf_skip_${* ?}"
+// enter
+alias _bf_skiploop "_bf_push _bf_execstack 1; _bf_skip_${* ?}"
+
+// run mode: execute until matching ] (1 on _bf_execstack), then exit
+alias bf_ "echo PROGRAMM FELL OFF THE EDGE"
+alias bf_+ "_bf_inc _bf_register $_bf_register; bf_${* ?}"
+alias bf_- "_bf_dec _bf_register $_bf_register; bf_${* ?}"
+alias bf_< "_bf_push _bf_left $_bf_register; _bf_pop _bf_right _bf_register $_bf_zero; bf_${* ?}"
+alias bf_> "_bf_push _bf_right $_bf_register; _bf_pop _bf_left _bf_register $_bf_zero; bf_${* ?}"
+alias bf_. "echo $_bf_register; bf_${* ?}"
+// note: on EOF, we don't change the register value!
+alias _bf_input_get "set _bf_register $_bf_inputval; bf_${* ?}" // read input
+alias _bf_input_eof "bf_${* ?}" // at EOF, just continue
+alias _bf_input_dispatch "set _bf_inputval_ _bf_input_get; set _bf_inputval_$_bf_inputval _bf_input_eof; _bf_vcall _bf_inputval_ ${* ?}"
+alias bf_, "_bf_pop bf_input _bf_inputval; _bf_input_dispatch ${* ?}"
+alias _bf_endloop_0 "echo IN SKIP MODE, EXCEPT NOT"
+alias _bf_endloop_1 "" // back to caller
+alias _bf_endloop_ "echo PROGRAMM FELL OFF THE EDGE"
+alias _bf_endloop_dispatch "_bf_endloop_$_bf_stackval ${* ?}"
+alias bf_] "_bf_pop _bf_execstack _bf_stackval; _bf_endloop_dispatch ${* ?}"
+// enter
+alias _bf_runloop "_bf_push _bf_execstack 1; bf_$*; bf_[ ${* ?}"
+// loop dispatcher
+alias bf_[ "set _bf_runloop_$_bf_zero _bf_runloop; set _bf_runloop_$_bf_register _bf_skiploop; _bf_vcall _bf_runloop_$_bf_zero ${* ?}"
+
+// start it
+alias bf_exec "_bf_clearstate; _bf_push _bf_execstack 1; bf_$1 ]"
+
+// "cat"
+// Xon RPN: bf_input "1 2 3 4 5"
+bf_input "+ ++ +++ ++++ +++++"
+bf_exec "[ - ] - , + [ - . + [ - ] - , + ]"
+
+// output 42
+// Xon RPN: bf_input "12 6 9"
+bf_input "++++++++++++ ++++++ +++++++++"
+bf_exec ", > , > , < [ > [ - > + > + < < ] > > [ - < < + > > ] < < < - ] < [ - > > > - < < < ] > > > ."
+
+// hello world
+bf_exec "+ + + + + + + + + + [ > + + + + + + + > + + + + + + + + + + > + + + > + < < < < - ] > + + . > + . + + + + + + + . . + + + . > + + . < < + + + + + + + + + + + + + + + . > . + + + . - - - - - - . - - - - - - - - . > + . > ."
index 6e856e986bf9d93e77a109aa5903c5055bedeaf0..c7edacdb726f2abc452741d3745967558ada5d33 100644 (file)
@@ -62,6 +62,9 @@ seta cl_velocityzoom 0        "velocity based zooming of fov, negative values zoom out"
 seta cl_velocityzoom_type 3 "how to factor in speed, 1 = all velocity in all directions, 2 = velocity only in forward direction (can be negative), 3 = velocity only in forward direction (limited to forward only)"
 seta cl_velocityzoom_speed 1000 "target speed for fov factoring"
 seta cl_velocityzoom_time 0.2  "time value for averaging speed values"
+seta cl_spawnzoom 1 "zoom effect immediately when a player spawns"
+seta cl_spawnzoom_speed 1 "speed at which zooming occurs while spawning"
+seta cl_spawnzoom_factor 2 "factor of zoom while spawning"
 seta cl_zoomfactor 5   "how much +zoom will zoom (1-16)"
 seta cl_zoomspeed 8    "how fast it will zoom (0.5-16), negative values mean instant zoom"
 seta cl_zoomsensitivity 0      "how zoom changes sensitivity (0 = weakest, 1 = strongest)"
@@ -176,6 +179,9 @@ set cl_hitsound_antispam_time 0.05 "don't play the hitsound more often than this
 seta cl_eventchase_death 1 "camera goes into 3rd person mode when the player is dead"
 seta cl_eventchase_distance 140 "final camera distance"
 seta cl_eventchase_speed 1.3 "how fast the camera slides back, 0 is instant"
+seta cl_eventchase_maxs "12 12 8" "max size of eventchase camera bbox"
+seta cl_eventchase_mins "-12 -12 -8" "min size of eventchase camera bbox"
+seta cl_eventchase_viewoffset "0 0 20" "viewoffset of eventchase camera"
 
 //nifreks lockonrestart feature, used in team-based game modes, if set to 1 and all players readied up no other player can then join the game anymore, useful to block spectators from joining
 set teamplay_lockonrestart 0 "it set to 1 in a team-based game, the teams are locked once all players readied up and the game restarted (no new players can join after restart unless using the server-command unlockteams)"
@@ -1066,6 +1072,10 @@ alias sethostname "set menu_use_default_hostname 0; hostname $*"
 
 set sv_foginterval 1 "force enable fog in regular intervals"
 
+set g_physical_items 0 "1 uses ODE physics for dropped weapons, 2 for all items, requires physics_ode to be enabled"
+set g_physical_items_damageforcescale 3 "how affected physical weapons are by damage"
+set g_physical_items_reset 1 "return map items to their original lotation after being picked up"
+
 // Audio track names (for old-style "cd loop NUMBER" usage)
 set _cdtrack_first "1"
 alias _cdtrack_0 "g_cdtracks_remaplist \"$g_cdtracks_remaplist $1\""
index 9fa80f8f884b8f215c249e0e9ce64a1d0f4a7a80..5d079dcb05d7d62c96810b82204005d3a1f9543e 100644 (file)
@@ -1,7 +1,7 @@
 SCM := $(shell if [ -d .svn ]; then echo svn; elif [ -d ../.git ]; then echo git; fi)
 PERL ?= perl
 QCCFLAGS_WATERMARK ?= -DWATERMARK='"$(shell git describe)"' -DCVAR_POPCON=1
-QCC ?= fteqcc
+QCC ?= gmqcc
 
 VERSION_MESSAGE = $(shell cd server && $(QCC) --version --help)
 ifneq (,$(findstring GMQCC,$(VERSION_MESSAGE)))
index b75fc166e585359c982d319e8bf1de0c168e60d7..57cf97c998889ce4576be0b839a5d8618e50da53 100644 (file)
@@ -1100,7 +1100,7 @@ void Net_ReadRace()
 void Net_ReadSpawn()
 {
        zoomin_effect = 1;
-       current_viewzoom = 0.6;
+       current_viewzoom = (1 / bound(1, autocvar_cl_spawnzoom_factor, 16));
 }
 
 void Net_TeamNagger()
@@ -1255,7 +1255,8 @@ string getcommandkey(string text, string command)
                                        keys = strcat(keys, ", ", keynumtostring(k));
 
                                ++l;
-                               if (autocvar_hud_showbinds_limit > 0 && autocvar_hud_showbinds_limit >= l) break;
+                               if (autocvar_hud_showbinds_limit > 0 && autocvar_hud_showbinds_limit <= l)
+                                       break;
                        }
 
                }
index 940f810573f3283f007b310855df39813e939c8f..8206c1f3a7a541129bb8f8f42b58a3fc3a0e1659 100644 (file)
@@ -104,7 +104,7 @@ vector GetCurrentFov(float fov)
                zoomfactor = 2.5;
        zoomspeed = autocvar_cl_zoomspeed;
        if(zoomspeed >= 0)
-               if(zoomspeed < 0.5 || zoomspeed > 16)
+       if(zoomspeed < 0.5 || zoomspeed > 16)
                        zoomspeed = 3.5;
 
        zoomdir = button_zoom;
@@ -123,13 +123,20 @@ vector GetCurrentFov(float fov)
                // fteqcc failed twice here already, don't optimize this
        }
 
-       if(zoomdir)
-               zoomin_effect = 0;
+       if(zoomdir) { zoomin_effect = 0; }
 
-       if(zoomin_effect || camera_active)
+       if(camera_active)
        {
                current_viewzoom = min(1, current_viewzoom + drawframetime);
        }
+       else if(autocvar_cl_spawnzoom && zoomin_effect)
+       {
+               float spawnzoomfactor = bound(1, autocvar_cl_spawnzoom_factor, 16);
+               
+               current_viewzoom += (autocvar_cl_spawnzoom_speed * (spawnzoomfactor - current_viewzoom) * drawframetime); 
+               current_viewzoom = bound(1 / spawnzoomfactor, current_viewzoom, 1);
+               if(current_viewzoom == 1) { zoomin_effect = 0; }
+       }
        else
        {
                if(zoomspeed < 0) // instant zoom
@@ -392,6 +399,13 @@ void CSQC_UpdateView(float w, float h)
 
        hud = getstati(STAT_HUD);
 
+       if(autocvar__hud_showbinds_reload) // menu can set this one
+       {
+               db_close(binddb);
+               binddb = db_create();
+               cvar_set("_hud_showbinds_reload", "0");
+       }
+
        if(checkextension("DP_CSQC_MINFPS_QUALITY"))
                view_quality = getproperty(VF_MINFPS_QUALITY);
        else
@@ -443,12 +457,12 @@ void CSQC_UpdateView(float w, float h)
                if(spectatee_status >= 0 && (autocvar_cl_eventchase_death && getstati(STAT_HEALTH) <= 0 && !intermission) || intermission)
                {
                        // make special vector since we can't use view_origin (It is one frame old as of this code, it gets set later with the results this code makes.)
-                       vector current_view_origin = getpropertyvec(VF_ORIGIN);
+                       vector current_view_origin = ((csqcplayer ? csqcplayer.origin : pmove_org) + autocvar_cl_eventchase_viewoffset);
 
                        // We must enable chase_active to get a third person view (weapon viewmodel hidden and own player model showing).
                        // Ideally, there should be another way to enable third person cameras, such as through setproperty()
-                       if(!autocvar_chase_active)
-                               cvar_set("chase_active", "-1"); // -1 enables chase_active while marking it as set by this code, and not by the user (which would be 1)
+                       // -1 enables chase_active while marking it as set by this code, and not by the user (which would be 1)
+                       if(!autocvar_chase_active) { cvar_set("chase_active", "-1"); }
 
                        // make the camera smooth back
                        if(autocvar_cl_eventchase_speed && eventchase_current_distance < autocvar_cl_eventchase_distance)
@@ -456,17 +470,20 @@ void CSQC_UpdateView(float w, float h)
                        else if(eventchase_current_distance != autocvar_cl_eventchase_distance)
                                eventchase_current_distance = autocvar_cl_eventchase_distance;
 
-                       vector eventchase_target_origin;
                        makevectors(view_angles);
-                       // pass 1, used to check where the camera would go and obtain the trace_fraction
-                       eventchase_target_origin = current_view_origin - v_forward * eventchase_current_distance;
-                       WarpZone_TraceLine(current_view_origin, eventchase_target_origin, MOVE_WORLDONLY, self);
-                       // pass 2, also multiplying view_forward with trace_fraction, to prevent the camera from going through walls
-                       // The 0.1 subtraction is to not limit the camera precisely at the wall surface, as that allows the view to poke through
-                       eventchase_target_origin = current_view_origin - v_forward * eventchase_current_distance * (trace_fraction - 0.1);
-                       WarpZone_TraceLine(current_view_origin, eventchase_target_origin, MOVE_WORLDONLY, self);
-
-                       setproperty(VF_ORIGIN, trace_endpos);
+
+                       vector eventchase_target_origin = (current_view_origin - (v_forward * eventchase_current_distance));
+                       WarpZone_TraceBox(current_view_origin, autocvar_cl_eventchase_mins, autocvar_cl_eventchase_maxs, eventchase_target_origin, MOVE_WORLDONLY, self);
+
+                       // If the boxtrace fails, revert back to line tracing.
+                       if(trace_startsolid)
+                       {
+                               eventchase_target_origin = (current_view_origin - (v_forward * eventchase_current_distance));
+                               WarpZone_TraceLine(current_view_origin, eventchase_target_origin, MOVE_WORLDONLY, self);
+                               setproperty(VF_ORIGIN, (trace_endpos - (v_forward * autocvar_cl_eventchase_mins_z)));
+                       }
+                       else { setproperty(VF_ORIGIN, trace_endpos); }
+
                        setproperty(VF_ANGLES, WarpZone_TransformVAngles(WarpZone_trace_transform, view_angles));
                }
                else if(autocvar_chase_active < 0) // time to disable chase_active if it was set by this code
@@ -898,25 +915,22 @@ void CSQC_UpdateView(float w, float h)
                // pro: matches model better
                // contra: it's not red because blood is red, but because red is an alarming color, so red should stay
                // maybe different reddish pics?
-               if(autocvar_chase_active >= 0) // not while the event chase camera is active
+               if(autocvar_cl_gentle_damage || autocvar_cl_gentle)
                {
-                       if(autocvar_cl_gentle_damage || autocvar_cl_gentle)
+                       if(autocvar_cl_gentle_damage == 2)
                        {
-                               if(autocvar_cl_gentle_damage == 2)
+                               if(myhealth_flash < pain_threshold) // only randomize when the flash is gone
                                {
-                                       if(myhealth_flash < pain_threshold) // only randomize when the flash is gone
-                                       {
-                                               myhealth_gentlergb = eX * random() + eY * random() + eZ * random();
-                                       }
+                                       myhealth_gentlergb = eX * random() + eY * random() + eZ * random();
                                }
-                               else
-                                       myhealth_gentlergb = stov(autocvar_hud_damage_gentle_color);
-
-                               drawfill('0 0 0', eX * vid_conwidth + eY * vid_conheight, myhealth_gentlergb, autocvar_hud_damage_gentle_alpha_multiplier * bound(0, myhealth_flash_temp, 1) * autocvar_hud_damage, DRAWFLAG_NORMAL);
                        }
                        else
-                               drawpic(splash_pos, "gfx/blood", splash_size, stov(autocvar_hud_damage_color), bound(0, myhealth_flash_temp, 1) * autocvar_hud_damage, DRAWFLAG_NORMAL);
+                               myhealth_gentlergb = stov(autocvar_hud_damage_gentle_color);
+
+                       drawfill('0 0 0', eX * vid_conwidth + eY * vid_conheight, myhealth_gentlergb, autocvar_hud_damage_gentle_alpha_multiplier * bound(0, myhealth_flash_temp, 1) * autocvar_hud_damage, DRAWFLAG_NORMAL);
                }
+               else
+                       drawpic(splash_pos, "gfx/blood", splash_size, stov(autocvar_hud_damage_color), bound(0, myhealth_flash_temp, 1) * autocvar_hud_damage, DRAWFLAG_NORMAL);
 
                if(autocvar_hud_postprocessing) // we still need to set this anyway even when chase_active is set, this way it doesn't get stuck on.
                {
@@ -944,7 +958,7 @@ void CSQC_UpdateView(float w, float h)
                if(cvar("r_glsl_postprocess_uservec2_enable") != e2) { cvar_set("r_glsl_postprocess_uservec2_enable", ftos(e2)); }
 
                // blur postprocess handling done first (used by hud_damage and hud_contents)
-               if((damage_blurpostprocess_x || content_blurpostprocess_x) && autocvar_chase_active >= 0) // not while the event chase camera is active
+               if((damage_blurpostprocess_x || content_blurpostprocess_x))
                {
                        float blurradius = bound(0, damage_blurpostprocess_y + content_blurpostprocess_y, autocvar_hud_postprocessing_maxblurradius);
                        float bluralpha = bound(0, damage_blurpostprocess_z + content_blurpostprocess_z, autocvar_hud_postprocessing_maxbluralpha);
@@ -969,7 +983,7 @@ void CSQC_UpdateView(float w, float h)
                
                sharpen_intensity = bound(0, ((getstati(STAT_HEALTH) > 0) ? sharpen_intensity : 0), 5); // Check to see if player is alive (if not, set 0) - also bound to fade out starting at 5 seconds.
                
-               if(autocvar_hud_powerup && sharpen_intensity > 0 && autocvar_chase_active >= 0) // not while the event chase camera is active
+               if(autocvar_hud_powerup && sharpen_intensity > 0)
                {
                        if(sharpen_intensity != old_sharpen_intensity) // reduce cvar_set spam as much as possible
                        {
index 01f3925199db205d29ace760acc23682ab9647d6..beb56874ab4393d0ac036b3d607ac38122fa1cbf 100644 (file)
@@ -63,6 +63,9 @@ var float autocvar_cl_reticle = 1;
 float autocvar_cl_reticle_item_nex;
 float autocvar_cl_reticle_item_normal;
 float autocvar_cl_reticle_stretch;
+var float autocvar_cl_spawnzoom = 1;
+var float autocvar_cl_spawnzoom_speed = 1;
+var float autocvar_cl_spawnzoom_factor = 2;
 float autocvar_cl_stripcolorcodes;
 var float autocvar_cl_vehicle_spiderbot_cross_alpha = 0.6;
 var float autocvar_cl_vehicle_spiderbot_cross_size = 1;
@@ -326,6 +329,7 @@ var float autocvar_hud_panel_weapons_timeout_speed_out = 0.75;
 float autocvar_hud_progressbar_alpha;
 float autocvar_hud_showbinds;
 float autocvar_hud_showbinds_limit;
+float autocvar__hud_showbinds_reload;
 float autocvar_hud_shownames;
 float autocvar_hud_shownames_enemies;
 float autocvar_hud_shownames_crosshairdistance;
@@ -383,6 +387,9 @@ float autocvar_cl_hitsound_antispam_time;
 var float autocvar_cl_eventchase_death = 1;
 var float autocvar_cl_eventchase_distance = 140;
 var float autocvar_cl_eventchase_speed = 1.3;
+var vector autocvar_cl_eventchase_maxs = '12 12 8';
+var vector autocvar_cl_eventchase_mins = '-12 -12 -8';
+var vector autocvar_cl_eventchase_viewoffset = '0 0 20';
 float autocvar_cl_lerpexcess;
 string autocvar__togglezoom;
 float autocvar_cl_damageeffect;
index 96bf5e5c67d207f3b8b864535eca34c2a6628a85..daa8f3ff104fe3bcfc659fd32f7762c2ebd1fc50 100644 (file)
@@ -40,6 +40,7 @@ void XonoticInputSettingsTab_fill(entity me)
                me.TD(me, 1, 1, e = makeXonoticButton(_("Clear"), '0 0 0'));
                        e.onClick = KeyBinder_Bind_Clear;
                        e.onClickEntity = kb;
+                       kb.clearButton = e;
 
        me.gotoRC(me, 0, 3.2); me.setFirstColumn(me, me.currentColumn);
                me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "con_closeontoggleconsole", _("Pressing \"enter console\" key also closes it")));
index 1107e60abc6b3296e77dd9582bbe6613a267ef32..739be1b056aa908da09d5ab1bfded18f3ef30708 100644 (file)
@@ -22,6 +22,7 @@ CLASS(XonoticKeyBinder) EXTENDS(XonoticListBox)
        ATTRIB(XonoticKeyBinder, inMouseHandler, float, 0)
        ATTRIB(XonoticKeyBinder, userbindEditButton, entity, NULL)
        ATTRIB(XonoticKeyBinder, keyGrabButton, entity, NULL)
+       ATTRIB(XonoticKeyBinder, clearButton, entity, NULL)
        ATTRIB(XonoticKeyBinder, userbindEditDialog, entity, NULL)
        METHOD(XonoticKeyBinder, editUserbind, void(entity, string, string, string))
 ENDCLASS(XonoticKeyBinder)
@@ -80,6 +81,8 @@ void replace_bind(string from, string to)
                if(k != -1)
                        localcmd("\nbind \"", keynumtostring(k), "\" \"", to, "\"\n");
        }
+       if(n)
+               cvar_set("_hud_showbinds_reload", "1");
 }
 void XonoticKeyBinder_configureXonoticKeyBinder(entity me)
 {
@@ -126,6 +129,7 @@ void KeyBinder_Bind_Change(entity btn, entity me)
                return;
 
        me.keyGrabButton.forcePressed = 1;
+       me.clearButton.disabled = 1;
        keyGrabber = me;
 }
 void XonoticKeyBinder_keyGrabbed(entity me, float key, float ascii)
@@ -134,6 +138,8 @@ void XonoticKeyBinder_keyGrabbed(entity me, float key, float ascii)
        string func;
 
        me.keyGrabButton.forcePressed = 0;
+       me.clearButton.disabled = 0;
+
        if(key == K_ESCAPE)
                return;
 
@@ -161,6 +167,7 @@ void XonoticKeyBinder_keyGrabbed(entity me, float key, float ascii)
        }
        localcmd("\nbind \"", keynumtostring(key), "\" \"", func, "\"\n");
        localcmd("-zoom\n"); // to make sure we aren't in togglezoom'd state
+       cvar_set("_hud_showbinds_reload", "1");
 }
 void XonoticKeyBinder_editUserbind(entity me, string theName, string theCommandPress, string theCommandRelease)
 {
@@ -168,11 +175,11 @@ void XonoticKeyBinder_editUserbind(entity me, string theName, string theCommandP
 
        if(!me.userbindEditDialog)
                return;
-       
+
        func = Xonotic_KeyBinds_Functions[me.selectedItem];
        if(func == "")
                return;
-       
+
        descr = Xonotic_KeyBinds_Descriptions[me.selectedItem];
        if(substring(descr, 0, 1) != "$")
                return;
@@ -189,11 +196,11 @@ void KeyBinder_Bind_Edit(entity btn, entity me)
 
        if(!me.userbindEditDialog)
                return;
-       
+
        func = Xonotic_KeyBinds_Functions[me.selectedItem];
        if(func == "")
                return;
-       
+
        descr = Xonotic_KeyBinds_Descriptions[me.selectedItem];
        if(substring(descr, 0, 1) != "$")
                return;
@@ -222,6 +229,7 @@ void KeyBinder_Bind_Clear(entity btn, entity me)
                        localcmd("\nbind \"", keynumtostring(k), "\" \"", KEY_NOT_BOUND_CMD, "\"\n");
        }
        localcmd("-zoom\n"); // to make sure we aren't in togglezoom'd state
+       cvar_set("_hud_showbinds_reload", "1");
 }
 void XonoticKeyBinder_clickListBoxItem(entity me, float i, vector where)
 {
diff --git a/qcsrc/server/attic/domination.qc b/qcsrc/server/attic/domination.qc
new file mode 100644 (file)
index 0000000..6a0dcdf
--- /dev/null
@@ -0,0 +1,525 @@
+
+/*
+Domination as a plugin for netquake mods
+by LordHavoc (lordhavoc@ghdigital.com)
+
+How to add domination points to a mod:
+1. Add this line to progs.src above world.qc:
+domination.qc
+2. Comment out all lines in ClientObituary in client.qc that begin with targ.frags  or attacker.frags.
+3. Add this above spawnfunc_worldspawn in world.qc:
+void() dom_init;
+4. Add this line to the end of spawnfunc_worldspawn in world.qc:
+dom_init();
+
+Note: The only teams who can use dom control points are identified by spawnfunc_dom_team entities (if none exist these default to red and blue and use only quake models/sounds).
+*/
+
+#define DOMPOINTFRAGS frags
+
+.float enemy_playerid;
+.entity sprite;
+.float captime;
+
+// pps: points per second
+.float dom_total_pps;
+.float dom_pps_red;
+.float dom_pps_blue;
+.float dom_pps_yellow;
+.float dom_pps_pink;
+float total_pps;
+float pps_red;
+float pps_blue;
+float pps_yellow;
+float pps_pink;
+void set_dom_state(entity e)
+{
+       e.dom_total_pps = total_pps;
+       e.dom_pps_red = pps_red;
+       e.dom_pps_blue = pps_blue;
+       if(c3 >= 0)
+               e.dom_pps_yellow = pps_yellow;
+       if(c4 >= 0)
+               e.dom_pps_pink = pps_pink;
+}
+
+void() dom_controlpoint_setup;
+
+void LogDom(string mode, float team_before, entity actor)
+{
+       string s;
+       if(!autocvar_sv_eventlog)
+               return;
+       s = strcat(":dom:", mode);
+       s = strcat(s, ":", ftos(team_before));
+       s = strcat(s, ":", ftos(actor.playerid));
+       GameLogEcho(s);
+}
+
+void() dom_spawnteams;
+
+void dompoint_captured ()
+{
+       entity head;
+       float old_delay, old_team, real_team;
+
+       // now that the delay has expired, switch to the latest team to lay claim to this point
+       head = self.owner;
+
+       real_team = self.cnt;
+       self.cnt = -1;
+
+       LogDom("taken", self.team, self.dmg_inflictor);
+       self.dmg_inflictor = world;
+
+       self.goalentity = head;
+       self.model = head.mdl;
+       self.modelindex = head.dmg;
+       self.skin = head.skin;
+
+       //bprint(head.message);
+       //bprint("\n");
+
+       //bprint(^3head.netname);
+       //bprint(head.netname);
+       //bprint(self.message);
+       //bprint("\n");
+
+       float points, wait_time;
+       if (autocvar_g_domination_point_amt)
+               points = autocvar_g_domination_point_amt;
+       else
+               points = self.frags;
+       if (autocvar_g_domination_point_rate)
+               wait_time = autocvar_g_domination_point_rate;
+       else
+               wait_time = self.wait;
+
+       bprint("^3", head.netname, "^3", self.message);
+       if (points != 1)
+               bprint(" ^7(", ftos(points), " points every ", ftos(wait_time), " seconds)\n");
+       else
+               bprint(" ^7(", ftos(points), " point every ", ftos(wait_time), " seconds)\n");
+
+       if(self.enemy.playerid == self.enemy_playerid)
+               PlayerScore_Add(self.enemy, SP_DOM_TAKES, 1);
+       else
+               self.enemy = world;
+
+       if (head.noise != "")
+               if(self.enemy)
+                       sound(self.enemy, CH_TRIGGER, head.noise, VOL_BASE, ATTN_NORM);
+               else
+                       sound(self, CH_TRIGGER, head.noise, VOL_BASE, ATTN_NORM);
+       if (head.noise1 != "")
+               play2all(head.noise1);
+
+       //self.nextthink = time + autocvar_g_domination_point_rate;
+       //self.think = dompointthink;
+
+       self.delay = time + wait_time;
+
+       // do trigger work
+       old_delay = self.delay;
+       old_team = self.team;
+       self.team = real_team;
+       self.delay = 0;
+       activator = self;
+       SUB_UseTargets ();
+       self.delay = old_delay;
+       self.team = old_team;
+
+       switch(self.goalentity.team)
+       {
+               case COLOR_TEAM1:
+                       WaypointSprite_UpdateSprites(self.sprite, "dom-red", "", "");
+                       break;
+               case COLOR_TEAM2:
+                       WaypointSprite_UpdateSprites(self.sprite, "dom-blue", "", "");
+                       break;
+               case COLOR_TEAM3:
+                       WaypointSprite_UpdateSprites(self.sprite, "dom-yellow", "", "");
+                       break;
+               case COLOR_TEAM4:
+                       WaypointSprite_UpdateSprites(self.sprite, "dom-pink", "", "");
+       }
+
+       total_pps = 0, pps_red = 0, pps_blue = 0, pps_yellow = 0, pps_pink = 0;
+       for(head = world; (head = find(head, classname, "dom_controlpoint")) != world; )
+       {
+               if (autocvar_g_domination_point_amt)
+                       points = autocvar_g_domination_point_amt;
+               else
+                       points = head.frags;
+               if (autocvar_g_domination_point_rate)
+                       wait_time = autocvar_g_domination_point_rate;
+               else
+                       wait_time = head.wait;
+               switch(head.goalentity.team)
+               {
+                       case COLOR_TEAM1:
+                               pps_red += points/wait_time;
+                               break;
+                       case COLOR_TEAM2:
+                               pps_blue += points/wait_time;
+                               break;
+                       case COLOR_TEAM3:
+                               pps_yellow += points/wait_time;
+                               break;
+                       case COLOR_TEAM4:
+                               pps_pink += points/wait_time;
+               }
+               total_pps += points/wait_time;
+       }
+
+       WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, colormapPaletteColor(self.goalentity.team - 1, 0));
+       WaypointSprite_Ping(self.sprite);
+
+       self.captime = time;
+
+       FOR_EACH_REALCLIENT(head)
+               set_dom_state(head);
+}
+
+void AnimateDomPoint()
+{
+       if(self.pain_finished > time)
+               return;
+       self.pain_finished = time + self.t_width;
+       if(self.nextthink > self.pain_finished)
+               self.nextthink = self.pain_finished;
+
+       self.frame = self.frame + 1;
+       if(self.frame > self.t_length)
+               self.frame = 0;
+}
+
+void dompointthink()
+{
+       float fragamt;
+
+       self.nextthink = time + 0.1;
+
+       //self.frame = self.frame + 1;
+       //if(self.frame > 119)
+       //      self.frame = 0;
+       AnimateDomPoint();
+
+       // give points
+
+       if (gameover || self.delay > time || time < game_starttime)     // game has ended, don't keep giving points
+               return;
+
+       if(autocvar_g_domination_point_rate)
+               self.delay = time + autocvar_g_domination_point_rate;
+       else
+               self.delay = time + self.wait;
+
+       // give credit to the team
+       // NOTE: this defaults to 0
+       if (self.goalentity.netname != "")
+       {
+               if(autocvar_g_domination_point_amt)
+                       fragamt = autocvar_g_domination_point_amt;
+               else
+                       fragamt = self.DOMPOINTFRAGS;
+               TeamScore_AddToTeam(self.goalentity.team, ST_SCORE, fragamt);
+               TeamScore_AddToTeam(self.goalentity.team, ST_DOM_TICKS, fragamt);
+
+               // give credit to the individual player, if he is still there
+               if (self.enemy.playerid == self.enemy_playerid)
+               {
+                       PlayerScore_Add(self.enemy, SP_SCORE, fragamt);
+                       PlayerScore_Add(self.enemy, SP_DOM_TICKS, fragamt);
+               }
+               else
+                       self.enemy = world;
+       }
+}
+
+void dompointtouch()
+{
+       entity head;
+       if (other.classname != "player")
+               return;
+       if (other.health < 1)
+               return;
+
+       if(time < self.captime + 0.3)
+               return;
+
+       // only valid teams can claim it
+       head = find(world, classname, "dom_team");
+       while (head && head.team != other.team)
+               head = find(head, classname, "dom_team");
+       if (!head || head.netname == "" || head == self.goalentity)
+               return;
+
+       // delay capture
+
+       self.team = self.goalentity.team; // this stores the PREVIOUS team!
+
+       self.cnt = other.team;
+       self.owner = head; // team to switch to after the delay
+       self.dmg_inflictor = other;
+
+       // self.state = 1;
+       // self.delay = time + cvar("g_domination_point_capturetime");
+       //self.nextthink = time + cvar("g_domination_point_capturetime");
+       //self.think = dompoint_captured;
+
+       // go to neutral team in the mean time
+       head = find(world, classname, "dom_team");
+       while (head && head.netname != "")
+               head = find(head, classname, "dom_team");
+       if(head == world)
+               return;
+
+       WaypointSprite_UpdateSprites(self.sprite, "dom-neut", "", "");
+       WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, '0 1 1');
+       WaypointSprite_Ping(self.sprite);
+
+       self.goalentity = head;
+       self.model = head.mdl;
+       self.modelindex = head.dmg;
+       self.skin = head.skin;
+
+       self.enemy = other; // individual player scoring
+       self.enemy_playerid = other.playerid;
+       dompoint_captured();
+}
+
+/*QUAKED spawnfunc_dom_team (0 .5 .8) (-32 -32 -24) (32 32 32)
+Team declaration for Domination gameplay, this allows you to decide what team
+names and control point models are used in your map.
+
+Note: If you use spawnfunc_dom_team entities you must define at least 3 and only two
+can have netname set!  The nameless team owns all control points at start.
+
+Keys:
+"netname"
+ Name of the team (for example Red Team, Blue Team, Green Team, Yellow Team, Life, Death, etc)
+"cnt"
+ Scoreboard color of the team (for example 4 is red and 13 is blue)
+"model"
+ Model to use for control points owned by this team (for example
+ "progs/b_g_key.mdl" is a gold keycard, and "progs/b_s_key.mdl" is a silver
+ keycard)
+"skin"
+ Skin of the model to use (for team skins on a single model)
+"noise"
+ Sound to play when this team captures a point.
+ (this is a localized sound, like a small alarm or other effect)
+"noise1"
+ Narrator speech to play when this team captures a point.
+ (this is a global sound, like "Red team has captured a control point")
+*/
+
+void spawnfunc_dom_team()
+{
+       if(!g_domination || autocvar_g_domination_teams_override >= 2)
+       {
+               remove(self);
+               return;
+       }
+       precache_model(self.model);
+       if (self.noise != "")
+               precache_sound(self.noise);
+       if (self.noise1 != "")
+               precache_sound(self.noise1);
+       self.classname = "dom_team";
+       setmodel(self, self.model); // precision not needed
+       self.mdl = self.model;
+       self.dmg = self.modelindex;
+       self.model = "";
+       self.modelindex = 0;
+       // this would have to be changed if used in quakeworld
+       if(self.cnt)
+               self.team = self.cnt + 1; // WHY are these different anyway?
+}
+
+void dom_controlpoint_setup()
+{
+       entity head;
+       // find the spawnfunc_dom_team representing unclaimed points
+       head = find(world, classname, "dom_team");
+       while(head && head.netname != "")
+               head = find(head, classname, "dom_team");
+       if (!head)
+               objerror("no spawnfunc_dom_team with netname \"\" found\n");
+
+       // copy important properties from spawnfunc_dom_team entity
+       self.goalentity = head;
+       setmodel(self, head.mdl); // precision already set
+       self.skin = head.skin;
+
+       self.cnt = -1;
+
+       if(self.message == "")
+               self.message = " has captured a control point";
+
+       if(self.DOMPOINTFRAGS <= 0)
+               self.DOMPOINTFRAGS = 1;
+       if(self.wait <= 0)
+               self.wait = 5;
+
+       float points, waittime;
+       if (autocvar_g_domination_point_amt)
+               points = autocvar_g_domination_point_amt;
+       else
+               points = self.frags;
+       if (autocvar_g_domination_point_rate)
+               waittime = autocvar_g_domination_point_rate;
+       else
+               waittime = self.wait;
+
+       total_pps += points/waittime;
+
+       if(!self.t_width)
+               self.t_width = 0.02; // frame animation rate
+       if(!self.t_length)
+               self.t_length = 239; // maximum frame
+
+       self.think = dompointthink;
+       self.nextthink = time;
+       self.touch = dompointtouch;
+       self.solid = SOLID_TRIGGER;
+       self.flags = FL_ITEM;
+       setsize(self, '-32 -32 -32', '32 32 32');
+       setorigin(self, self.origin + '0 0 20');
+       droptofloor();
+
+       waypoint_spawnforitem(self);
+       WaypointSprite_SpawnFixed("dom-neut", self.origin + '0 0 32', self, sprite, RADARICON_DOMPOINT, '0 1 1');
+}
+
+
+
+/*QUAKED spawnfunc_dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
+Control point for Domination gameplay.
+*/
+void spawnfunc_dom_controlpoint()
+{
+       if(!g_domination)
+       {
+               remove(self);
+               return;
+       }
+       self.think = dom_controlpoint_setup;
+       self.nextthink = time + 0.1;
+       self.reset = dom_controlpoint_setup;
+
+       if(!self.scale)
+               self.scale = 0.6;
+
+       //if(!self.glow_size)
+       //      self.glow_size = cvar("g_domination_point_glow");
+       self.effects = self.effects | EF_LOWPRECISION;
+       if (autocvar_g_domination_point_fullbright)
+               self.effects |= EF_FULLBRIGHT;
+}
+
+// code from here on is just to support maps that don't have control point and team entities
+void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage)
+{
+       entity oldself;
+       oldself = self;
+       self = spawn();
+       self.classname = "dom_team";
+       self.netname = teamname;
+       self.cnt = teamcolor;
+       self.model = pointmodel;
+       self.skin = pointskin;
+       self.noise = capsound;
+       self.noise1 = capnarration;
+       self.message = capmessage;
+
+       // this code is identical to spawnfunc_dom_team
+       setmodel(self, self.model); // precision not needed
+       self.mdl = self.model;
+       self.dmg = self.modelindex;
+       self.model = "";
+       self.modelindex = 0;
+       // this would have to be changed if used in quakeworld
+       self.team = self.cnt + 1;
+
+       //eprint(self);
+       self = oldself;
+}
+
+void dom_spawnpoint(vector org)
+{
+       entity oldself;
+       oldself = self;
+       self = spawn();
+       self.classname = "dom_controlpoint";
+       self.think = spawnfunc_dom_controlpoint;
+       self.nextthink = time;
+       setorigin(self, org);
+       spawnfunc_dom_controlpoint();
+       self = oldself;
+}
+
+// spawn some default teams if the map is not set up for domination
+void dom_spawnteams()
+{
+       float numteams;
+       if(autocvar_g_domination_teams_override < 2)
+               numteams = autocvar_g_domination_default_teams;
+       else
+               numteams = autocvar_g_domination_teams_override;
+       // LordHavoc: edit this if you want to change defaults
+       dom_spawnteam("Red", COLOR_TEAM1-1, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
+       dom_spawnteam("Blue", COLOR_TEAM2-1, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
+       if(numteams > 2)
+               dom_spawnteam("Yellow", COLOR_TEAM3-1, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
+       if(numteams > 3)
+               dom_spawnteam("Pink", COLOR_TEAM4-1, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
+       dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
+}
+
+void dom_delayedinit()
+{
+       entity head;
+
+       // if no teams are found, spawn defaults, if custom teams are set, use them
+       if (find(world, classname, "dom_team") == world || autocvar_g_domination_teams_override >= 2)
+               dom_spawnteams();
+       // if no control points are found, spawn defaults
+       if (find(world, classname, "dom_controlpoint") == world)
+       {
+               // TODO in a few months (maybe 2011/08): change this into error() and remove this very poor dom point selection
+               backtrace("This map contains no dom_controlpoint entities. A very poor dom point placement will be chosen. Please fix the map.");
+
+               // if no supported map was found, make every deathmatch spawn a point
+               head = find(world, classname, "info_player_deathmatch");
+               while (head)
+               {
+                       dom_spawnpoint(head.origin);
+                       head = find(head, classname, "info_player_deathmatch");
+               }
+       }
+
+       ScoreRules_dom();
+}
+
+void dom_init()
+{
+       // we have to precache default models/sounds even if they might not be
+       // used because spawnfunc_worldspawn is executed before any other entities are read,
+       // so we don't even know yet if this map is set up for domination...
+       precache_model("models/domination/dom_red.md3");
+       precache_model("models/domination/dom_blue.md3");
+       precache_model("models/domination/dom_yellow.md3");
+       precache_model("models/domination/dom_pink.md3");
+       precache_model("models/domination/dom_unclaimed.md3");
+       precache_sound("domination/claim.wav");
+       InitializeEntity(world, dom_delayedinit, INITPRIO_GAMETYPE);
+
+       addstat(STAT_DOM_TOTAL_PPS, AS_FLOAT, dom_total_pps);
+       addstat(STAT_DOM_PPS_RED, AS_FLOAT, dom_pps_red);
+       addstat(STAT_DOM_PPS_BLUE, AS_FLOAT, dom_pps_blue);
+       if(c3 >= 0) addstat(STAT_DOM_PPS_YELLOW, AS_FLOAT, dom_pps_yellow);
+       if(c4 >= 0) addstat(STAT_DOM_PPS_PINK, AS_FLOAT, dom_pps_pink);
+}
+
index 1a94bca8930f5c55628abe11c6b56ec9b3d20c7d..efd5303dd10c8730c6186b15d5a4e67f9772d233 100644 (file)
@@ -1274,3 +1274,6 @@ float autocvar_g_sandbox_object_material_velocity_min;
 float autocvar_g_sandbox_object_material_velocity_factor;
 float autocvar_g_max_info_autoscreenshot;
 float autocvar_physics_ode;
+float autocvar_g_physical_items;
+float autocvar_g_physical_items_damageforcescale;
+float autocvar_g_physical_items_reset;
index ad3bb2367418f611d13bb67725c70cd416d7831f..a3078c4e43588bc64bbbeef1c2a09e490a4af928 100644 (file)
@@ -203,25 +203,6 @@ void havocbot_goalrating_enemyplayers(float ratingscale, vector org, float sradi
 // choose a role according to the situation
 void havocbot_role_dm();
 
-//DOM:
-//go to best items, or control points you don't own
-void havocbot_role_dom()
-{
-       if(self.deadflag != DEAD_NO)
-               return;
-
-       if (self.bot_strategytime < time)
-       {
-               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
-               navigation_goalrating_start();
-               havocbot_goalrating_controlpoints(10000, self.origin, 15000);
-               havocbot_goalrating_items(8000, self.origin, 8000);
-               //havocbot_goalrating_enemyplayers(3000, self.origin, 2000);
-               //havocbot_goalrating_waypoints(1, self.origin, 1000);
-               navigation_goalrating_end();
-       }
-}
-
 //DM:
 //go to best items
 void havocbot_role_dm()
@@ -284,19 +265,12 @@ void havocbot_chooserole_race()
        self.havocbot_role = havocbot_role_race;
 }
 
-void havocbot_chooserole_dom()
-{
-       self.havocbot_role = havocbot_role_dom;
-}
-
 void havocbot_chooserole()
 {
        dprint("choosing a role...\n");
        self.bot_strategytime = 0;
        if (MUTATOR_CALLHOOK(HavocBot_ChooseRule))
                return;
-       else if (g_domination)
-               havocbot_chooserole_dom();
        else if (g_keyhunt)
                havocbot_chooserole_kh();
        else if (g_race || g_cts)
index 0766ab0466dbe24fc9ae2bed9ecb2ce529eeef5d..8e9ae6b911e745cfb58bf6acb7132792f3d898eb 100644 (file)
@@ -390,7 +390,7 @@ float waypoint_load_links()
                wp_to_pos       = stov(argv(1));
 
                // Search "from" waypoint
-               if(wp_from && wp_from.origin!=wp_from_pos)
+               if(!wp_from || wp_from.origin!=wp_from_pos)
                {
                        wp_from = findradius(wp_from_pos, 1);
                        found = FALSE;
@@ -486,7 +486,7 @@ void waypoint_load_links_hardwired()
                wp_to_pos       = stov(argv(1));
 
                // Search "from" waypoint
-               if(wp_from && wp_from.origin!=wp_from_pos)
+               if(!wp_from || wp_from.origin!=wp_from_pos)
                {
                        wp_from = findradius(wp_from_pos, 5);
                        found = FALSE;
index adea40c4b946d385ec9aa735812f99cca416c9b9..1078eea1308df0c90c4d2f00b07710568894a118 100644 (file)
@@ -407,10 +407,11 @@ void PutObserverInServer (void)
        minstagib_stop_countdown(self);
 
        Portal_ClearAll(self);
-
+       
        if(self.alivetime)
        {
-               PlayerStats_Event(self, PLAYERSTATS_ALIVETIME, time - self.alivetime);
+               if(!inWarmupStage)
+                       PlayerStats_Event(self, PLAYERSTATS_ALIVETIME, time - self.alivetime);
                self.alivetime = 0;
        }
 
@@ -511,11 +512,13 @@ void PutObserverInServer (void)
        {
                if(self.version_mismatch)
                {
+                       self.frags = FRAGS_SPECTATOR;
                        Spawnqueue_Unmark(self);
                        Spawnqueue_Remove(self);
                }
                else
                {
+                       self.frags = FRAGS_LMS_LOSER;
                        Spawnqueue_Insert(self);
                }
        }
@@ -534,6 +537,13 @@ void PutObserverInServer (void)
                else
                        self.frags = FRAGS_SPECTATOR;
        }
+       else if((g_race && g_race_qualifying) || g_cts)
+       {
+               if(PlayerScore_Add(self, SP_RACE_FASTEST, 0))
+                       self.frags = FRAGS_LMS_LOSER;
+               else
+                       self.frags = FRAGS_SPECTATOR;
+       }
        else
                self.frags = FRAGS_SPECTATOR;
 }
@@ -913,8 +923,9 @@ void PutClientInServer (void)
                self.weaponname = "";
                self.switchingweapon = 0;
 
-               if(!self.alivetime)
-                       self.alivetime = time;
+               if(!inWarmupStage)
+                       if(!self.alivetime)
+                               self.alivetime = time;
 
                antilag_clear(self);
 
@@ -1381,9 +1392,6 @@ void ClientConnect (void)
 
        race_PreSpawnObserver();
 
-       //if(g_domination)
-       //      dom_player_join_team(self);
-
        // identify the right forced team
        if(autocvar_g_campaign)
        {
@@ -1569,9 +1577,6 @@ void ClientConnect (void)
        else if(autocvar_sv_teamnagger && !(autocvar_bot_vs_human && (c3==-1 && c4==-1)) && !g_ca) // teamnagger is currently bad for ca
                send_CSQC_teamnagger();
 
-       if (g_domination)
-               set_dom_state(self);
-
        CheatInitClient();
 
        if(!autocvar_g_campaign)
index 95dba951df6efb1109ab3c1f29d810c8b348a099..50401604ef905313d521380d6995b0cf202d6bec 100644 (file)
@@ -894,15 +894,15 @@ void SV_PlayerPhysics()
                maxspd_mod = autocvar_sv_spectator_speed_multiplier;
                if(!self.spectatorspeed)
                        self.spectatorspeed = maxspd_mod;
-               if(self.impulse && self.impulse <= 19)
+               if(self.impulse && self.impulse <= 19 || self.impulse >= 200 && self.impulse <= 209 || self.impulse >= 220 && self.impulse <= 229)
                {
                        if(self.lastclassname != "player")
                        {
-                               if(self.impulse == 10 || self.impulse == 15 || self.impulse == 18)
+                               if(self.impulse == 10 || self.impulse == 15 || self.impulse == 18 || self.impulse >= 200 && self.impulse <= 209)
                                        self.spectatorspeed = bound(1, self.spectatorspeed + 0.5, 5);
                                else if(self.impulse == 11)
                                        self.spectatorspeed = maxspd_mod;
-                               else if(self.impulse == 12 || self.impulse == 16  || self.impulse == 19)
+                               else if(self.impulse == 12 || self.impulse == 16  || self.impulse == 19 || self.impulse >= 220 && self.impulse <= 229)
                                        self.spectatorspeed = bound(1, self.spectatorspeed - 0.5, 5);
                                else if(self.impulse >= 1 && self.impulse <= 9)
                                        self.spectatorspeed = 1 + 0.5 * (self.impulse - 1);
index 236c0923fdc330834469234bc9eb2285a1f6bb26..4df59a09afde775e7e73cca9fb0be6e248ad1205 100644 (file)
@@ -298,6 +298,7 @@ string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vecto
                wep.savenextthink = wep.nextthink;
                wep.nextthink = min(wep.nextthink, time + 0.5);
                wep.pickup_anyway = TRUE; // these are ALWAYS pickable
+
                return s;
        }
 }
index 0710ddfa8474874cca23787d9ed6221e44660ebd..8f3ea869d49e24bcdf0febd74dc5564c1002f7b6 100644 (file)
@@ -1415,6 +1415,7 @@ void GameCommand_stuffto(float request, float argc)
        // we can be certain they understand the risks of it... So to enable, compile server with -DSTUFFTO_ENABLED argument.
        
        #ifdef STUFFTO_ENABLED
+       #message "stuffto command enabled"
        switch(request)
        {
                case CMD_REQUEST_COMMAND:
index abdae75d5e73f62c6feed4bbf25ba8580e948a05..329fb9d4ef98c1a4d8d4ee0c30b8b15e9625f701 100644 (file)
@@ -347,7 +347,14 @@ void ReadyRestart_force()
        readyrestart_happened = 1;
        game_starttime = time;
        if(!g_ca && !g_arena) { game_starttime += RESTART_COUNTDOWN; }
-               
+
+       // clear alivetime
+       FOR_EACH_CLIENT(tmp_player)
+       {
+               tmp_player.alivetime = 0;
+               PlayerStats_Event(tmp_player, PLAYERSTATS_ALIVETIME, -PlayerStats_Event(tmp_player, PLAYERSTATS_ALIVETIME, 0));
+       }
+
        restart_mapalreadyrestarted = 0; // reset this var, needed when cvar sv_ready_restart_repeatable is in use
 
        // disable the warmup global for the server
diff --git a/qcsrc/server/domination.qc b/qcsrc/server/domination.qc
deleted file mode 100644 (file)
index 6a0dcdf..0000000
+++ /dev/null
@@ -1,525 +0,0 @@
-
-/*
-Domination as a plugin for netquake mods
-by LordHavoc (lordhavoc@ghdigital.com)
-
-How to add domination points to a mod:
-1. Add this line to progs.src above world.qc:
-domination.qc
-2. Comment out all lines in ClientObituary in client.qc that begin with targ.frags  or attacker.frags.
-3. Add this above spawnfunc_worldspawn in world.qc:
-void() dom_init;
-4. Add this line to the end of spawnfunc_worldspawn in world.qc:
-dom_init();
-
-Note: The only teams who can use dom control points are identified by spawnfunc_dom_team entities (if none exist these default to red and blue and use only quake models/sounds).
-*/
-
-#define DOMPOINTFRAGS frags
-
-.float enemy_playerid;
-.entity sprite;
-.float captime;
-
-// pps: points per second
-.float dom_total_pps;
-.float dom_pps_red;
-.float dom_pps_blue;
-.float dom_pps_yellow;
-.float dom_pps_pink;
-float total_pps;
-float pps_red;
-float pps_blue;
-float pps_yellow;
-float pps_pink;
-void set_dom_state(entity e)
-{
-       e.dom_total_pps = total_pps;
-       e.dom_pps_red = pps_red;
-       e.dom_pps_blue = pps_blue;
-       if(c3 >= 0)
-               e.dom_pps_yellow = pps_yellow;
-       if(c4 >= 0)
-               e.dom_pps_pink = pps_pink;
-}
-
-void() dom_controlpoint_setup;
-
-void LogDom(string mode, float team_before, entity actor)
-{
-       string s;
-       if(!autocvar_sv_eventlog)
-               return;
-       s = strcat(":dom:", mode);
-       s = strcat(s, ":", ftos(team_before));
-       s = strcat(s, ":", ftos(actor.playerid));
-       GameLogEcho(s);
-}
-
-void() dom_spawnteams;
-
-void dompoint_captured ()
-{
-       entity head;
-       float old_delay, old_team, real_team;
-
-       // now that the delay has expired, switch to the latest team to lay claim to this point
-       head = self.owner;
-
-       real_team = self.cnt;
-       self.cnt = -1;
-
-       LogDom("taken", self.team, self.dmg_inflictor);
-       self.dmg_inflictor = world;
-
-       self.goalentity = head;
-       self.model = head.mdl;
-       self.modelindex = head.dmg;
-       self.skin = head.skin;
-
-       //bprint(head.message);
-       //bprint("\n");
-
-       //bprint(^3head.netname);
-       //bprint(head.netname);
-       //bprint(self.message);
-       //bprint("\n");
-
-       float points, wait_time;
-       if (autocvar_g_domination_point_amt)
-               points = autocvar_g_domination_point_amt;
-       else
-               points = self.frags;
-       if (autocvar_g_domination_point_rate)
-               wait_time = autocvar_g_domination_point_rate;
-       else
-               wait_time = self.wait;
-
-       bprint("^3", head.netname, "^3", self.message);
-       if (points != 1)
-               bprint(" ^7(", ftos(points), " points every ", ftos(wait_time), " seconds)\n");
-       else
-               bprint(" ^7(", ftos(points), " point every ", ftos(wait_time), " seconds)\n");
-
-       if(self.enemy.playerid == self.enemy_playerid)
-               PlayerScore_Add(self.enemy, SP_DOM_TAKES, 1);
-       else
-               self.enemy = world;
-
-       if (head.noise != "")
-               if(self.enemy)
-                       sound(self.enemy, CH_TRIGGER, head.noise, VOL_BASE, ATTN_NORM);
-               else
-                       sound(self, CH_TRIGGER, head.noise, VOL_BASE, ATTN_NORM);
-       if (head.noise1 != "")
-               play2all(head.noise1);
-
-       //self.nextthink = time + autocvar_g_domination_point_rate;
-       //self.think = dompointthink;
-
-       self.delay = time + wait_time;
-
-       // do trigger work
-       old_delay = self.delay;
-       old_team = self.team;
-       self.team = real_team;
-       self.delay = 0;
-       activator = self;
-       SUB_UseTargets ();
-       self.delay = old_delay;
-       self.team = old_team;
-
-       switch(self.goalentity.team)
-       {
-               case COLOR_TEAM1:
-                       WaypointSprite_UpdateSprites(self.sprite, "dom-red", "", "");
-                       break;
-               case COLOR_TEAM2:
-                       WaypointSprite_UpdateSprites(self.sprite, "dom-blue", "", "");
-                       break;
-               case COLOR_TEAM3:
-                       WaypointSprite_UpdateSprites(self.sprite, "dom-yellow", "", "");
-                       break;
-               case COLOR_TEAM4:
-                       WaypointSprite_UpdateSprites(self.sprite, "dom-pink", "", "");
-       }
-
-       total_pps = 0, pps_red = 0, pps_blue = 0, pps_yellow = 0, pps_pink = 0;
-       for(head = world; (head = find(head, classname, "dom_controlpoint")) != world; )
-       {
-               if (autocvar_g_domination_point_amt)
-                       points = autocvar_g_domination_point_amt;
-               else
-                       points = head.frags;
-               if (autocvar_g_domination_point_rate)
-                       wait_time = autocvar_g_domination_point_rate;
-               else
-                       wait_time = head.wait;
-               switch(head.goalentity.team)
-               {
-                       case COLOR_TEAM1:
-                               pps_red += points/wait_time;
-                               break;
-                       case COLOR_TEAM2:
-                               pps_blue += points/wait_time;
-                               break;
-                       case COLOR_TEAM3:
-                               pps_yellow += points/wait_time;
-                               break;
-                       case COLOR_TEAM4:
-                               pps_pink += points/wait_time;
-               }
-               total_pps += points/wait_time;
-       }
-
-       WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, colormapPaletteColor(self.goalentity.team - 1, 0));
-       WaypointSprite_Ping(self.sprite);
-
-       self.captime = time;
-
-       FOR_EACH_REALCLIENT(head)
-               set_dom_state(head);
-}
-
-void AnimateDomPoint()
-{
-       if(self.pain_finished > time)
-               return;
-       self.pain_finished = time + self.t_width;
-       if(self.nextthink > self.pain_finished)
-               self.nextthink = self.pain_finished;
-
-       self.frame = self.frame + 1;
-       if(self.frame > self.t_length)
-               self.frame = 0;
-}
-
-void dompointthink()
-{
-       float fragamt;
-
-       self.nextthink = time + 0.1;
-
-       //self.frame = self.frame + 1;
-       //if(self.frame > 119)
-       //      self.frame = 0;
-       AnimateDomPoint();
-
-       // give points
-
-       if (gameover || self.delay > time || time < game_starttime)     // game has ended, don't keep giving points
-               return;
-
-       if(autocvar_g_domination_point_rate)
-               self.delay = time + autocvar_g_domination_point_rate;
-       else
-               self.delay = time + self.wait;
-
-       // give credit to the team
-       // NOTE: this defaults to 0
-       if (self.goalentity.netname != "")
-       {
-               if(autocvar_g_domination_point_amt)
-                       fragamt = autocvar_g_domination_point_amt;
-               else
-                       fragamt = self.DOMPOINTFRAGS;
-               TeamScore_AddToTeam(self.goalentity.team, ST_SCORE, fragamt);
-               TeamScore_AddToTeam(self.goalentity.team, ST_DOM_TICKS, fragamt);
-
-               // give credit to the individual player, if he is still there
-               if (self.enemy.playerid == self.enemy_playerid)
-               {
-                       PlayerScore_Add(self.enemy, SP_SCORE, fragamt);
-                       PlayerScore_Add(self.enemy, SP_DOM_TICKS, fragamt);
-               }
-               else
-                       self.enemy = world;
-       }
-}
-
-void dompointtouch()
-{
-       entity head;
-       if (other.classname != "player")
-               return;
-       if (other.health < 1)
-               return;
-
-       if(time < self.captime + 0.3)
-               return;
-
-       // only valid teams can claim it
-       head = find(world, classname, "dom_team");
-       while (head && head.team != other.team)
-               head = find(head, classname, "dom_team");
-       if (!head || head.netname == "" || head == self.goalentity)
-               return;
-
-       // delay capture
-
-       self.team = self.goalentity.team; // this stores the PREVIOUS team!
-
-       self.cnt = other.team;
-       self.owner = head; // team to switch to after the delay
-       self.dmg_inflictor = other;
-
-       // self.state = 1;
-       // self.delay = time + cvar("g_domination_point_capturetime");
-       //self.nextthink = time + cvar("g_domination_point_capturetime");
-       //self.think = dompoint_captured;
-
-       // go to neutral team in the mean time
-       head = find(world, classname, "dom_team");
-       while (head && head.netname != "")
-               head = find(head, classname, "dom_team");
-       if(head == world)
-               return;
-
-       WaypointSprite_UpdateSprites(self.sprite, "dom-neut", "", "");
-       WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, '0 1 1');
-       WaypointSprite_Ping(self.sprite);
-
-       self.goalentity = head;
-       self.model = head.mdl;
-       self.modelindex = head.dmg;
-       self.skin = head.skin;
-
-       self.enemy = other; // individual player scoring
-       self.enemy_playerid = other.playerid;
-       dompoint_captured();
-}
-
-/*QUAKED spawnfunc_dom_team (0 .5 .8) (-32 -32 -24) (32 32 32)
-Team declaration for Domination gameplay, this allows you to decide what team
-names and control point models are used in your map.
-
-Note: If you use spawnfunc_dom_team entities you must define at least 3 and only two
-can have netname set!  The nameless team owns all control points at start.
-
-Keys:
-"netname"
- Name of the team (for example Red Team, Blue Team, Green Team, Yellow Team, Life, Death, etc)
-"cnt"
- Scoreboard color of the team (for example 4 is red and 13 is blue)
-"model"
- Model to use for control points owned by this team (for example
- "progs/b_g_key.mdl" is a gold keycard, and "progs/b_s_key.mdl" is a silver
- keycard)
-"skin"
- Skin of the model to use (for team skins on a single model)
-"noise"
- Sound to play when this team captures a point.
- (this is a localized sound, like a small alarm or other effect)
-"noise1"
- Narrator speech to play when this team captures a point.
- (this is a global sound, like "Red team has captured a control point")
-*/
-
-void spawnfunc_dom_team()
-{
-       if(!g_domination || autocvar_g_domination_teams_override >= 2)
-       {
-               remove(self);
-               return;
-       }
-       precache_model(self.model);
-       if (self.noise != "")
-               precache_sound(self.noise);
-       if (self.noise1 != "")
-               precache_sound(self.noise1);
-       self.classname = "dom_team";
-       setmodel(self, self.model); // precision not needed
-       self.mdl = self.model;
-       self.dmg = self.modelindex;
-       self.model = "";
-       self.modelindex = 0;
-       // this would have to be changed if used in quakeworld
-       if(self.cnt)
-               self.team = self.cnt + 1; // WHY are these different anyway?
-}
-
-void dom_controlpoint_setup()
-{
-       entity head;
-       // find the spawnfunc_dom_team representing unclaimed points
-       head = find(world, classname, "dom_team");
-       while(head && head.netname != "")
-               head = find(head, classname, "dom_team");
-       if (!head)
-               objerror("no spawnfunc_dom_team with netname \"\" found\n");
-
-       // copy important properties from spawnfunc_dom_team entity
-       self.goalentity = head;
-       setmodel(self, head.mdl); // precision already set
-       self.skin = head.skin;
-
-       self.cnt = -1;
-
-       if(self.message == "")
-               self.message = " has captured a control point";
-
-       if(self.DOMPOINTFRAGS <= 0)
-               self.DOMPOINTFRAGS = 1;
-       if(self.wait <= 0)
-               self.wait = 5;
-
-       float points, waittime;
-       if (autocvar_g_domination_point_amt)
-               points = autocvar_g_domination_point_amt;
-       else
-               points = self.frags;
-       if (autocvar_g_domination_point_rate)
-               waittime = autocvar_g_domination_point_rate;
-       else
-               waittime = self.wait;
-
-       total_pps += points/waittime;
-
-       if(!self.t_width)
-               self.t_width = 0.02; // frame animation rate
-       if(!self.t_length)
-               self.t_length = 239; // maximum frame
-
-       self.think = dompointthink;
-       self.nextthink = time;
-       self.touch = dompointtouch;
-       self.solid = SOLID_TRIGGER;
-       self.flags = FL_ITEM;
-       setsize(self, '-32 -32 -32', '32 32 32');
-       setorigin(self, self.origin + '0 0 20');
-       droptofloor();
-
-       waypoint_spawnforitem(self);
-       WaypointSprite_SpawnFixed("dom-neut", self.origin + '0 0 32', self, sprite, RADARICON_DOMPOINT, '0 1 1');
-}
-
-
-
-/*QUAKED spawnfunc_dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
-Control point for Domination gameplay.
-*/
-void spawnfunc_dom_controlpoint()
-{
-       if(!g_domination)
-       {
-               remove(self);
-               return;
-       }
-       self.think = dom_controlpoint_setup;
-       self.nextthink = time + 0.1;
-       self.reset = dom_controlpoint_setup;
-
-       if(!self.scale)
-               self.scale = 0.6;
-
-       //if(!self.glow_size)
-       //      self.glow_size = cvar("g_domination_point_glow");
-       self.effects = self.effects | EF_LOWPRECISION;
-       if (autocvar_g_domination_point_fullbright)
-               self.effects |= EF_FULLBRIGHT;
-}
-
-// code from here on is just to support maps that don't have control point and team entities
-void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage)
-{
-       entity oldself;
-       oldself = self;
-       self = spawn();
-       self.classname = "dom_team";
-       self.netname = teamname;
-       self.cnt = teamcolor;
-       self.model = pointmodel;
-       self.skin = pointskin;
-       self.noise = capsound;
-       self.noise1 = capnarration;
-       self.message = capmessage;
-
-       // this code is identical to spawnfunc_dom_team
-       setmodel(self, self.model); // precision not needed
-       self.mdl = self.model;
-       self.dmg = self.modelindex;
-       self.model = "";
-       self.modelindex = 0;
-       // this would have to be changed if used in quakeworld
-       self.team = self.cnt + 1;
-
-       //eprint(self);
-       self = oldself;
-}
-
-void dom_spawnpoint(vector org)
-{
-       entity oldself;
-       oldself = self;
-       self = spawn();
-       self.classname = "dom_controlpoint";
-       self.think = spawnfunc_dom_controlpoint;
-       self.nextthink = time;
-       setorigin(self, org);
-       spawnfunc_dom_controlpoint();
-       self = oldself;
-}
-
-// spawn some default teams if the map is not set up for domination
-void dom_spawnteams()
-{
-       float numteams;
-       if(autocvar_g_domination_teams_override < 2)
-               numteams = autocvar_g_domination_default_teams;
-       else
-               numteams = autocvar_g_domination_teams_override;
-       // LordHavoc: edit this if you want to change defaults
-       dom_spawnteam("Red", COLOR_TEAM1-1, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
-       dom_spawnteam("Blue", COLOR_TEAM2-1, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
-       if(numteams > 2)
-               dom_spawnteam("Yellow", COLOR_TEAM3-1, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
-       if(numteams > 3)
-               dom_spawnteam("Pink", COLOR_TEAM4-1, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
-       dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
-}
-
-void dom_delayedinit()
-{
-       entity head;
-
-       // if no teams are found, spawn defaults, if custom teams are set, use them
-       if (find(world, classname, "dom_team") == world || autocvar_g_domination_teams_override >= 2)
-               dom_spawnteams();
-       // if no control points are found, spawn defaults
-       if (find(world, classname, "dom_controlpoint") == world)
-       {
-               // TODO in a few months (maybe 2011/08): change this into error() and remove this very poor dom point selection
-               backtrace("This map contains no dom_controlpoint entities. A very poor dom point placement will be chosen. Please fix the map.");
-
-               // if no supported map was found, make every deathmatch spawn a point
-               head = find(world, classname, "info_player_deathmatch");
-               while (head)
-               {
-                       dom_spawnpoint(head.origin);
-                       head = find(head, classname, "info_player_deathmatch");
-               }
-       }
-
-       ScoreRules_dom();
-}
-
-void dom_init()
-{
-       // we have to precache default models/sounds even if they might not be
-       // used because spawnfunc_worldspawn is executed before any other entities are read,
-       // so we don't even know yet if this map is set up for domination...
-       precache_model("models/domination/dom_red.md3");
-       precache_model("models/domination/dom_blue.md3");
-       precache_model("models/domination/dom_yellow.md3");
-       precache_model("models/domination/dom_pink.md3");
-       precache_model("models/domination/dom_unclaimed.md3");
-       precache_sound("domination/claim.wav");
-       InitializeEntity(world, dom_delayedinit, INITPRIO_GAMETYPE);
-
-       addstat(STAT_DOM_TOTAL_PPS, AS_FLOAT, dom_total_pps);
-       addstat(STAT_DOM_PPS_RED, AS_FLOAT, dom_pps_red);
-       addstat(STAT_DOM_PPS_BLUE, AS_FLOAT, dom_pps_blue);
-       if(c3 >= 0) addstat(STAT_DOM_PPS_YELLOW, AS_FLOAT, dom_pps_yellow);
-       if(c4 >= 0) addstat(STAT_DOM_PPS_PINK, AS_FLOAT, dom_pps_pink);
-}
-
index 8d8c0328ec6abed9d4e4e7c7d516fb4249faf260..680afbc8909a2ffeaa9d3880a0bf48f5ad16aba6 100644 (file)
@@ -669,9 +669,6 @@ void spawnfunc_worldspawn (void)
 
        WaypointSprite_Init();
 
-       //if (g_domination)
-       //      dom_init();
-
        GameLogInit(); // prepare everything
        // NOTE for matchid:
        // changing the logic generating it is okay. But:
@@ -944,9 +941,7 @@ void spawnfunc_worldspawn (void)
                modname = "MinstaGib";
        // extra mutators that deserve to count as mod
        MUTATOR_CALLHOOK(SetModname);
-       // weird game types that deserve to count as mod
-       if(g_cts)
-               modname = "CTS";
+
        // save it for later
        modname = strzone(modname);
 
index efb73540672df091703c6649b2aa90e7862ffb2b..7f8931bffe648c90d3bd4a7b3eada671725f66e6 100644 (file)
@@ -1121,6 +1121,8 @@ void readlevelcvars(void)
                MUTATOR_ADD(mutator_dodging);
        if(cvar("g_spawn_near_teammate"))
                MUTATOR_ADD(mutator_spawn_near_teammate);
+       if(cvar("g_physical_items"))
+               MUTATOR_ADD(mutator_physical_items);
        if(!g_minstagib)
        {
                if(cvar("g_invincible_projectiles"))
index 368ab589824ef5a66464d99283c7d223184cdf3f..f4761b884dca28175d58da08589a777c589722c2 100644 (file)
@@ -116,8 +116,14 @@ float Mutator_Add(mutatorfunc_t func, string name)
                // good
                return 1;
        }
-       backtrace("WARNING: when adding mutator: adding failed\n");
-       Mutator_Remove(func, name);
+
+       backtrace("WARNING: when adding mutator: adding failed, rolling back\n");
+
+       if(func(MUTATOR_ROLLING_BACK) != 0)
+       {
+               // baaaaad
+               error("WARNING: when adding mutator: rolling back failed");
+       }
        return 0;
 }
 void Mutator_Remove(float(float) func, string name)
index 1b6cea8fc4501ee47eaa8b26089926d927544878..d90d564b501447acb3e3ef1def71530be1a521ee 100644 (file)
@@ -18,6 +18,7 @@ float CallbackChain_Call(entity cb);
 
 #define MUTATOR_REMOVING 0
 #define MUTATOR_ADDING 1
+#define MUTATOR_ROLLING_BACK 2
 typedef float(float) mutatorfunc_t;
 float Mutator_Add(mutatorfunc_t func, string name);
 void Mutator_Remove(mutatorfunc_t func, string name); // calls error() on fail
@@ -27,9 +28,10 @@ void Mutator_Remove(mutatorfunc_t func, string name); // calls error() on fail
 #define MUTATOR_DEFINITION(name) float MUTATOR_##name(float mode)
 #define MUTATOR_DECLARATION(name) float MUTATOR_##name(float mode)
 #define MUTATOR_HOOKFUNCTION(name) float HOOKFUNCTION_##name()
-#define MUTATOR_HOOK(cb,func,order) do { if(mode == MUTATOR_ADDING) { if(!HOOK_##cb) HOOK_##cb = CallbackChain_New(#cb); if(!CallbackChain_Add(HOOK_##cb,HOOKFUNCTION_##func,order)) { print("HOOK FAILED: ", #func, "\n"); return 1; } } else if(mode == MUTATOR_REMOVING) { if(HOOK_##cb) CallbackChain_Remove(HOOK_##cb,HOOKFUNCTION_##func); } } while(0)
+#define MUTATOR_HOOK(cb,func,order) do { if(mode == MUTATOR_ADDING) { if(!HOOK_##cb) HOOK_##cb = CallbackChain_New(#cb); if(!CallbackChain_Add(HOOK_##cb,HOOKFUNCTION_##func,order)) { print("HOOK FAILED: ", #func, "\n"); return 1; } } else if(mode == MUTATOR_REMOVING || mode == MUTATOR_ROLLING_BACK) { if(HOOK_##cb) CallbackChain_Remove(HOOK_##cb,HOOKFUNCTION_##func); } } while(0)
 #define MUTATOR_ONADD if(mode == MUTATOR_ADDING)
 #define MUTATOR_ONREMOVE if(mode == MUTATOR_REMOVING)
+#define MUTATOR_ONROLLBACK_OR_REMOVE if(mode == MUTATOR_REMOVING || mode == MUTATOR_ROLLING_BACK)
 
 #define MUTATOR_HOOKABLE(cb) entity HOOK_##cb
 #define MUTATOR_CALLHOOK(cb) CallbackChain_Call(HOOK_##cb)
@@ -205,6 +207,12 @@ MUTATOR_HOOKABLE(SV_StartFrame);
 MUTATOR_HOOKABLE(SetModname);
        // OUT
        string modname; // name of the mutator/mod if it warrants showing as such in the server browser
+       
+MUTATOR_HOOKABLE(Item_Spawn);
+       // called for each item being spawned on a map, including dropped weapons
+       // return 1 to remove an item
+       // INPUT
+       entity self; // the item
 
 MUTATOR_HOOKABLE(SetWeaponreplace);
        // IN
index 908d81cf3fe0a9d96b13a1c529a8db568f5008a9..134e979d1c17d0b09da3b1658b0fd724a791b55e 100644 (file)
@@ -2200,9 +2200,17 @@ MUTATOR_DEFINITION(gamemode_ctf)
                ctf_Initialize();
        }
 
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
+               // we actually cannot roll back ctf_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.");
+               print("This is a game type and it cannot be removed at runtime.");
+               return -1;
        }
 
        return 0;
diff --git a/qcsrc/server/mutators/gamemode_domination.qc b/qcsrc/server/mutators/gamemode_domination.qc
new file mode 100644 (file)
index 0000000..852fb90
--- /dev/null
@@ -0,0 +1,515 @@
+void dom_EventLog(string mode, float team_before, entity actor) // use an alias for easy changing and quick editing later
+{
+       if(autocvar_sv_eventlog)
+               GameLogEcho(strcat(":dom:", mode, ":", ftos(team_before), ((actor != world) ? (strcat(":", ftos(actor.playerid))) : "")));
+}
+
+void set_dom_state(entity e)
+{
+       e.dom_total_pps = total_pps;
+       e.dom_pps_red = pps_red;
+       e.dom_pps_blue = pps_blue;
+       if(c3 >= 0)
+               e.dom_pps_yellow = pps_yellow;
+       if(c4 >= 0)
+               e.dom_pps_pink = pps_pink;
+}
+
+void dompoint_captured ()
+{
+       entity head;
+       float old_delay, old_team, real_team;
+
+       // now that the delay has expired, switch to the latest team to lay claim to this point
+       head = self.owner;
+
+       real_team = self.cnt;
+       self.cnt = -1;
+
+       dom_EventLog("taken", self.team, self.dmg_inflictor);
+       self.dmg_inflictor = world;
+
+       self.goalentity = head;
+       self.model = head.mdl;
+       self.modelindex = head.dmg;
+       self.skin = head.skin;
+       
+       float points, wait_time;
+       if (autocvar_g_domination_point_amt)
+               points = autocvar_g_domination_point_amt;
+       else
+               points = self.frags;
+       if (autocvar_g_domination_point_rate)
+               wait_time = autocvar_g_domination_point_rate;
+       else
+               wait_time = self.wait;
+
+       bprint("^3", head.netname, "^3", self.message);
+       if (points != 1)
+               bprint(" ^7(", ftos(points), " points every ", ftos(wait_time), " seconds)\n");
+       else
+               bprint(" ^7(", ftos(points), " point every ", ftos(wait_time), " seconds)\n");
+
+       if(self.enemy.playerid == self.enemy_playerid)
+               PlayerScore_Add(self.enemy, SP_DOM_TAKES, 1);
+       else
+               self.enemy = world;
+
+       if (head.noise != "")
+               if(self.enemy)
+                       sound(self.enemy, CH_TRIGGER, head.noise, VOL_BASE, ATTN_NORM);
+               else
+                       sound(self, CH_TRIGGER, head.noise, VOL_BASE, ATTN_NORM);
+       if (head.noise1 != "")
+               play2all(head.noise1);
+
+       self.delay = time + wait_time;
+
+       // do trigger work
+       old_delay = self.delay;
+       old_team = self.team;
+       self.team = real_team;
+       self.delay = 0;
+       activator = self;
+       SUB_UseTargets ();
+       self.delay = old_delay;
+       self.team = old_team;
+
+       switch(self.goalentity.team)
+       {
+               case COLOR_TEAM1:
+                       WaypointSprite_UpdateSprites(self.sprite, "dom-red", "", "");
+                       break;
+               case COLOR_TEAM2:
+                       WaypointSprite_UpdateSprites(self.sprite, "dom-blue", "", "");
+                       break;
+               case COLOR_TEAM3:
+                       WaypointSprite_UpdateSprites(self.sprite, "dom-yellow", "", "");
+                       break;
+               case COLOR_TEAM4:
+                       WaypointSprite_UpdateSprites(self.sprite, "dom-pink", "", "");
+       }
+
+       total_pps = 0, pps_red = 0, pps_blue = 0, pps_yellow = 0, pps_pink = 0;
+       for(head = world; (head = find(head, classname, "dom_controlpoint")) != world; )
+       {
+               if (autocvar_g_domination_point_amt)
+                       points = autocvar_g_domination_point_amt;
+               else
+                       points = head.frags;
+               if (autocvar_g_domination_point_rate)
+                       wait_time = autocvar_g_domination_point_rate;
+               else
+                       wait_time = head.wait;
+               switch(head.goalentity.team)
+               {
+                       case COLOR_TEAM1:
+                               pps_red += points/wait_time;
+                               break;
+                       case COLOR_TEAM2:
+                               pps_blue += points/wait_time;
+                               break;
+                       case COLOR_TEAM3:
+                               pps_yellow += points/wait_time;
+                               break;
+                       case COLOR_TEAM4:
+                               pps_pink += points/wait_time;
+               }
+               total_pps += points/wait_time;
+       }
+
+       WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, colormapPaletteColor(self.goalentity.team - 1, 0));
+       WaypointSprite_Ping(self.sprite);
+
+       self.captime = time;
+
+       FOR_EACH_REALCLIENT(head)
+               set_dom_state(head);
+}
+
+void AnimateDomPoint()
+{
+       if(self.pain_finished > time)
+               return;
+       self.pain_finished = time + self.t_width;
+       if(self.nextthink > self.pain_finished)
+               self.nextthink = self.pain_finished;
+
+       self.frame = self.frame + 1;
+       if(self.frame > self.t_length)
+               self.frame = 0;
+}
+
+void dompointthink()
+{
+       float fragamt;
+
+       self.nextthink = time + 0.1;
+
+       //self.frame = self.frame + 1;
+       //if(self.frame > 119)
+       //      self.frame = 0;
+       AnimateDomPoint();
+
+       // give points
+
+       if (gameover || self.delay > time || time < game_starttime)     // game has ended, don't keep giving points
+               return;
+
+       if(autocvar_g_domination_point_rate)
+               self.delay = time + autocvar_g_domination_point_rate;
+       else
+               self.delay = time + self.wait;
+
+       // give credit to the team
+       // NOTE: this defaults to 0
+       if (self.goalentity.netname != "")
+       {
+               if(autocvar_g_domination_point_amt)
+                       fragamt = autocvar_g_domination_point_amt;
+               else
+                       fragamt = self.frags;
+               TeamScore_AddToTeam(self.goalentity.team, ST_SCORE, fragamt);
+               TeamScore_AddToTeam(self.goalentity.team, ST_DOM_TICKS, fragamt);
+
+               // give credit to the individual player, if he is still there
+               if (self.enemy.playerid == self.enemy_playerid)
+               {
+                       PlayerScore_Add(self.enemy, SP_SCORE, fragamt);
+                       PlayerScore_Add(self.enemy, SP_DOM_TICKS, fragamt);
+               }
+               else
+                       self.enemy = world;
+       }
+}
+
+void dompointtouch()
+{
+       entity head;
+       if (other.classname != "player")
+               return;
+       if (other.health < 1)
+               return;
+
+       if(time < self.captime + 0.3)
+               return;
+
+       // only valid teams can claim it
+       head = find(world, classname, "dom_team");
+       while (head && head.team != other.team)
+               head = find(head, classname, "dom_team");
+       if (!head || head.netname == "" || head == self.goalentity)
+               return;
+
+       // delay capture
+
+       self.team = self.goalentity.team; // this stores the PREVIOUS team!
+
+       self.cnt = other.team;
+       self.owner = head; // team to switch to after the delay
+       self.dmg_inflictor = other;
+
+       // self.state = 1;
+       // self.delay = time + cvar("g_domination_point_capturetime");
+       //self.nextthink = time + cvar("g_domination_point_capturetime");
+       //self.think = dompoint_captured;
+
+       // go to neutral team in the mean time
+       head = find(world, classname, "dom_team");
+       while (head && head.netname != "")
+               head = find(head, classname, "dom_team");
+       if(head == world)
+               return;
+
+       WaypointSprite_UpdateSprites(self.sprite, "dom-neut", "", "");
+       WaypointSprite_UpdateTeamRadar(self.sprite, RADARICON_DOMPOINT, '0 1 1');
+       WaypointSprite_Ping(self.sprite);
+
+       self.goalentity = head;
+       self.model = head.mdl;
+       self.modelindex = head.dmg;
+       self.skin = head.skin;
+
+       self.enemy = other; // individual player scoring
+       self.enemy_playerid = other.playerid;
+       dompoint_captured();
+}
+
+void dom_controlpoint_setup()
+{
+       entity head;
+       // find the spawnfunc_dom_team representing unclaimed points
+       head = find(world, classname, "dom_team");
+       while(head && head.netname != "")
+               head = find(head, classname, "dom_team");
+       if (!head)
+               objerror("no spawnfunc_dom_team with netname \"\" found\n");
+
+       // copy important properties from spawnfunc_dom_team entity
+       self.goalentity = head;
+       setmodel(self, head.mdl); // precision already set
+       self.skin = head.skin;
+
+       self.cnt = -1;
+
+       if(self.message == "")
+               self.message = " has captured a control point";
+
+       if(self.frags <= 0)
+               self.frags = 1;
+       if(self.wait <= 0)
+               self.wait = 5;
+
+       float points, waittime;
+       if (autocvar_g_domination_point_amt)
+               points = autocvar_g_domination_point_amt;
+       else
+               points = self.frags;
+       if (autocvar_g_domination_point_rate)
+               waittime = autocvar_g_domination_point_rate;
+       else
+               waittime = self.wait;
+
+       total_pps += points/waittime;
+
+       if(!self.t_width)
+               self.t_width = 0.02; // frame animation rate
+       if(!self.t_length)
+               self.t_length = 239; // maximum frame
+
+       self.think = dompointthink;
+       self.nextthink = time;
+       self.touch = dompointtouch;
+       self.solid = SOLID_TRIGGER;
+       self.flags = FL_ITEM;
+       setsize(self, '-32 -32 -32', '32 32 32');
+       setorigin(self, self.origin + '0 0 20');
+       droptofloor();
+
+       waypoint_spawnforitem(self);
+       WaypointSprite_SpawnFixed("dom-neut", self.origin + '0 0 32', self, sprite, RADARICON_DOMPOINT, '0 1 1');
+}
+
+//go to best items, or control points you don't own
+void havocbot_role_dom()
+{
+       if(self.deadflag != DEAD_NO)
+               return;
+
+       if (self.bot_strategytime < time)
+       {
+               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+               navigation_goalrating_start();
+               havocbot_goalrating_controlpoints(10000, self.origin, 15000);
+               havocbot_goalrating_items(8000, self.origin, 8000);
+               //havocbot_goalrating_enemyplayers(3000, self.origin, 2000);
+               //havocbot_goalrating_waypoints(1, self.origin, 1000);
+               navigation_goalrating_end();
+       }
+}
+
+MUTATOR_HOOKFUNCTION(dom_ClientConnect)
+{
+       set_dom_state(self);
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(dom_BotRoles)
+{
+       self.havocbot_role = havocbot_role_dom;
+       return TRUE;
+}
+
+/*QUAKED spawnfunc_dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
+Control point for Domination gameplay.
+*/
+void spawnfunc_dom_controlpoint()
+{
+       if(!g_domination)
+       {
+               remove(self);
+               return;
+       }
+       self.think = dom_controlpoint_setup;
+       self.nextthink = time + 0.1;
+       self.reset = dom_controlpoint_setup;
+
+       if(!self.scale)
+               self.scale = 0.6;
+
+       self.effects = self.effects | EF_LOWPRECISION;
+       if (autocvar_g_domination_point_fullbright)
+               self.effects |= EF_FULLBRIGHT;
+}
+
+/*QUAKED spawnfunc_dom_team (0 .5 .8) (-32 -32 -24) (32 32 32)
+Team declaration for Domination gameplay, this allows you to decide what team
+names and control point models are used in your map.
+
+Note: If you use spawnfunc_dom_team entities you must define at least 3 and only two
+can have netname set!  The nameless team owns all control points at start.
+
+Keys:
+"netname"
+ Name of the team (for example Red Team, Blue Team, Green Team, Yellow Team, Life, Death, etc)
+"cnt"
+ Scoreboard color of the team (for example 4 is red and 13 is blue)
+"model"
+ Model to use for control points owned by this team (for example
+ "progs/b_g_key.mdl" is a gold keycard, and "progs/b_s_key.mdl" is a silver
+ keycard)
+"skin"
+ Skin of the model to use (for team skins on a single model)
+"noise"
+ Sound to play when this team captures a point.
+ (this is a localized sound, like a small alarm or other effect)
+"noise1"
+ Narrator speech to play when this team captures a point.
+ (this is a global sound, like "Red team has captured a control point")
+*/
+
+void spawnfunc_dom_team()
+{
+       if(!g_domination || autocvar_g_domination_teams_override >= 2)
+       {
+               remove(self);
+               return;
+       }
+       precache_model(self.model);
+       if (self.noise != "")
+               precache_sound(self.noise);
+       if (self.noise1 != "")
+               precache_sound(self.noise1);
+       self.classname = "dom_team";
+       setmodel(self, self.model); // precision not needed
+       self.mdl = self.model;
+       self.dmg = self.modelindex;
+       self.model = "";
+       self.modelindex = 0;
+       // this would have to be changed if used in quakeworld
+       if(self.cnt)
+               self.team = self.cnt + 1; // WHY are these different anyway?
+}
+
+// scoreboard setup
+void ScoreRules_dom()
+{
+       float sp_domticks, sp_score;
+       sp_score = sp_domticks = 0;
+       if(autocvar_g_domination_disable_frags)
+               sp_domticks = SFL_SORT_PRIO_PRIMARY;
+       else
+               sp_score = SFL_SORT_PRIO_PRIMARY;
+       CheckAllowedTeams(world);
+       ScoreRules_basics(((c4>=0) ? 4 : (c3>=0) ? 3 : 2), sp_score, sp_score, TRUE);
+       ScoreInfo_SetLabel_TeamScore  (ST_DOM_TICKS,    "ticks",     sp_domticks);
+       ScoreInfo_SetLabel_PlayerScore(SP_DOM_TICKS,    "ticks",     sp_domticks);
+       ScoreInfo_SetLabel_PlayerScore(SP_DOM_TAKES,    "takes",     0);
+       ScoreRules_basics_end();
+}
+
+// code from here on is just to support maps that don't have control point and team entities
+void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, string capsound, string capnarration, string capmessage)
+{
+       entity oldself;
+       oldself = self;
+       self = spawn();
+       self.classname = "dom_team";
+       self.netname = teamname;
+       self.cnt = teamcolor;
+       self.model = pointmodel;
+       self.skin = pointskin;
+       self.noise = capsound;
+       self.noise1 = capnarration;
+       self.message = capmessage;
+
+       // this code is identical to spawnfunc_dom_team
+       setmodel(self, self.model); // precision not needed
+       self.mdl = self.model;
+       self.dmg = self.modelindex;
+       self.model = "";
+       self.modelindex = 0;
+       // this would have to be changed if used in quakeworld
+       self.team = self.cnt + 1;
+
+       //eprint(self);
+       self = oldself;
+}
+
+void dom_spawnpoint(vector org)
+{
+       entity oldself;
+       oldself = self;
+       self = spawn();
+       self.classname = "dom_controlpoint";
+       self.think = spawnfunc_dom_controlpoint;
+       self.nextthink = time;
+       setorigin(self, org);
+       spawnfunc_dom_controlpoint();
+       self = oldself;
+}
+
+// spawn some default teams if the map is not set up for domination
+void dom_spawnteams()
+{
+       float numteams = ((autocvar_g_domination_teams_override < 2) ? autocvar_g_domination_default_teams : autocvar_g_domination_teams_override);
+
+       dom_spawnteam("Red", COLOR_TEAM1-1, "models/domination/dom_red.md3", 0, "domination/claim.wav", "", "Red team has captured a control point");
+       dom_spawnteam("Blue", COLOR_TEAM2-1, "models/domination/dom_blue.md3", 0, "domination/claim.wav", "", "Blue team has captured a control point");
+       if(numteams > 2)
+               dom_spawnteam("Yellow", COLOR_TEAM3-1, "models/domination/dom_yellow.md3", 0, "domination/claim.wav", "", "Yellow team has captured a control point");
+       if(numteams > 3)
+               dom_spawnteam("Pink", COLOR_TEAM4-1, "models/domination/dom_pink.md3", 0, "domination/claim.wav", "", "Pink team has captured a control point");
+       dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, "", "", "");
+}
+
+void dom_DelayedInit() // Do this check with a delay so we can wait for teams to be set up.
+{
+       // if no teams are found, spawn defaults
+       if(find(world, classname, "dom_team") == world || autocvar_g_domination_teams_override >= 2)
+       {
+               print("No ""dom_team"" entities found on this map, creating them anyway.\n");
+               dom_spawnteams();
+       }
+       
+       ScoreRules_dom();
+}
+
+void dom_Initialize()
+{
+       precache_model("models/domination/dom_red.md3");
+       precache_model("models/domination/dom_blue.md3");
+       precache_model("models/domination/dom_yellow.md3");
+       precache_model("models/domination/dom_pink.md3");
+       precache_model("models/domination/dom_unclaimed.md3");
+       precache_sound("domination/claim.wav");
+       
+       addstat(STAT_DOM_TOTAL_PPS, AS_FLOAT, dom_total_pps);
+       addstat(STAT_DOM_PPS_RED, AS_FLOAT, dom_pps_red);
+       addstat(STAT_DOM_PPS_BLUE, AS_FLOAT, dom_pps_blue);
+       if(c3 >= 0) addstat(STAT_DOM_PPS_YELLOW, AS_FLOAT, dom_pps_yellow);
+       if(c4 >= 0) addstat(STAT_DOM_PPS_PINK, AS_FLOAT, dom_pps_pink);
+       
+       InitializeEntity(world, dom_DelayedInit, INITPRIO_GAMETYPE);
+}
+
+
+MUTATOR_DEFINITION(gamemode_domination)
+{
+       MUTATOR_HOOK(ClientConnect, dom_ClientConnect, CBC_ORDER_ANY);
+       MUTATOR_HOOK(HavocBot_ChooseRule, dom_BotRoles, CBC_ORDER_ANY);
+       
+       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();
+       }
+
+       MUTATOR_ONREMOVE
+       {
+               error("This is a game type and it cannot be removed at runtime.");
+       }
+
+       return 0;
+}
diff --git a/qcsrc/server/mutators/gamemode_domination.qh b/qcsrc/server/mutators/gamemode_domination.qh
new file mode 100644 (file)
index 0000000..a7d1853
--- /dev/null
@@ -0,0 +1,23 @@
+// these are needed since mutators are compiled last
+
+// score rule declarations
+#define ST_DOM_TICKS 1
+#define SP_DOM_TICKS 4
+#define SP_DOM_TAKES 5
+
+// pps: points per second
+.float dom_total_pps;
+.float dom_pps_red;
+.float dom_pps_blue;
+.float dom_pps_yellow;
+.float 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;
\ No newline at end of file
index aedfd6364c942ef5f1d967b38d0dac04fa8d202f..116eecbbf6b773613f7b299568df3ee31a5817d6 100644 (file)
@@ -476,9 +476,17 @@ MUTATOR_DEFINITION(gamemode_freezetag)
                freezetag_Initialize();
        }
 
+       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
        {
-               error("This is a game type and it cannot be removed at runtime.");
+               print("This is a game type and it cannot be removed at runtime.");
+               return -1;
        }
 
        return 0;
index 9f8647d9dfa5cab3a61a1a37735360bcaf4602ec..3be0b035272b013598402f91188aed6c003069b6 100644 (file)
@@ -426,9 +426,17 @@ MUTATOR_DEFINITION(gamemode_keepaway)
                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
        {
-               error("This is a game type and it cannot be removed at runtime.");
+               print("This is a game type and it cannot be removed at runtime.");
+               return -1;
        }
 
        return 0;
index 58d732d7f70b926c6cb13c6f93aa0f9a5807f48e..592fa327cfb7af6ae6b413369dcc6759573523a2 100644 (file)
@@ -1111,9 +1111,17 @@ MUTATOR_DEFINITION(gamemode_keyhunt)
                kh_Initialize();
        }
 
+       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
        {
-               error("This is a game type and it cannot be removed at runtime.");
+               print("This is a game type and it cannot be removed at runtime.");
+               return -1;
        }
 
        return 0;
index 5f27b60a55f882e851cc99fe5bc876fec16e5601..62cfd01ad867250673f406bfc68ab7ea99841671 100644 (file)
@@ -979,5 +979,18 @@ MUTATOR_DEFINITION(gamemode_nexball)
                InitializeEntity(world, nb_delayedinit, INITPRIO_GAMETYPE);
        }
 
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
+               // we actually cannot roll back nb_delayedinit here
+               // BUT: we don't need to! If this gets called, adding always
+               // succeeds.
+       }
+
+       MUTATOR_ONREMOVE
+       {
+               print("This is a game type and it cannot be removed at runtime.");
+               return -1;
+       }
+
        return 0;
 }
index 8137cc3047d35ed2822d17a9841415e83c991ff5..dd0152ed084311be37509b34e5471531386afdcc 100644 (file)
@@ -1696,7 +1696,14 @@ MUTATOR_DEFINITION(gamemode_onslaught)
        
        MUTATOR_ONADD
        {
-               //InitializeEntity(world, nb_delayedinit, INITPRIO_GAMETYPE);
+               if(time > 1) // game loads at time 1
+                       error("This is a game type and it cannot be added at runtime.");
+       }
+
+       MUTATOR_ONREMOVE
+       {
+               print("This is a game type and it cannot be removed at runtime.");
+               return -1;
        }
 
        return 0;
index 969c980a298dd44d7f085672385b4cf28650b815..1d6dd911eb7f958fad79b957c120e06575433530 100644 (file)
 // the jump part of the dodge cannot be ramped
 .float dodging_single_action;
 
-void dodging_Initialize() {
-       // print("dodging_Initialize\n");
-
-       self.last_FORWARD_KEY_time = 0;
-       self.last_BACKWARD_KEY_time = 0;
-       self.last_RIGHT_KEY_time = 0;
-       self.last_LEFT_KEY_time = 0;
-       self.last_dodging_time = 0;
-       self.dodging_action = 0;
-       self.dodging_velocity_gain = 0;
-       self.dodging_single_action = 0;
-       self.dodging_direction_x = 0;
-       self.dodging_direction_y = 0;
-}
-
 MUTATOR_HOOKFUNCTION(dodging_GetCvars) {
        GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_dodging_timeout, "cl_dodging_timeout");
        return 0;
@@ -283,14 +268,17 @@ MUTATOR_DEFINITION(mutator_dodging)
        MUTATOR_ONADD
        {
                g_dodging = 1;
-               dodging_Initialize();
        }
 
        // this just turns off the cvar.
-       MUTATOR_ONREMOVE
-       {        
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
                g_dodging = 0;
        }
 
+       MUTATOR_ONREMOVE
+       {
+       }
+
        return 0;
 }
index cc4f94324b8273fadf9fbc113c4dc286c6b8066b..6ee3e87b3c193e52354dd4e9e40301a8b6a756fc 100644 (file)
@@ -208,9 +208,19 @@ MUTATOR_DEFINITION(mutator_new_toys)
                        if(nt_IsNewToy(i))
                                get_weaponinfo(i).spawnflags &~= WEP_FLAG_MUTATORBLOCKED;
        }
+
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
+               float i;
+               for(i = WEP_FIRST; i <= WEP_LAST; ++i)
+                       if(nt_IsNewToy(i))
+                               get_weaponinfo(i).spawnflags |= WEP_FLAG_MUTATORBLOCKED;
+       }
+
        MUTATOR_ONREMOVE
        {
-               error("This cannot be removed at runtime\n");
+               print("This cannot be removed at runtime\n");
+               return -1;
        }
 
        return 0;
index dad19e4a3748f468c93db674b6b2e9c7e7e43a2c..b492ee60eb48add1046b6278fb171783bc85dea8 100644 (file)
@@ -247,6 +247,11 @@ MUTATOR_DEFINITION(mutator_nix)
                NIX_precache();
        }
 
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
+               // nothing to roll back
+       }
+
        MUTATOR_ONREMOVE
        {
                // as the PlayerSpawn hook will no longer run, NIX is turned off by this!
diff --git a/qcsrc/server/mutators/mutator_physical_items.qc b/qcsrc/server/mutators/mutator_physical_items.qc
new file mode 100644 (file)
index 0000000..74b7db2
--- /dev/null
@@ -0,0 +1,127 @@
+.vector spawn_origin, spawn_angles;
+
+void physical_item_think()
+{
+       self.nextthink = time;
+
+       self.alpha = self.owner.alpha; // apply fading and ghosting
+
+       if(!self.cnt) // map item, not dropped
+       {
+               // copy ghost item properties
+               self.colormap = self.owner.colormap;
+               self.colormod = self.owner.colormod;
+               self.glowmod = self.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(self.owner.nextthink > time) // awaiting respawn
+                       {
+                               setorigin(self, self.spawn_origin);
+                               self.angles = self.spawn_angles;
+                               self.solid = SOLID_NOT;
+                               self.movetype = MOVETYPE_NONE;
+                       }
+                       else
+                       {
+                               self.solid = SOLID_CORPSE;
+                               self.movetype = MOVETYPE_PHYSICS;
+                       }
+               }
+       }
+
+       if(!self.owner.modelindex)
+               remove(self); // the real item is gone, remove this
+}
+
+void physical_item_touch()
+{
+       if(!self.cnt) // not for dropped items
+       if (ITEM_TOUCH_NEEDKILL())
+       {
+               setorigin(self, self.spawn_origin);
+               self.angles = self.spawn_angles;
+       }
+}
+
+void physical_item_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+{
+       if(!self.cnt) // not for dropped items
+       if(ITEM_DAMAGE_NEEDKILL(deathtype))
+       {
+               setorigin(self, self.spawn_origin);
+               self.angles = self.spawn_angles;
+       }
+}
+
+MUTATOR_HOOKFUNCTION(item_spawning)
+{
+       if(self.owner == world && autocvar_g_physical_items <= 1)
+               return FALSE;
+       if (self.spawnflags & 1) // floating item
+               return FALSE;
+
+       // 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, self.model);
+       setsize(wep, self.mins, self.maxs);
+       setorigin(wep, self.origin);
+       wep.angles = self.angles;
+       wep.velocity = self.velocity;
+
+       wep.owner = self;
+       wep.solid = SOLID_CORPSE;
+       wep.movetype = MOVETYPE_PHYSICS;
+       wep.takedamage = DAMAGE_AIM;
+       wep.effects |= EF_NOMODELFLAGS; // disable the spinning
+       wep.colormap = self.owner.colormap;
+       wep.glowmod = self.owner.glowmod;
+       wep.damageforcescale = autocvar_g_physical_items_damageforcescale;
+       wep.dphitcontentsmask = self.dphitcontentsmask;
+       wep.cnt = (self.owner != world);
+
+       wep.think = physical_item_think;
+       wep.nextthink = time;
+       wep.touch = physical_item_touch;
+       wep.event_damage = physical_item_damage;
+
+       wep.spawn_origin = self.origin;
+       wep.spawn_angles = self.angles;
+
+       self.effects |= EF_NODRAW; // hide the original weapon
+       self.movetype = MOVETYPE_FOLLOW;
+       self.aiment = wep; // attach the original weapon
+
+       return FALSE;
+}
+
+MUTATOR_DEFINITION(mutator_physical_items)
+{
+       MUTATOR_HOOK(Item_Spawn, item_spawning, CBC_ORDER_ANY);
+
+       // check if we have a physics engine
+       MUTATOR_ONADD
+       {
+               if not(autocvar_physics_ode && checkextension("DP_PHYSICS_ODE"))
+               {
+                       dprint("Warning: Physical items are enabled but no physics engine can be used. Reverting to old items.\n");
+                       return -1;
+               }
+       }
+
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
+               // nothing to roll back
+       }
+
+       MUTATOR_ONREMOVE
+       {
+               print("This cannot be removed at runtime\n");
+               return -1;
+       }
+
+       return 0;
+}
index 87915f9af49b7cc1d175986ae26fe3df7613444a..0645b4805446b17cd1baafe035225d4b4793fceb 100644 (file)
@@ -518,13 +518,5 @@ MUTATOR_DEFINITION(mutator_superspec)
        //MUTATOR_HOOK(MakePlayerObserver, superspec_MakePlayerObserver, CBC_ORDER_ANY);
        MUTATOR_HOOK(ClientDisconnect, superspec_ClientDisconnect, CBC_ORDER_ANY);
 
-       MUTATOR_ONADD
-       {
-       }
-
-       MUTATOR_ONREMOVE
-       {
-       }
-
        return 0;
 }
index 2ac6094d339d578d174da5ec3ed06eed91fcad20..4bdcbb28234834629ecc13f8f52767fcd1e4ce90 100644 (file)
@@ -4,6 +4,7 @@ MUTATOR_DECLARATION(gamemode_keepaway);
 MUTATOR_DECLARATION(gamemode_ctf);
 MUTATOR_DECLARATION(gamemode_nexball);
 MUTATOR_DECLARATION(gamemode_onslaught);
+MUTATOR_DECLARATION(gamemode_domination);
 
 MUTATOR_DECLARATION(mutator_dodging);
 MUTATOR_DECLARATION(mutator_invincibleprojectiles);
@@ -11,6 +12,7 @@ MUTATOR_DECLARATION(mutator_new_toys);
 MUTATOR_DECLARATION(mutator_nix);
 MUTATOR_DECLARATION(mutator_rocketflying);
 MUTATOR_DECLARATION(mutator_spawn_near_teammate);
+MUTATOR_DECLARATION(mutator_physical_items);
 MUTATOR_DECLARATION(mutator_vampire);
 MUTATOR_DECLARATION(mutator_superspec);
 
index 4d97b8eece8e8fc585377a84d5167ea9dc3fea1a..391e317423dffd4dd37e628effe9a988b452267f 100644 (file)
@@ -817,6 +817,16 @@ MUTATOR_DEFINITION(sandbox)
                        sandbox_Database_Load();
        }
 
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
+               // nothing to roll back
+       }
+
+       MUTATOR_ONREMOVE
+       {
+               // nothing to remove
+       }
+
        return FALSE;
 }
 
index 53f39091393a616b6dcb33399df15b66fec2ebda..c01a25bbdc8228807ab8602cbd83b8aeaa2dfbb8 100644 (file)
@@ -25,6 +25,7 @@ void PlayerStats_Init() // initiated before InitGameplayMode so that scores are
        PlayerStats_AddEvent(PLAYERSTATS_MATCHES);
        PlayerStats_AddEvent(PLAYERSTATS_JOINS);
        PlayerStats_AddEvent(PLAYERSTATS_SCOREBOARD_VALID);
+       PlayerStats_AddEvent(PLAYERSTATS_SCOREBOARD_POS);
        PlayerStats_AddEvent(PLAYERSTATS_RANK);
 
     // accuracy stats
@@ -144,10 +145,10 @@ void PlayerStats_AddEvent(string event_id)
        }
 }
 
-void PlayerStats_Event(entity e, string event_id, float value)
+float PlayerStats_Event(entity e, string event_id, float value)
 {
        if((e.playerstats_id == "") || playerstats_db < 0)
-               return;
+               return 0;
        
        string key;
        float val;
@@ -155,6 +156,7 @@ void PlayerStats_Event(entity e, string event_id, float value)
        val = stof(db_get(playerstats_db, key));
        val += value;
        db_put(playerstats_db, key, ftos(val));
+       return val;
 }
 
 void PlayerStats_TeamScore(float t, string event_id, float value) // TODO: doesn't this remain unused?
@@ -223,7 +225,7 @@ void PlayerStats_ready(entity fh, entity pass, float status)
        switch(status)
        {
                case URL_READY_CANWRITE:
-                       url_fputs(fh, "V 5\n");
+                       url_fputs(fh, "V 6\n");
 #ifdef WATERMARK
                        url_fputs(fh, sprintf("R %s\n", WATERMARK));
 #endif
@@ -350,11 +352,11 @@ void PlayerStats_AddGlobalInfo(entity p)
        }
 
        db_put(playerstats_db, sprintf("%s:_playerid", p.playerstats_id), ftos(p.playerid));
-       
+
        if(p.cvar_cl_allow_uid2name == 1 || clienttype(p) == CLIENTTYPE_BOT)
                db_put(playerstats_db, sprintf("%s:_netname", p.playerstats_id), p.netname);
 
-    if(teamplay)
+       if(teamplay)
                db_put(playerstats_db, sprintf("%s:_team", p.playerstats_id), ftos(p.team));
 
        if(stof(db_get(playerstats_db, sprintf("%d:%s", p.playerstats_id, PLAYERSTATS_ALIVETIME))) > 0)
@@ -362,6 +364,15 @@ void PlayerStats_AddGlobalInfo(entity p)
 
        PlayerStats_Accuracy(p);
 
+       if(clienttype(p) == CLIENTTYPE_REAL)
+       {
+               if(p.latency_cnt)
+               {
+                       float latency = (p.latency_sum / p.latency_cnt);
+                       if(latency) { PlayerStats_Event(p, PLAYERSTATS_AVGLATENCY, latency); }
+               }
+       }
+
        strunzone(p.playerstats_id);
        p.playerstats_id = string_null;
 }
@@ -370,32 +381,30 @@ void PlayerStats_AddGlobalInfo(entity p)
 void PlayerStats_EndMatch(float finished)
 {
        entity p;
-       PlayerScore_Sort(score_dummyfield, 0);
-       PlayerScore_Sort(scoreboard_pos, 1);
+       PlayerScore_Sort(score_dummyfield, 0, 0, 0);
+       PlayerScore_Sort(scoreboard_pos, 1, 1, 1);
        FOR_EACH_CLIENT(p)
        {
-               //PlayerStats_Accuracy(p); // stats are already written with PlayerStats_AddGlobalInfo(entity), don't double them up.
-               
-               if((g_arena || g_lms || g_ca) && (p.alivetime <= 0)) { continue; }
-               else if(p.classname != "player") { continue; }
+               // add personal score rank
+               PlayerStats_Event(p, PLAYERSTATS_RANK, p.score_dummyfield);
 
-               if(clienttype(p) == CLIENTTYPE_REAL)
-               {
-                       if(p.latency_cnt)
-                       {
-                               float latency = (p.latency_sum / p.latency_cnt);
-                               if(latency) { PlayerStats_Event(p, PLAYERSTATS_AVGLATENCY, latency); }
-                       }
-               }
-               
-               PlayerScore_PlayerStats(p);
+               if(!p.scoreboard_pos)
+                       continue;
+
+               // scoreboard is valid!
                PlayerStats_Event(p, PLAYERSTATS_SCOREBOARD_VALID, 1);
+
+               // add scoreboard position
+               PlayerStats_Event(p, PLAYERSTATS_SCOREBOARD_POS, p.scoreboard_pos);
+
+               // add scoreboard data
+               PlayerScore_PlayerStats(p);
+
+               // if the match ended normally, add winning info
                if(finished)
                {
                        PlayerStats_Event(p, PLAYERSTATS_WINS, p.winning);
                        PlayerStats_Event(p, PLAYERSTATS_MATCHES, 1);
-                       PlayerStats_Event(p, PLAYERSTATS_RANK, p.score_dummyfield);
-                       PlayerStats_Event(p, PLAYERSTATS_SCOREBOARD_POS, p.scoreboard_pos);
                }
        }
 }
index f3806d50df917fe39a165663fd62fa3f0937578c..445d6f31cc66028fe2e318e66363dc8f6fa23f96 100644 (file)
@@ -38,7 +38,7 @@ void PlayerStats_AddTeam(float t);
 void PlayerStats_AddEvent(string event_id);
 
 // call on each event to track, or at player disconnect OR match end for "global stuff"
-void PlayerStats_Event(entity e, string event_id, float value);
+float PlayerStats_Event(entity e, string event_id, float value);
 
 // add a team score
 void PlayerStats_TeamScore(float t, string event_id, float value);
index 0ef33f85403daa2840106d6b6fb241e4dbf663ca..db93116109612dd97660f35306509908f43df41c 100644 (file)
@@ -31,6 +31,7 @@ defs.qh               // Should rename this, it has fields and globals
 mutators/base.qh
 mutators/mutators.qh
 mutators/gamemode_ctf.qh
+mutators/gamemode_domination.qh
 mutators/gamemode_keyhunt.qh // TODO fix this
 mutators/gamemode_keepaway.qh
 mutators/gamemode_nexball.qh 
@@ -142,7 +143,7 @@ t_plats.qc
 antilag.qc
 
 //ctf.qc
-domination.qc
+//domination.qc
 //mode_onslaught.qc
 //nexball.qc
 g_hook.qc
@@ -209,6 +210,7 @@ playerstats.qc
 
 mutators/base.qc
 mutators/gamemode_ctf.qc
+mutators/gamemode_domination.qc
 mutators/gamemode_freezetag.qc
 mutators/gamemode_keyhunt.qc
 mutators/gamemode_keepaway.qc
@@ -221,6 +223,7 @@ mutators/mutator_dodging.qc
 mutators/mutator_rocketflying.qc
 mutators/mutator_vampire.qc
 mutators/mutator_spawn_near_teammate.qc
+mutators/mutator_physical_items.qc
 mutators/sandbox.qc
 mutators/mutator_superspec.qc
 
index 6aaa19803a5410e1c96316ed7c27e68c8b46c5c3..2742888b6179bab1e088d5d4a222b778f01af3e2 100644 (file)
@@ -273,6 +273,10 @@ void race_SendTime(entity e, float cp, float t, float tvalid)
                }
                else
                {
+                       s = PlayerScore_Add(e, SP_RACE_FASTEST, 0);
+                       if(!s || t < s)
+                               PlayerScore_Add(e, SP_RACE_FASTEST, t - s);
+
                        s = PlayerScore_Add(e, SP_RACE_TIME, 0);
                        snew = TIME_ENCODE(time - game_starttime);
                        PlayerScore_Add(e, SP_RACE_TIME, snew - s);
@@ -1026,7 +1030,7 @@ void race_ReadyRestart()
        Score_NicePrint(world);
 
        race_ClearRecords();
-       PlayerScore_Sort(race_place, 1);
+       PlayerScore_Sort(race_place, 0, 1, 0);
 
        entity e;
        FOR_EACH_CLIENT(e)
index c2307ad3756208e08b7329ce90e7c2c8712a004a..0df5b69af06fbc09c3809ddca9604a3d51a0f412 100644 (file)
@@ -248,18 +248,18 @@ float PlayerScore_SendEntity(entity to, float sendflags)
        return TRUE;
 }
 
-void PlayerScore_Clear(entity player)
+float PlayerScore_Clear(entity player)
 {
        entity sk;
        float i;
 
        if(teamscores_entities_count)
-               return;
+               return 0;
 
-       if(g_lms) return;
-       if(g_arena || g_ca) return;
-       if(g_cts) return; // in CTS, you don't lose score by observing
-       if(g_race && g_race_qualifying) return; // in qualifying, you don't lose score by observing
+       if(g_lms) return 0;
+       if(g_arena || g_ca) return 0;
+       if(g_cts) return 0; // in CTS, you don't lose score by observing
+       if(g_race && g_race_qualifying) return 0; // in qualifying, you don't lose score by observing
 
        sk = player.scorekeeper;
        for(i = 0; i < MAX_SCORE; ++i)
@@ -269,6 +269,8 @@ void PlayerScore_Clear(entity player)
                                sk.SendFlags |= pow(2, i);
                sk.(scores[i]) = 0;
        }
+
+       return 1;
 }
 
 void Score_ClearAll()
@@ -665,23 +667,27 @@ string GetTeamScoreString(float tm, float shortString)
        return out;
 }
 
-float PlayerTeamScore_Compare(entity p1, entity p2, float strict)
+float PlayerTeamScore_Compare(entity p1, entity p2, float teams, float strict)
 {
-       if(teamscores_entities_count)
+       if(teams && teamscores_entities_count)
+       {
                if(p1.team != p2.team)
                {
                        entity t1, t2;
                        float r;
                        t1 = teamscorekeepers[p1.team - 1];
                        t2 = teamscorekeepers[p2.team - 1];
-                       r = TeamScore_Compare(t1, t2, strict);
+                       r = TeamScore_Compare(t1, t2, ((teams >= 0) ? 1 : strict));
                        return r;
                }
+               if(teams < 0)
+                       return 0;
+       }
        
        return PlayerScore_Compare(p1.scorekeeper, p2.scorekeeper, strict);
 }
 
-entity PlayerScore_Sort(.float field, float strict)
+entity PlayerScore_Sort(.float field, float teams, float strict, float nospectators)
 {
        entity p, plist, pprev, pbest, pbestprev, pfirst, plast;
        float i, j;
@@ -691,8 +697,12 @@ entity PlayerScore_Sort(.float field, float strict)
        FOR_EACH_CLIENT(p)
                p.field = 0;
 
-       FOR_EACH_PLAYER(p) if(p.scorekeeper)
+       FOR_EACH_CLIENT(p) if(p.scorekeeper)
        {
+               if(nospectators)
+                       if(p.frags == FRAGS_SPECTATOR)
+                               continue;
+
                p.chain = plist;
                plist = p;
        }
@@ -707,7 +717,7 @@ entity PlayerScore_Sort(.float field, float strict)
                pbest = plist;
                for(p = plist; (pprev = p), (p = p.chain); )
                {
-                       if(PlayerTeamScore_Compare(p, pbest, strict) > 0)
+                       if(PlayerTeamScore_Compare(p, pbest, teams, strict) > 0)
                        {
                                pbest = p;
                                pbestprev = pprev;
@@ -722,7 +732,7 @@ entity PlayerScore_Sort(.float field, float strict)
                pbest.chain = world;
 
                ++i;
-               if(!plast || PlayerTeamScore_Compare(plast, pbest, 0))
+               if(!plast || PlayerTeamScore_Compare(plast, pbest, teams, 0))
                        j = i;
 
                pbest.field = j;
@@ -867,7 +877,7 @@ void Score_NicePrint(entity to)
                        ++t;
        w = bound(6, floor(SCORESWIDTH / t - 1), 9);
 
-       p = PlayerScore_Sort(score_dummyfield, 1);
+       p = PlayerScore_Sort(score_dummyfield, 1, 1, 0);
        t = -1;
 
        if(!teamscores_entities_count)
index 96f15bc804266766b2180ab56f22e99a1d11cf61..5960b9f427fc3a459550dd0585fd8eacee686134 100644 (file)
@@ -25,8 +25,9 @@ float PlayerScore_Add(entity player, float scorefield, float score);
  * Initialize the score of this player if needed.
  * Does nothing in teamplay.
  * Use that when a spectator becomes a player.
+ * Returns whether clearing has been performed
  */
-void PlayerScore_Clear(entity player);
+float PlayerScore_Clear(entity player);
 
 /**
  * Adds a score to the player's team's scores.
@@ -117,5 +118,8 @@ string GetTeamScoreString(float tm, float shortString);
  * Sorts the players and stores their place in the given field, starting with
  * 1. Non-players get 0 written into that field.
  * Returns the beginning of a sorted chain of the non-spectators.
+ * teams: >0: sort by teams first (always strict ordering); <0: sort by teams only (respects strict flag)
+ * strict: return a strict ordering
+ * nospectators: exclude spectators
  */
-entity PlayerScore_Sort(.float field, float strict);
+entity PlayerScore_Sort(.float field, float teams, float strict, float nospectators);
index c4021fc397ea3d2cf28c67e004bb9afdde68e1aa..13fd49f29bf58e72a57ca3514b2f19103fc0d0bf 100644 (file)
@@ -44,26 +44,6 @@ void ScoreRules_generic()
        ScoreRules_basics_end();
 }
 
-// g_domination
-#define ST_DOM_TICKS 1
-#define SP_DOM_TICKS 4
-#define SP_DOM_TAKES 5
-void ScoreRules_dom()
-{
-       float sp_domticks, sp_score;
-       sp_score = sp_domticks = 0;
-       if(autocvar_g_domination_disable_frags)
-               sp_domticks = SFL_SORT_PRIO_PRIMARY;
-       else
-               sp_score = SFL_SORT_PRIO_PRIMARY;
-       CheckAllowedTeams(world);
-       ScoreRules_basics(((c4>=0) ? 4 : (c3>=0) ? 3 : 2), sp_score, sp_score, TRUE);
-       ScoreInfo_SetLabel_TeamScore  (ST_DOM_TICKS,    "ticks",     sp_domticks);
-       ScoreInfo_SetLabel_PlayerScore(SP_DOM_TICKS,    "ticks",     sp_domticks);
-       ScoreInfo_SetLabel_PlayerScore(SP_DOM_TAKES,    "takes",     0);
-       ScoreRules_basics_end();
-}
-
 // LMS stuff
 #define SP_LMS_LIVES 4
 #define SP_LMS_RANK 5
@@ -99,9 +79,8 @@ void ScoreRules_kh(float teams)
 // Race stuff
 #define ST_RACE_LAPS 1
 #define SP_RACE_LAPS 4
-#define SP_RACE_FASTEST 5
 #define SP_RACE_TIME 5
-//#define SP_RACE_RANK 6
+#define SP_RACE_FASTEST 6
 void ScoreRules_race()
 {
        ScoreRules_basics(race_teams, 0, 0, FALSE);
@@ -110,7 +89,7 @@ void ScoreRules_race()
                ScoreInfo_SetLabel_TeamScore(  ST_RACE_LAPS,    "laps",      SFL_SORT_PRIO_PRIMARY);
                ScoreInfo_SetLabel_PlayerScore(SP_RACE_LAPS,    "laps",      SFL_SORT_PRIO_PRIMARY);
                ScoreInfo_SetLabel_PlayerScore(SP_RACE_TIME,    "time",      SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER | SFL_TIME);
-               //ScoreInfo_SetLabel_PlayerScore(SP_RACE_RANK,    "rank",      SFL_LOWER_IS_BETTER | SFL_RANK | SFL_ALLOW_HIDE);
+               ScoreInfo_SetLabel_PlayerScore(SP_RACE_FASTEST, "fastest",   SFL_LOWER_IS_BETTER | SFL_TIME);
        }
        else if(g_race_qualifying)
        {
@@ -118,9 +97,9 @@ void ScoreRules_race()
        }
        else
        {
-               //ScoreInfo_SetLabel_TeamScore(  ST_RACE_LAPS,    "laps",      0);
                ScoreInfo_SetLabel_PlayerScore(SP_RACE_LAPS,    "laps",      SFL_SORT_PRIO_PRIMARY);
                ScoreInfo_SetLabel_PlayerScore(SP_RACE_TIME,    "time",      SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER | SFL_TIME);
+               ScoreInfo_SetLabel_PlayerScore(SP_RACE_FASTEST, "fastest",   SFL_LOWER_IS_BETTER | SFL_TIME);
        }
        ScoreRules_basics_end();
 }
index bd6b9962cc2d623b81a13db5e3733c3b562e5a17..bf70da0286a837b98fb68fb2adbff927f8d30254 100644 (file)
@@ -1214,6 +1214,14 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime,
                Item_Reset();
         
     Net_LinkEntity(self, FALSE, 0, ItemSend);
+
+       // call this hook after everything else has been done
+       if(MUTATOR_CALLHOOK(Item_Spawn))
+       {
+               startitem_failed = TRUE;
+               remove(self);
+               return;
+       }
 }
 
 /* replace items in minstagib
index b8f2f3ac8746ea4e02daae0936833d2086954699..1ed2f533b3a923c93981ef14ad5403eb847a0f25 100644 (file)
@@ -52,7 +52,6 @@ string TeamNoName(float t)
        return "Neutral Team";
 }
 
-void dom_init();
 void runematch_init();
 void tdm_init();
 void entcs_init();
@@ -134,7 +133,7 @@ void InitGameplayMode()
                ActivateTeamplay();
                fraglimit_override = autocvar_g_domination_point_limit;
                leadlimit_override = autocvar_g_domination_point_leadlimit;
-               dom_init();
+               MUTATOR_ADD(gamemode_domination);
                have_team_spawns = -1; // request team spawns
        }
 
index f1d3890447140399b4206ac232694483aa42c1b1..7d5a787d0061d914ca9159d1ebe6a42108057690 100644 (file)
@@ -48,7 +48,7 @@ float VHF_PLAYERSLOT    = 16384;    /// This ent is a player slot on a multi-per
 //.void() vehicle_spawn;
 
 void vehicles_exit(float eject);
-var .void(float exit_flags) vehicle_exit;
+.void(float exit_flags) vehicle_exit;
 float VHEF_NORMAL = 0;  /// User pressed exit key
 float VHEF_EJECT  = 1;  /// User pressed exit key 3 times fast (not implemented) or vehile is dying
 float VHEF_RELESE = 2;  /// Release ownership, client possibly allready dissconnected / went spec / changed team / used "kill" (not implemented)
@@ -57,15 +57,15 @@ float SVC_SETVIEWPORT   = 5;   // Net.Protocol 0x05
 float SVC_SETVIEWANGLES = 10;  // Net.Protocol 0x0A
 float SVC_UPDATEENTITY  = 128; // Net.Protocol 0x80
 
-var .void() vehicle_enter;  /// Vehicles custom funciton to be executed when owner exit it
-var .void() vehicle_die;    /// Vehicles custom function to be executed when vehile die
+.void() vehicle_enter;  /// Vehicles custom funciton to be executed when owner exit it
+.void() vehicle_die;    /// Vehicles custom function to be executed when vehile die
 #define VHSF_NORMAL 0
 #define VHSF_FACTORY 2
-var .void(float _spawnflag) vehicle_spawn;  /// Vehicles custom fucntion to be efecuted when vehicle (re)spawns
-var .float(float _imp) vehicles_impusle;
+.void(float _spawnflag) vehicle_spawn;  /// Vehicles custom fucntion to be efecuted when vehicle (re)spawns
+.float(float _imp) vehicles_impusle;
 .float vehicle_weapon2mode = volly_counter;
 
-//§ var .void() vehicle_factory()
+//§ .void() vehicle_factory()
 
 #ifdef VEHICLES_USE_ODE
 void(entity e, float physics_enabled) physics_enable = #540; // enable or disable physics on object
index 1f1137e4b845b151321d1501c5dbdca9e325b2fd..a2d3b988734a6d75e9ff2c3fd3db737573821e30 100644 (file)
@@ -34,8 +34,8 @@ void W_MinstaNex_Attack (void)
                }
                if(damage_goodhits && self.minstanex_lasthit)
                {
-                       if(AnnounceTo(self, "impressive"))
-                               damage_goodhits = 0; // only every second time
+                       AnnounceTo(self, "impressive");
+                       damage_goodhits = 0; // only every second time
                }
        }