// 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"
--- /dev/null
+// 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 "+ + + + + + + + + + [ > + + + + + + + > + + + + + + + + + + > + + + > + < < < < - ] > + + . > + . + + + + + + + . . + + + . > + + . < < + + + + + + + + + + + + + + + . > . + + + . - - - - - - . - - - - - - - - . > + . > ."
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)"
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)"
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\""
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)))
void Net_ReadSpawn()
{
zoomin_effect = 1;
- current_viewzoom = 0.6;
+ current_viewzoom = (1 / bound(1, autocvar_cl_spawnzoom_factor, 16));
}
void Net_TeamNagger()
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;
}
}
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;
// 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
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
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)
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
// 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.
{
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);
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
{
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;
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;
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;
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")));
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)
if(k != -1)
localcmd("\nbind \"", keynumtostring(k), "\" \"", to, "\"\n");
}
+ if(n)
+ cvar_set("_hud_showbinds_reload", "1");
}
void XonoticKeyBinder_configureXonoticKeyBinder(entity me)
{
return;
me.keyGrabButton.forcePressed = 1;
+ me.clearButton.disabled = 1;
keyGrabber = me;
}
void XonoticKeyBinder_keyGrabbed(entity me, float key, float ascii)
string func;
me.keyGrabButton.forcePressed = 0;
+ me.clearButton.disabled = 0;
+
if(key == K_ESCAPE)
return;
}
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)
{
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;
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;
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)
{
--- /dev/null
+
+/*
+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);
+}
+
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;
// 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()
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)
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;
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;
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;
}
{
if(self.version_mismatch)
{
+ self.frags = FRAGS_SPECTATOR;
Spawnqueue_Unmark(self);
Spawnqueue_Remove(self);
}
else
{
+ self.frags = FRAGS_LMS_LOSER;
Spawnqueue_Insert(self);
}
}
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;
}
self.weaponname = "";
self.switchingweapon = 0;
- if(!self.alivetime)
- self.alivetime = time;
+ if(!inWarmupStage)
+ if(!self.alivetime)
+ self.alivetime = time;
antilag_clear(self);
race_PreSpawnObserver();
- //if(g_domination)
- // dom_player_join_team(self);
-
// identify the right forced team
if(autocvar_g_campaign)
{
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)
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);
wep.savenextthink = wep.nextthink;
wep.nextthink = min(wep.nextthink, time + 0.5);
wep.pickup_anyway = TRUE; // these are ALWAYS pickable
+
return s;
}
}
// 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:
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
+++ /dev/null
-
-/*
-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);
-}
-
WaypointSprite_Init();
- //if (g_domination)
- // dom_init();
-
GameLogInit(); // prepare everything
// NOTE for matchid:
// changing the logic generating it is okay. But:
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);
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"))
// 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)
#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
#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)
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
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;
--- /dev/null
+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;
+}
--- /dev/null
+// 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
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;
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;
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;
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;
}
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;
// 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;
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;
}
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;
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!
--- /dev/null
+.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;
+}
//MUTATOR_HOOK(MakePlayerObserver, superspec_MakePlayerObserver, CBC_ORDER_ANY);
MUTATOR_HOOK(ClientDisconnect, superspec_ClientDisconnect, CBC_ORDER_ANY);
- MUTATOR_ONADD
- {
- }
-
- MUTATOR_ONREMOVE
- {
- }
-
return 0;
}
MUTATOR_DECLARATION(gamemode_ctf);
MUTATOR_DECLARATION(gamemode_nexball);
MUTATOR_DECLARATION(gamemode_onslaught);
+MUTATOR_DECLARATION(gamemode_domination);
MUTATOR_DECLARATION(mutator_dodging);
MUTATOR_DECLARATION(mutator_invincibleprojectiles);
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);
sandbox_Database_Load();
}
+ MUTATOR_ONROLLBACK_OR_REMOVE
+ {
+ // nothing to roll back
+ }
+
+ MUTATOR_ONREMOVE
+ {
+ // nothing to remove
+ }
+
return FALSE;
}
PlayerStats_AddEvent(PLAYERSTATS_MATCHES);
PlayerStats_AddEvent(PLAYERSTATS_JOINS);
PlayerStats_AddEvent(PLAYERSTATS_SCOREBOARD_VALID);
+ PlayerStats_AddEvent(PLAYERSTATS_SCOREBOARD_POS);
PlayerStats_AddEvent(PLAYERSTATS_RANK);
// accuracy stats
}
}
-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;
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?
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
}
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)
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;
}
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);
}
}
}
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);
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
antilag.qc
//ctf.qc
-domination.qc
+//domination.qc
//mode_onslaught.qc
//nexball.qc
g_hook.qc
mutators/base.qc
mutators/gamemode_ctf.qc
+mutators/gamemode_domination.qc
mutators/gamemode_freezetag.qc
mutators/gamemode_keyhunt.qc
mutators/gamemode_keepaway.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
}
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);
Score_NicePrint(world);
race_ClearRecords();
- PlayerScore_Sort(race_place, 1);
+ PlayerScore_Sort(race_place, 0, 1, 0);
entity e;
FOR_EACH_CLIENT(e)
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)
sk.SendFlags |= pow(2, i);
sk.(scores[i]) = 0;
}
+
+ return 1;
}
void Score_ClearAll()
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;
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;
}
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;
pbest.chain = world;
++i;
- if(!plast || PlayerTeamScore_Compare(plast, pbest, 0))
+ if(!plast || PlayerTeamScore_Compare(plast, pbest, teams, 0))
j = i;
pbest.field = j;
++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)
* 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.
* 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);
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
// 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);
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)
{
}
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();
}
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
return "Neutral Team";
}
-void dom_init();
void runematch_init();
void tdm_init();
void entcs_init();
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
}
//.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)
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
}
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
}
}