alias +hook +button6
alias -hook -button6
alias use "impulse 21"
-alias +use use // always send that impulse AND press the key (+use is engine internal command and executes anyway)
-set cl_newusekeysupported 1 // indicates that we always send the use impulse too, so they do not need to be synthesized
alias ready "cmd ready"
alias lockteams "sv_cmd lockteams"
alias unlockteams "sv_cmd unlockteams"
bind r reload
bind BACKSPACE dropweapon
bind g dropweapon
-bind f use
+bind f +use
// misc
bind e +hook
prvm_leaktest_ignore_classnames "ctf_team dom_team tdm_team"
sv_allowdownloads_inarchive 1 // for csprogs.dat
+sv_allowdownloads 0 // download protocol is evil
set g_jump_grunt 0 "Do you make a grunting noise every time you jump? Is it the same grunting noise every time?"
seta cl_clippedspectating 1 "movement collision for spectators so that you can't pass through walls and such. (client setting) NOTE: reconnect or use sendcvar command to update the choice."
+seta cl_autoscreenshot 0 "client option to automatically take a screenshot once the map has ended (see also sv_autoscreenshot)"
+
// must be at the bottom of this file:
// alias for switching the teamselect menu
alias menu_showteamselect "menu_cmd directmenu TeamSelect"
r_glsl_deluxemapping 1
r_glsl_offsetmapping 1
r_glsl_offsetmapping_reliefmapping 0
-r_hdr 0
r_motionblur 0.5
r_shadow_gloss 1
r_shadow_realtime_dlight 1
r_glsl_deluxemapping 0
r_glsl_offsetmapping 0
r_glsl_offsetmapping_reliefmapping 0
-r_hdr 0
r_motionblur 0
r_shadow_gloss 0
r_shadow_realtime_dlight 0
r_glsl_deluxemapping 0
r_glsl_offsetmapping 0
r_glsl_offsetmapping_reliefmapping 0
-r_hdr 0
r_motionblur 0
r_shadow_gloss 0
r_shadow_realtime_dlight 1
r_glsl_deluxemapping 1
r_glsl_offsetmapping 0
r_glsl_offsetmapping_reliefmapping 0
-r_hdr 0
r_motionblur 0
r_shadow_gloss 1
r_shadow_realtime_dlight 1
r_glsl_deluxemapping 0
r_glsl_offsetmapping 0
r_glsl_offsetmapping_reliefmapping 0
-r_hdr 0
r_motionblur 0
r_shadow_gloss 0
r_shadow_realtime_dlight 0
r_glsl_deluxemapping 1
r_glsl_offsetmapping 1
r_glsl_offsetmapping_reliefmapping 1
-r_hdr 2
r_motionblur 0.5
r_shadow_gloss 1
r_shadow_realtime_dlight 1
r_glsl_deluxemapping 1
r_glsl_offsetmapping 1
r_glsl_offsetmapping_reliefmapping 0
-r_hdr 0
r_motionblur 0.5
r_shadow_gloss 1
r_shadow_realtime_dlight 1
{
float argc;
// Tokenize String
- //argc = tokenize(strMessage);
argc = tokenize_console(strMessage);
// Acquire Command
#ifdef DP_CSQC_ENTITY_REMOVE_IS_B0RKED
if(self.enttype)
- if(t != self.enttype)
+ {
+ if(t != self.enttype || bIsNewEntity)
{
//print(_("A CSQC entity changed its type!\n"));
- print(sprintf(_("A CSQC entity changed its type! (edict: %d, classname: %s)\n"), num_for_edict(self), self.classname));
+ print(sprintf(_("A CSQC entity changed its type! (edict: %d, type: %d -> %d)\n"), num_for_edict(self), self.enttype, t));
Ent_Remove();
bIsNewEntity = 1;
}
+ }
+ else
+ {
+ if(!bIsNewEntity)
+ {
+ print(sprintf(_("A CSQC entity appeared out of nowhere! (edict: %d, type: %d)\n"), num_for_edict(self), t));
+ bIsNewEntity = 1;
+ }
+ }
#endif
self.enttype = t;
switch(t)
}
}
- if(autocvar_hud_damage && !autocvar_chase_active)
+ if(autocvar_hud_damage)
{
splash_size_x = max(vid_conwidth, vid_conheight);
splash_size_y = max(vid_conwidth, vid_conheight);
// 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_cl_gentle_damage || autocvar_cl_gentle)
+ if(autocvar_chase_active >= 0) // not while the event chase camera is active
{
- if(autocvar_cl_gentle_damage == 2)
+ if(autocvar_cl_gentle_damage || autocvar_cl_gentle)
{
- if(myhealth_flash < pain_threshold) // only randomize when the flash is gone
+ if(autocvar_cl_gentle_damage == 2)
{
- myhealth_gentlergb = eX * random() + eY * random() + eZ * random();
+ if(myhealth_flash < pain_threshold) // only randomize when the flash is gone
+ {
+ 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
- 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);
+ drawpic(splash_pos, "gfx/blood", splash_size, stov(autocvar_hud_damage_color), 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)
+ 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(autocvar_hud_damage_blur && myhealth_flash_temp)
{
if(acc_color_levels)
strunzone(acc_color_levels);
acc_color_levels = strzone(autocvar_accuracy_color_levels);
- acc_levels = tokenize(acc_color_levels);
+ acc_levels = tokenize_console(acc_color_levels);
if (acc_levels > MAX_ACCURACY_LEVELS)
acc_levels = MAX_ACCURACY_LEVELS;
string con_keys;
float keys;
con_keys = findkeysforcommand("toggleconsole");
- keys = tokenize(con_keys);
+ keys = tokenize(con_keys); // findkeysforcommand returns data for this
float hit_con_bind, i;
for (i = 0; i < keys; ++i)
return -1;
return ((a * 22 + b) * 22 + c) * 22 + d;
}
+
+float lowestbit(float f)
+{
+ f &~= f * 2;
+ f &~= f * 4;
+ f &~= f * 16;
+ f &~= f * 256;
+ f &~= f * 65536;
+ return f;
+}
float GameCommand_Generic(string cmd);
// returns TRUE if handled, FALSE otherwise
-// uses tokenize on its argument!
+// tokenizes its input!
// iterative depth-first search, with fields that go "up", "down left" and "right" in a tree
// for each element, funcPre is called first, then funcPre and funcPost for all its children, and funcPost last
return;
}
-#if 0
- if(argv(0) == "tokentest")
- {
- string s;
- float i, n;
-
- print("SANE tokenizer:\n");
- s = cvar_string("tokentest");
- n = tokenize_console_force_builtin(s);
- for(i = -n; i < n; ++i)
- {
- print("token ", ftos(i), ": '", argv(i), "' = ");
- print(ftos(argv_start_index(i)), " to ", ftos(argv_end_index(i)), "\n");
- }
- print(".\n");
-
- print("INSANE tokenizer:\n");
- s = cvar_string("tokentest");
- n = tokenize(s);
- for(i = -n; i < n; ++i)
- {
- print("token ", ftos(i), ": '", argv(i), "' = ");
- print(ftos(argv_start_index(i)), " to ", ftos(argv_end_index(i)), "\n");
- }
- print(".\n");
-
- print("EMULATED tokenizer:\n");
- s = cvar_string("tokentest");
- n = tokenize_console_force_emulation(s);
- for(i = -n; i < n; ++i)
- {
- print("token ", ftos(i), ": '", argv(i), "' = ");
- print(ftos(argv_start_index(i)), " to ", ftos(argv_end_index(i)), "\n");
- }
- print(".\n");
- return;
- }
-#endif
-
print(_("Invalid command. For a list of supported commands, try menu_cmd help.\n"));
}
me.TD(me, 1, 1, e = makeXonoticCheckBox(0, "r_coronas", _("Coronas")));
me.TD(me, 1, 2, e = makeXonoticCheckBox(0, "r_coronas_occlusionquery", _("Use Occlusion Queries")));
me.TR(me);
- me.TD(me, 1, 1.2, e = makeXonoticCheckBox(0, "r_bloom", _("Bloom")));
- setDependent(e, "r_hdr", 0, 0);
- me.TD(me, 1, 1.8, e = makeXonoticCheckBoxEx(2, 0, "r_hdr", _("High Dynamic Range (HDR)")));
+ me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "r_bloom", _("High Dynamic Range (HDR)")));
me.TR(me);
s = makeXonoticSlider(0.1, 1, 0.1, "r_motionblur");
me.TR(me);
me.TD(me, 1, 1, e = makeXonoticCheckBox(0, "vid_fullscreen", _("Full screen")));
me.TD(me, 1, 2, e = makeXonoticCheckBox(0, "vid_vsync", _("Vertical Synchronization")));
- me.TR(me);
me.TR(me);
me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "vid_gl20", _("Use OpenGL 2.0 shaders (GLSL)")));
me.TR(me);
me.TDempty(me, 0.2);
me.TD(me, 1, 2.8, e = makeXonoticCheckBox(0, "v_glslgamma", _("Use GLSL to handle color control")));
setDependent(e, "vid_gl20", 1, 1);
- me.TR(me);
me.TR(me);
me.TD(me, 1, 3, e = makeXonoticTextLabel(0, _("Vertex Buffer Objects (VBOs)")));
me.TR(me);
me.TDempty(me, 0.2);
me.TD(me, 1, 0.9, e = makeXonoticRadioButton(1, "gl_vbo", "2", _("Vertices")));
me.TD(me, 1, 1.9, e = makeXonoticRadioButton(1, "gl_vbo", "1", _("Vertices and Triangles")));
+ me.TR(me);
+ me.TD(me, 1, 3, e = makeXonoticTextLabel(0, _("Frame Buffer Objects (FBOs)")));
+ me.TR(me);
+ me.TDempty(me, 0.2);
+ me.TD(me, 1, 0.9, e = makeXonoticRadioButton(1, "r_viewfbo", "0", _("None")));
+ me.TD(me, 1, 0.9, e = makeXonoticRadioButton(1, "r_viewfbo", "1", _("8bit")));
+ me.TR(me);
+ me.TDempty(me, 0.2);
+ me.TD(me, 1, 0.9, e = makeXonoticRadioButton(1, "r_viewfbo", "2", _("16bit HDR")));
+ me.TD(me, 1, 0.9, e = makeXonoticRadioButton(1, "r_viewfbo", "3", _("32bit HDR")));
me.TR(me);
me.TR(me);
me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Depth first:")));
e.addValue(e, ZCTX(_("DF^World")), "1");
e.addValue(e, ZCTX(_("DF^All")), "2");
e.configureXonoticTextSliderValues(e);
- me.TR(me);
- if(cvar_type("apple_multithreadedgl") & CVAR_TYPEFLAG_ENGINE)
- me.TD(me, 1, 3, e = makeXonoticCheckBox(1, "apple_multithreadedgl", _("Disable multithreaded OpenGL")));
me.TR(me);
me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "gl_finish", _("Wait for GPU to finish each frame")));
void antilag_record(entity e, float t)
{
+ if(e.vehicle)
+ antilag_record(e.vehicle, t);
+
if(time < e.(antilag_times[e.antilag_index]))
return;
e.antilag_index = e.antilag_index + 1;
if(e.antilag_debug)
te_spark(antilag_takebackorigin(e, t - e.antilag_debug), '0 0 0', 32);
+
}
// finds the index BEFORE t
vector antilag_takebackavgvelocity(entity e, float t0, float t1)
{
vector o0, o1;
+
if(t0 >= t1)
return '0 0 0';
o0 = antilag_takebackorigin(e, t0);
void antilag_takeback(entity e, float t)
{
+ if(e.vehicle)
+ antilag_takeback(e.vehicle, t);
+
e.antilag_saved_origin = e.origin;
setorigin(e, antilag_takebackorigin(e, t));
}
void antilag_restore(entity e)
{
+ if(e.vehicle)
+ antilag_restore(e.vehicle);
+
setorigin(e, e.antilag_saved_origin);
}
void antilag_record(entity e, float t);
-float antilag_find(entity e, float t);
vector antilag_takebackorigin(entity e, float t);
vector antilag_takebackavgvelocity(entity e, float t0, float t1);
void antilag_takeback(entity e, float t);
WriteByte(MSG_ONE, SVC_SETVIEW);
WriteEntity(MSG_ONE, self);
}
+
+ // reset player keys
+ self.itemkeys = 0;
// player is dead and becomes observer
// FIXME fix LMS scoring for new system
MUTATOR_CALLHOOK(PlayerPreThink);
- if(!self.cvar_cl_newusekeysupported)
+ if(!self.cvar_cl_newusekeysupported) // FIXME remove this - it was a stupid idea to begin with, we can JUST use the button
{
if(self.BUTTON_USE && !self.usekeypressed)
PlayerUseKey();
.float checkfail;
void SV_ParseClientCommand(string s) {
- string cmd;
- float tokens;
float i;
entity e;
- tokens = tokenize_console(s);
-
- cmd = strtolower(argv(0));
- if(cmd != "reportcvar")
- if(cmd != "sentcvar")
- if(cmd != "pause")
- if(cmd != "prespawn")
- if(cmd != "spawn")
- if(cmd != "begin")
+ cmd_argc = tokenize_console(s);
+ cmd_string = s;
+ cmd_name = strtolower(argv(0));
+ if(cmd_name != "reportcvar")
+ if(cmd_name != "sentcvar")
+ if(cmd_name != "pause")
+ if(cmd_name != "prespawn")
+ if(cmd_name != "spawn")
+ if(cmd_name != "begin")
{
if(cmd_floodcheck())
return;
}
+ if(MUTATOR_CALLHOOK(SV_ParseClientCommand))
+ return; // already handled
+
if(GameCommand_Vote(s, self)) {
return;
} else if(GameCommand_MapVote(argv(0))) {
return;
- } else if(cmd == "checkfail") {
+ } else if(cmd_name == "checkfail") {
print(sprintf("CHECKFAIL: %s (%s) epically failed check %s\n", self.netname, self.netaddress, substring(s, argv_start_index(1), argv_end_index(-1) - argv_start_index(1))));
self.checkfail = 1;
- } else if(cmd == "autoswitch") {
+ } else if(cmd_name == "autoswitch") {
// be backwards compatible with older clients (enabled)
self.autoswitch = ("0" != argv(1));
string autoswitchmsg;
autoswitchmsg = "off";
}
sprint(self, strcat("^1autoswitch turned ", autoswitchmsg, "\n"));
- } else if(cmd == "clientversion") {
+ } else if(cmd_name == "clientversion") {
if not(self.flags & FL_CLIENT)
return;
if (argv(1) == "$gameversion") {
self.classname = "observer";
stuffcmd(self,"menu_showteamselect\n");
}
- } else if(cmd == "reportcvar") { // old system
+ } else if(cmd_name == "reportcvar") { // old system
if(substring(argv(2), 0, 1) == "$") // undefined cvar: use the default value on the server then
{
s = strcat(substring(s, argv_start_index(0), argv_end_index(1) - argv_start_index(0)), " \"", cvar_defstring(argv(1)), "\"");
- tokens = tokenize_console(s);
+ cmd_argc = tokenize_console(s);
}
GetCvars(1);
- } else if(cmd == "sentcvar") { // new system
- if(tokens == 2) // undefined cvar: use the default value on the server then
+ } else if(cmd_name == "sentcvar") { // new system
+ if(cmd_argc == 2) // undefined cvar: use the default value on the server then
{
s = strcat(substring(s, argv_start_index(0), argv_end_index(1) - argv_start_index(0)), " \"", cvar_defstring(argv(1)), "\"");
- tokens = tokenize_console(s);
+ cmd_argc = tokenize_console(s);
}
GetCvars(1);
- } else if(cmd == "spectate") {
+ } else if(cmd_name == "spectate") {
if(cmd_floodcheck())
return;
if not(self.flags & FL_CLIENT)
sprint(self, "WARNING: you will spectate in the next round.\n");
self.caplayer = 0;
}
- } else if(cmd == "join") {
+ } else if(cmd_name == "join") {
if not(self.flags & FL_CLIENT)
return;
if(!g_arena)
centerprint(self, PREVENT_JOIN_TEXT);
}
}
- } else if( cmd == "selectteam" ) {
+ } else if( cmd_name == "selectteam" ) {
if not(self.flags & FL_CLIENT)
return;
if( !teamplay ) {
} else {
sprint( self, strcat( "selectteam none/red/blue/yellow/pink/auto - \"", argv(1), "\" not recognised\n" ) );
}
- } else if(cmd == "ready") {
+ } else if(cmd_name == "ready") {
if not(self.flags & FL_CLIENT)
return;
sprint(self, "^1Game has already been restarted\n");
}
}
- } else if(cmd == "maplist") {
+ } else if(cmd_name == "maplist") {
sprint(self, maplist_reply);
- } else if(cmd == "lsmaps") {
+ } else if(cmd_name == "lsmaps") {
sprint(self, lsmaps_reply);
- } else if(cmd == "lsnewmaps") {
+ } else if(cmd_name == "lsnewmaps") {
sprint(self, lsnewmaps_reply);
- } else if(cmd == "records") {
+ } else if(cmd_name == "records") {
for(i = 0; i < 10; ++i)
sprint(self, records_reply[i]);
- } else if(cmd == "ladder") {
+ } else if(cmd_name == "ladder") {
sprint(self, ladder_reply);
- } else if(cmd == "rankings") {
+ } else if(cmd_name == "rankings") {
sprint(self, rankings_reply);
- } else if(cmd == "voice") {
- if(tokens >= 3)
+ } else if(cmd_name == "voice") {
+ if(cmd_argc >= 3)
VoiceMessage(argv(1), substring(s, argv_start_index(2), argv_end_index(-1) - argv_start_index(2)));
else
VoiceMessage(argv(1), "");
- } else if(cmd == "say") {
- if(tokens >= 2)
+ } else if(cmd_name == "say") {
+ if(cmd_argc >= 2)
Say(self, FALSE, world, substring(s, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)), 1);
//clientcommand(self, formatmessage(s));
- } else if(cmd == "say_team") {
- if(tokens >= 2)
+ } else if(cmd_name == "say_team") {
+ if(cmd_argc >= 2)
Say(self, TRUE, world, substring(s, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)), 1);
//clientcommand(self, formatmessage(s));
- } else if(cmd == "tell") {
- e = GetCommandPlayerSlotTargetFromTokenizedCommand(tokens, 1);
- if(e && tokens > ParseCommandPlayerSlotTarget_firsttoken)
+ } else if(cmd_name == "tell") {
+ e = GetCommandPlayerSlotTargetFromTokenizedCommand(cmd_argc, 1);
+ if(e && cmd_argc > ParseCommandPlayerSlotTarget_firsttoken)
{
Say(self, FALSE, e, substring(s, argv_start_index(ParseCommandPlayerSlotTarget_firsttoken), argv_end_index(-1) - argv_start_index(ParseCommandPlayerSlotTarget_firsttoken)), TRUE);
}
else
{
- if(tokens > ParseCommandPlayerSlotTarget_firsttoken)
+ if(cmd_argc > ParseCommandPlayerSlotTarget_firsttoken)
trigger_magicear_processmessage_forallears(self, -1, world, substring(s, argv_start_index(ParseCommandPlayerSlotTarget_firsttoken), argv_end_index(-1) - argv_start_index(ParseCommandPlayerSlotTarget_firsttoken)));
sprint(self, "ERROR: usage: tell # playerid text...\n");
}
//clientcommand(self, formatmessage(s));
- } else if(cmd == "info") {
- cmd = cvar_string_builtin(strcat("sv_info_", argv(1))); // This needed fixed for the cvar check
- if(cmd == "")
+ } else if(cmd_name == "info") {
+ cmd_name = cvar_string_builtin(strcat("sv_info_", argv(1))); // This needed fixed for the cvar check
+ if(cmd_name == "")
sprint(self, "ERROR: unsupported info command\n");
else
- wordwrap_sprint(cmd, 1111);
- } else if(cmd == "suggestmap") {
+ wordwrap_sprint(cmd_name, 1111);
+ } else if(cmd_name == "suggestmap") {
sprint(self, strcat(MapVote_Suggest(argv(1)), "\n"));
- } else if(cmd == "timeout") {
+ } else if(cmd_name == "timeout") {
if not(self.flags & FL_CLIENT)
return;
if(autocvar_sv_timeout) {
else
sprint(self, "^7Error: only players can call a timeout!\n");
}
- } else if(cmd == "timein") {
+ } else if(cmd_name == "timein") {
if not(self.flags & FL_CLIENT)
return;
if(autocvar_sv_timeout) {
evaluateTimein();
}
- } else if(cmd == "teamstatus") {
+ } else if(cmd_name == "teamstatus") {
Score_NicePrint(self);
- } else if(cmd == "cvar_changes") {
+ } else if(cmd_name == "cvar_changes") {
sprint(self, cvar_changes);
- } else if(cmd == "cvar_purechanges") {
+ } else if(cmd_name == "cvar_purechanges") {
sprint(self, cvar_purechanges);
- } else if(CheatCommand(tokens)) {
+ } else if(CheatCommand(cmd_argc)) {
} else {
#if 0
//if(ctf_clientcommand())
// return;
// grep for Cmd_AddCommand_WithClientCommand to find them all
- if(cmd != "status")
- //if(cmd != "say") // handled above
- //if(cmd != "say_team") // handled above
- if(cmd != "kill")
- if(cmd != "pause")
- if(cmd != "ping")
- if(cmd != "name")
- if(cmd != "color")
- if(cmd != "rate")
- if(cmd != "pmodel")
- if(cmd != "playermodel")
- if(cmd != "playerskin")
- if(cmd != "prespawn")
- if(cmd != "spawn")
- if(cmd != "begin")
- if(cmd != "pings")
- if(cmd != "sv_startdownload")
- if(cmd != "download")
+ if(cmd_name != "status")
+ //if(cmd_name != "say") // handled above
+ //if(cmd_name != "say_team") // handled above
+ if(cmd_name != "kill")
+ if(cmd_name != "pause")
+ if(cmd_name != "ping")
+ if(cmd_name != "name")
+ if(cmd_name != "color")
+ if(cmd_name != "rate")
+ if(cmd_name != "pmodel")
+ if(cmd_name != "playermodel")
+ if(cmd_name != "playerskin")
+ if(cmd_name != "prespawn")
+ if(cmd_name != "spawn")
+ if(cmd_name != "begin")
+ if(cmd_name != "pings")
+ if(cmd_name != "sv_startdownload")
+ if(cmd_name != "download")
{
print("WARNING: Invalid clientcommand by ", self.netname, ": ", s, "\n");
return;
#endif
if(self.jointime > 0 && time > self.jointime + 10 && time > self.nickspamtime) // allow any changes in the first 10 seconds since joining
- if(cmd == "name" || cmd == "playermodel") // TODO also playerskin and color?
+ if(cmd_name == "name" || cmd_name == "playermodel") // TODO also playerskin and color?
{
if(self.nickspamtime == 0 || time > self.nickspamtime + autocvar_g_nick_flood_timeout)
// good, no serious flood
.float runes;
+// Keys player is holding
+.float itemkeys;
+// message delay for func_door locked by keys and key locks
+// this field is used on player entities
+.float key_door_messagetime;
+
.float version;
.float cvar_cl_handicap;
.float cvar_cl_playerdetailreduction;
.float cvar_cl_clippedspectating;
+.float cvar_cl_autoscreenshot;
.float cvar_cl_movement_track_canjump;
.float cvar_cl_newusekeysupported;
.float misc_bulletcounter; // replaces uzi & hlac bullet counter.
void PlayerUseKey();
+
}
else if (attacker.classname == "player")
{
- if(teamplay && attacker.team == targ.team)
+ if(!IsDifferentTeam(attacker, targ))
{
if(attacker.team == COLOR_TEAM1)
type = KILL_TEAM_RED;
damage = 0;
force = '0 0 0';
}
- else if(teamplay && attacker.team == targ.team)
+ else if(!IsDifferentTeam(attacker, targ))
{
if(autocvar_teamplay_mode == 1)
damage = 0;
if (lag)
{
// take players back into the past
- player = player_list;
- while (player)
- {
- antilag_takeback(player, time - lag);
- player = player.nextplayer;
- }
+ FOR_EACH_PLAYER(player)
+ if(player != forent)
+ antilag_takeback(player, time - lag);
}
// do the trace
// restore players to current positions
if (lag)
{
- player = player_list;
- while (player)
- {
- antilag_restore(player);
- player = player.nextplayer;
- }
+ FOR_EACH_PLAYER(player)
+ if(player != forent)
+ antilag_restore(player);
}
// restore shooter solid type
float Map_Count, Map_Current;
string Map_Current_Name;
-// NOTE: this now expects the map list to be already tokenize()d and the count in Map_Count
+// NOTE: this now expects the map list to be already tokenized and the count in Map_Count
float GetMaplistPosition()
{
float pos, idx;
{
FixIntermissionClient(self);
- if(autocvar_sv_autoscreenshot)
- if(self.autoscreenshot > 0)
- if(time > self.autoscreenshot)
+ if( (autocvar_sv_autoscreenshot || self.cvar_cl_autoscreenshot)
+ && ((self.autoscreenshot > 0) && (time > self.autoscreenshot)) )
{
self.autoscreenshot = -1;
if(clienttype(self) == CLIENTTYPE_REAL)
fh = fopen("effectinfo.txt", FILE_READ);
while((s = fgets(fh)))
{
- tokenize(s); // tokenize_console would hit the loop counter :(
+ tokenize_console(s);
if(argv(0) == "effect")
{
if(db_get(d, argv(1)) != "1")
--- /dev/null
+/*
+TODO:
+- add an unlock sound (here to trigger_keylock and to func_door)
+- display available keys on the HUD
+- make more tests
+- think about adding NOT_EASY/NOT_NORMAL/NOT_HARD for Q1 compatibility
+- should keys have a trigger?
+*/
+
+float item_keys_usekey(entity l, entity p) {
+ float valid = l.itemkeys & p.itemkeys;
+
+ if not(valid) {
+ // other has none of the needed keys
+ return FALSE;
+ } else if (l.itemkeys == valid) {
+ // ALL needed keys were given
+ l.itemkeys = 0;
+ return TRUE;
+ } else {
+ // only some of the needed keys were given
+ l.itemkeys &~= valid;
+ return TRUE;
+ }
+}
+
+string item_keys_keylist(float keylist) {
+ float base, l;
+ string n;
+
+ // no keys
+ if not(keylist)
+ return "";
+
+ // one key
+ if ((keylist & (keylist-1)) != 0)
+ return strcat("the ", item_keys_names[lowestbit(keylist)]);
+
+ while (keylist) {
+ l = lowestbit(keylist);
+ if (n)
+ n = strcat(n, ", the ", item_keys_names[base + l]);
+ else
+ n = strcat("the ", item_keys_names[base + l]);
+
+ keylist = bitshift(keylist, -(l + 1));
+ base+= l + 1;
+ }
+
+ return n;
+}
+
+
+/*
+================================
+item_key
+================================
+*/
+
+/**
+ * Key touch handler.
+ */
+void item_key_touch(void) {
+ if (other.classname != "player")
+ return;
+
+ // player already picked up this key
+ if (other.itemkeys & self.itemkeys)
+ return;
+
+ other.itemkeys |= self.itemkeys;
+ play2(other, self.noise);
+
+ centerprint(other, self.message);
+};
+
+/**
+ * Spawn a key with given model, key code and color.
+ */
+void spawn_item_key() {
+ precache_model(self.model);
+
+ if (self.spawnflags & 1) // FLOATING
+ self.noalign = 1;
+
+ if (self.noalign)
+ self.movetype = MOVETYPE_NONE;
+ else
+ self.movetype = MOVETYPE_TOSS;
+
+ precache_sound(self.noise);
+
+ self.mdl = self.model;
+ self.effects = EF_LOWPRECISION;
+ setmodel(self, self.model);
+ //setsize(self, '-16 -16 -24', '16 16 32');
+ setorigin(self, self.origin + '0 0 32');
+ setsize(self, '-16 -16 -56', '16 16 0');
+ self.modelflags |= MF_ROTATE;
+ self.solid = SOLID_TRIGGER;
+
+ if (!self.noalign)
+ {
+ // first nudge it off the floor a little bit to avoid math errors
+ setorigin(self, self.origin + '0 0 1');
+ // note droptofloor returns FALSE if stuck/or would fall too far
+ droptofloor();
+ }
+
+ self.touch = item_key_touch;
+};
+
+
+/*QUAKED item_key (0 .5 .8) (-16 -16 -24) (16 16 32) FLOATING
+A key entity.
+The itemkeys should contain one of the following key IDs:
+1 - GOLD key -
+2 - SILVER key
+4 - BRONZE key
+8 - RED keycard
+16 - BLUE keycard
+32 - GREEN keycard
+Custom keys:
+... - last key is 1<<23
+Keys with bigger Id than 32 don't have a default netname and model, if you use one of them, you MUST provide those.
+-----------KEYS------------
+colormod: color of the key (default: '.9 .9 .9').
+itemkeys: a key Id.
+message: message to print when player picks up this key.
+model: custom key model to use.
+netname: the display name of the key.
+noise: custom sound to play when player picks up the key.
+-------- SPAWNFLAGS --------
+FLOATING: the item will float in air, instead of aligning to the floor by falling
+---------NOTES----------
+This is the only correct way to put keys on the map!
+
+itemkeys MUST always have exactly one bit set.
+*/
+void spawnfunc_item_key() {
+ local string _model, _netname;
+ local vector _colormod;
+
+ // reject this entity if more than one key was set!
+ if (self.itemkeys>0 && (self.itemkeys & (self.itemkeys-1)) != 0) {
+ objerror("item_key.itemkeys must contain only 1 bit set specifying the key it represents!");
+ remove(self);
+ return;
+ }
+
+ // find default netname and colormod
+ switch(self.itemkeys) {
+ case 1:
+ _netname = "GOLD key";
+ _colormod = '1 .9 0';
+ break;
+
+ case 2:
+ _netname = "SILVER key";
+ _colormod = '.9 .9 .9';
+ break;
+
+ case 4:
+ _netname = "BRONZE key";
+ _colormod = '.6 .25 0';
+ break;
+
+ case 8:
+ _netname = "RED keycard";
+ _colormod = '.9 0 0';
+ break;
+
+ case 16:
+ _netname = "BLUE keycard";
+ _colormod = '0 0 .9';
+ break;
+
+ case 32:
+ _netname = "GREEN keycard";
+ _colormod = '0 .9 0';
+ break;
+
+ default:
+ if (!self.netname) {
+ objerror("item_key doesn't have a default name for this key and a custom one was not specified!");
+ remove(self);
+ return;
+ } else if (!self.colormod) {
+ _colormod = '1 1 1';
+ }
+ break;
+
+ }
+
+ // find default model
+ if (self.itemkeys <= ITEM_KEY_BIT(2)) {
+ _model = "models/keys/key.md3";
+ } else if (self.itemkeys >= ITEM_KEY_BIT(3) && self.itemkeys <= ITEM_KEY_BIT(5)) {
+ _model = "models/keys/key.md3"; // FIXME: replace it by a keycard model!
+ } else if (!self.model) {
+ objerror("item_key doesn't have a default model for this key and a custom one was not specified!");
+ remove(self);
+ return;
+ }
+
+ // set defailt netname
+ if (!self.netname)
+ self.netname = _netname;
+
+ // set default colormod
+ if (!self.colormod)
+ self.colormod = _colormod;
+
+ // set default model
+ if (!self.model)
+ self.model = _model;
+
+ // set default pickup message
+ if (!self.message)
+ self.message = strzone(strcat("You've picked up the ", self.netname, "!"));
+
+ if (!self.noise)
+ self.noise = "misc/itempickup.wav";
+
+ // save the name for later
+ item_keys_names[lowestbit(self.itemkeys)] = self.netname;
+
+ // put the key on the map
+ spawn_item_key();
+}
+
+/*QUAKED item_key1 (0 .5 .8) (-16 -16 -24) (16 16 32) FLOATING
+SILVER key.
+-----------KEYS------------
+colormod: color of the key (default: '.9 .9 .9').
+message: message to print when player picks up this key.
+model: custom model to use.
+noise: custom sound to play when player picks up the key.
+-------- SPAWNFLAGS --------
+FLOATING: the item will float in air, instead of aligning to the floor by falling
+---------NOTES----------
+Don't use this entity on new maps! Use item_key instead.
+*/
+void spawnfunc_item_key1(void) {
+ self.classname = "item_key";
+ self.itemkeys = ITEM_KEY_BIT(1);
+ spawnfunc_item_key();
+};
+
+/*QUAKED item_key2 (0 .5 .8) (-16 -16 -24) (16 16 32) FLOATING
+GOLD key.
+-----------KEYS------------
+colormod: color of the key (default: '1 .9 0').
+message: message to print when player picks up this key.
+model: custom model to use.
+noise: custom sound to play when player picks up the key.
+-------- SPAWNFLAGS --------
+FLOATING: the item will float in air, instead of aligning to the floor by falling
+---------NOTES----------
+Don't use this entity on new maps! Use item_key instead.
+*/
+void spawnfunc_item_key2(void) {
+ self.classname = "item_key";
+ self.itemkeys = ITEM_KEY_BIT(0);
+ spawnfunc_item_key();
+};
+
+
+/*
+================================
+trigger_keylock
+================================
+*/
+
+/**
+ * trigger givent targets
+ */
+void trigger_keylock_trigger(string s) {
+ local entity t, stemp, otemp, atemp;
+
+ stemp = self;
+ otemp = other;
+ atemp = activator;
+
+
+ for(t = world; (t = find(t, targetname, s)); )
+ if (t.use) {
+ self = t;
+ other = stemp;
+ activator = atemp;
+ self.use();
+ }
+
+ self = stemp;
+ other = otemp;
+ activator = atemp;
+};
+
+/**
+ * kill killtarget of trigger keylock.
+ */
+void trigger_keylock_kill(string s) {
+ local entity t;
+ for(t = world; (t = find(t, targetname, s)); )
+ remove(t);
+};
+
+void trigger_keylock_touch(void) {
+ local float key_used, started_delay;
+
+ key_used = FALSE;
+ started_delay = FALSE;
+
+ // only player may trigger the lock
+ if (other.classname != "player")
+ return;
+
+
+ // check silver key
+ if (self.itemkeys)
+ key_used = item_keys_usekey(self, other);
+
+ activator = other;
+
+ if (self.itemkeys) {
+ // at least one of the keys is missing
+ if (key_used) {
+ // one or more keys were given, but others are still missing!
+ play2(other, self.noise1);
+ centerprint(other, strcat("You also need ", item_keys_keylist(self.itemkeys), "!"));
+ other.key_door_messagetime = time + 2;
+ } else if (other.key_door_messagetime <= time) {
+ // no keys were given
+ play2(other, self.noise2);
+ centerprint(other, strcat("You need ", item_keys_keylist(self.itemkeys), "!"));
+ other.key_door_messagetime = time + 2;
+ }
+
+ // trigger target2
+ if (self.delay <= time || started_delay == TRUE)
+ if (self.target2) {
+ trigger_keylock_trigger(self.target2);
+ started_delay = TRUE;
+ self.delay = time + self.wait;
+ }
+ } else {
+ // all keys were given!
+ play2(other, self.noise);
+ centerprint(other, self.message);
+
+ if (self.target)
+ trigger_keylock_trigger(self.target);
+
+ if (self.killtarget)
+ trigger_keylock_kill(self.killtarget);
+
+ remove(self);
+ }
+
+};
+
+/*QUAKED trigger_keylock (.0 .5 .8) ?
+Keylock trigger. Must target other entities.
+This trigger will trigger target entities when all required keys are provided.
+-------- KEYS --------
+itemkeys: A bit field with key IDs that are needed to open this lock.
+sounds: 1 to play misc/secret.wav, 2 to play misc/talk.wav, 3 to play misc/trigger1.wav (3 is default)
+target: trigger all entities with this targetname when triggered and all keys have been given to it, then remove this trigger
+target2: trigger all entities with this targetname when triggered without giving it all the required keys.
+killtarget: remove all entities with this targetname when triggered with all the needed keys.
+message: print this message to the player who activated the trigger when all needed keys have been given.
+message2: print this message to the player who activated the trigger when not all of the needed keys have been given.
+noise: sound to play when lock gets unlocked (default: see sounds)
+noise1: sound to play when only some of the needed key were used but not all (default: misc/decreasevalue.wav)
+noise2: sound to play when a key is missing (default: misc/talk.wav)
+wait: prevent triggering again for this amount of time (default: 5) - applies to target2, target3, target4.
+---------NOTES----------
+If spawned without any key specified in itemkeys, this trigger will display an error and remove itself.
+message2 and noise2 will be resent to the player every 2 seconds while he is in the trigger zone.
+*/
+void spawnfunc_trigger_keylock(void) {
+ if (!self.itemkeys) {
+ remove(self);
+ return;
+ }
+
+ // set unlocked message
+ if (!self.message)
+ self.message = "Unlocked!";
+
+ // set default unlock noise
+ if (!self.noise) {
+ if (self.sounds == 1)
+ self.noise = "misc/secret.wav";
+ else if (self.sounds == 2)
+ self.noise = "misc/talk.wav";
+ else //if (self.sounds == 3) {
+ self.noise = "misc/trigger1.wav";
+ }
+
+ // set default use key sound
+ if (!self.noise1)
+ self.noise1 = "misc/decreasevalue.wav";
+
+ // set closed sourd
+ if (!self.noise2)
+ self.noise2 = "misc/talk.wav";
+
+ // delay between triggering message2 and trigger2
+ if (!self.wait)
+ self.wait = 5;
+
+ // precache sounds
+ precache_sound(self.noise);
+ precache_sound(self.noise1);
+ precache_sound(self.noise2);
+
+ EXACTTRIGGER_INIT;
+
+ self.touch = trigger_keylock_touch;
+};
+
+
--- /dev/null
+/**
+ * Returns the bit ID of a key
+ */
+#define ITEM_KEY_BIT(n) ( bitshift(1, n) )
+
+#define ITEM_KEY_MAX 24
+
+/**
+ * list of key names.
+ */
+string item_keys_names[ITEM_KEY_MAX];
+
+/**
+ * Use keys from p on l.
+ * Returns TRUE if any new keys were given, FALSE otherwise.
+ */
+float item_keys_usekey(entity l, entity p);
+
+/**
+ * Returns a string with a comma separated list of key names, as specified in keylist.
+ */
+string item_keys_keylist(float keylist);
+
get_cvars_s = s;
MUTATOR_CALLHOOK(GetCvars);
GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
+ GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
// called when the use key is pressed
// if MUTATOR_RETURNVALUE is 1, don't do anything
// return 1 if the use key actually did something
+
+MUTATOR_HOOKABLE(SV_ParseClientCommand);
+ // called when a client command is parsed
+ // NOTE: hooks MUST start with if(MUTATOR_RETURNVALUE) return 0;
+ // NOTE: return 1 if you handled the command, return 0 to continue handling
+ // NOTE: THESE HOOKS MUST NEVER EVER CALL tokenize()
+ // INPUT
+ string cmd_name; // command name
+ float cmd_argc; // also, argv() can be used
+ string cmd_string; // whole command, use only if you really have to
+ /*
+ // example:
+ MUTATOR_HOOKFUNCTION(foo_SV_ParseClientCommand)
+ {
+ if(MUTATOR_RETURNVALUE) // command was already handled?
+ return 0;
+ if(cmd_name == "echocvar" && cmd_argc >= 2)
+ {
+ print(cvar_string(argv(1)), "\n");
+ return 1;
+ }
+ if(cmd_name == "echostring" && cmd_argc >= 2)
+ {
+ print(substring(cmd_string, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)), "\n");
+ return 1;
+ }
+ return 0;
+ }
+ */
void ka_RespawnBall(void);
void ka_DropEvent(entity);
void ka_TimeScoring(void);
+void ka_EventLog(string, entity);
entity ka_ball;
other.effects |= autocvar_g_keepaway_ballcarrier_effects;
// messages and sounds
+ ka_EventLog("pickup", other);
Send_KillNotification(other.netname, "", "", KA_PICKUPBALL, MSG_KA);
WriteByte(MSG_BROADCAST, SVC_CENTERPRINT);
WriteString(MSG_BROADCAST, strcat(other.netname, "^7 has picked up the ball!"));
plyr.effects &~= autocvar_g_keepaway_ballcarrier_effects;
// messages and sounds
+ ka_EventLog("dropped", plyr);
Send_KillNotification(plyr.netname, "", "", KA_DROPBALL, MSG_KA);
WriteByte(MSG_BROADCAST, SVC_CENTERPRINT);
WriteString(MSG_BROADCAST, strcat(plyr.netname, "^7 has dropped the ball!"));
}
}
+void ka_EventLog(string mode, entity actor) // use an alias for easy changing and quick editing later
+{
+ if(autocvar_sv_eventlog)
+ GameLogEcho(strcat(":ka:", mode, ((actor != world) ? (strcat(":", ftos(actor.playerid))) : "")));
+}
+
MUTATOR_HOOKFUNCTION(ka_Scoring)
{
if((frag_attacker != frag_target) && (frag_attacker.classname == "player"))
playerdemo.qh
+item_key.qh
+
scores_rules.qc
miscfunctions.qc
t_items.qc
cl_weapons.qc
cl_impulse.qc
+item_key.qc
ent_cs.qc
s = substring(s, 1, -1);
}
- n = tokenize(s);
+ n = tokenize_console(s);
for(i = 0; i < n; ++i)
{
s = argv(i);
}
+
/*
=============================================================================
=============================================================================
*/
+float door_check_keys(void) {
+ local entity door;
+
+
+ if (self.owner)
+ door = self.owner;
+ else
+ door = self;
+
+ // no key needed
+ if not(door.itemkeys)
+ return TRUE;
+
+ // this door require a key
+ // only a player can have a key
+ if (other.classname != "player")
+ return FALSE;
+
+ if (item_keys_usekey(door, other)) {
+ // some keys were used
+ if (other.key_door_messagetime <= time) {
+ play2(other, "misc/talk.wav");
+ centerprint(other, strcat("You also need ", item_keys_keylist(door.itemkeys), "!"));
+ other.key_door_messagetime = time + 2;
+ }
+ } else {
+ // no keys were used
+ if (other.key_door_messagetime <= time) {
+ play2(other, "misc/talk.wav");
+ centerprint(other, strcat("You need ", item_keys_keylist(door.itemkeys), "!"));
+ other.key_door_messagetime = time + 2;
+ }
+ }
+
+ if (door.itemkeys) {
+ // door is now unlocked
+ play2(other, "misc/talk.wav");
+ centerprint(other, "Door unlocked!");
+ return TRUE;
+ } else
+ return FALSE;
+}
+
+
void door_fire()
{
entity oself;
entity oself;
//dprint("door_use (model: ");dprint(self.model);dprint(")\n");
+
if (self.owner)
{
oself = self;
void door_trigger_touch()
{
if (other.health < 1)
- if not(other.iscreature && other.deadflag == DEAD_NO)
- return;
+ if not(other.iscreature && other.deadflag == DEAD_NO)
+ return;
if (time < self.attack_finished_single)
return;
+
+ // check if door is locked
+ if (!door_check_keys())
+ return;
+
self.attack_finished_single = time + 1;
activator = other;
if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
return;
self.health = self.health - damage;
+
+ if (self.itemkeys) {
+ // don't allow opening doors through damage if keys are required
+ return;
+ }
+
if (self.health <= 0)
{
oself = self;
}
-/*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK x x TOGGLE
+/*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE
if two doors touch, they are assumed to be connected and operate as a unit.
TOGGLE causes the door to wait in both the start and end states for a trigger event.
START_OPEN causes the door to move to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not useful for touch or takedamage doors).
+GOLD_KEY causes the door to open only if the activator holds a gold key.
+
+SILVER_KEY causes the door to open only if the activator holds a silver key.
+
"message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
"angle" determines the opening direction
"targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
self.think = SUB_Null;
}
+// spawnflags require key (for now only func_door)
+#define SPAWNFLAGS_GOLD_KEY 8
+#define SPAWNFLAGS_SILVER_KEY 16
void spawnfunc_func_door()
{
+ // Quake 1 keys compatibility
+ if (self.spawnflags & SPAWNFLAGS_GOLD_KEY)
+ self.itemkeys |= ITEM_KEY_BIT(0);
+ if (self.spawnflags & SPAWNFLAGS_SILVER_KEY)
+ self.itemkeys |= ITEM_KEY_BIT(1);
+
//if (!self.deathtype) // map makers can override this
// self.deathtype = " got in the way";
SetMovedir ();
self.blocked = door_blocked;
self.use = door_use;
- if(self.spawnflags & 8)
- self.dmg = 10000;
+ // FIXME: undocumented flag 8, originally (Q1) GOLD_KEY
+ // if(self.spawnflags & 8)
+ // self.dmg = 10000;
if(self.dmg && (!self.message))
self.message = "was squished";
float turret_tesla_firecheck()
{
- if not (turret_stdproc_firecheck())
- return 0;
+ // g_turrets_targetscan_maxdelay forces a target re-scan at least this often
+ float do_target_scan;
+
+ if((self.target_select_time + autocvar_g_turrets_targetscan_maxdelay) < time)
+ do_target_scan = 1;
+
+ // Old target (if any) invalid?
+ if(self.target_validate_time < time)
+ if (turret_validate_target(self, self.enemy, self.target_validate_flags) <= 0)
+ {
+ self.enemy = world;
+ self.target_validate_time = time + 0.5;
+ do_target_scan = 1;
+ }
- self.target_select_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK;
+ // But never more often then g_turrets_targetscan_mindelay!
+ if (self.target_select_time + autocvar_g_turrets_targetscan_mindelay > time)
+ do_target_scan = 0;
- self.enemy = turret_select_target();
+ if(do_target_scan)
+ {
+ self.enemy = turret_select_target();
+ self.target_select_time = time;
+ }
+
+ if not (turret_stdproc_firecheck())
+ return 0;
if(self.enemy)
return 1;
return 0;
-
}
+
void turret_tesla_fire()
{
- entity e,t;
- float d,r,i;
+ entity e, t;
+ float d, r, i;
//w_deathtypestring = "discoverd how a tesla coil works";
{
if(vlen(trace_endpos - fireBallisticBullet_trace_callback_ent.origin) > 16)
zcurveparticles_from_tracetoss(fireBallisticBullet_trace_callback_eff, fireBallisticBullet_trace_callback_ent.origin, trace_endpos, fireBallisticBullet_trace_callback_ent.velocity);
+ WarpZone_trace_forent = world;
+ self.owner = world;
}
void fireBallisticBullet(vector start, vector dir, float spread, float pSpeed, float lifetime, float damage, float headshotbonus, float force, float dtype, float tracereffects, float gravityfactor, float bulletconstant)
if(lag)
FOR_EACH_PLAYER(pl)
- antilag_takeback(pl, time - lag);
+ if(pl != self)
+ antilag_takeback(pl, time - lag);
oldself = self;
self = proj;
trace_fraction = 0;
fireBallisticBullet_trace_callback_ent = self;
fireBallisticBullet_trace_callback_eff = eff;
- // FIXME can we somehow do this with just ONE trace?
- WarpZone_TraceToss(self, self.owner);
- if(self.owner && WarpZone_trace_firstzone)
- {
- self.owner = world;
- self.velocity = v0;
- self.gravity = g0;
- continue;
- }
WarpZone_TraceToss_ThroughZone(self, self.owner, world, fireBallisticBullet_trace_callback);
self.velocity = v0;
self.gravity = g0;
if(lag)
FOR_EACH_PLAYER(pl)
- antilag_restore(pl);
+ if(pl != self)
+ antilag_restore(pl);
remove(proj);
precache_model ("models/weapons/g_tuba.md3");
precache_model ("models/weapons/v_tuba.md3");
precache_model ("models/weapons/h_tuba.iqm");
+ precache_model ("models/weapons/g_akordeon.md3");
+ precache_model ("models/weapons/v_akordeon.md3");
+ precache_model ("models/weapons/h_akordeon.iqm");
//float i;
//for(i = -18; i <= +27; ++i)
entity wz;
vector vf, vr, vu;
+ WarpZone_trace_forent = forent;
WarpZone_trace_firstzone = world;
WarpZone_trace_lastzone = world;
WarpZone_Trace_InitTransform();
}
else
{
- tracebox(org, mi, ma, end, nomonsters, forent);
+ tracebox(org, mi, ma, end, nomonsters, WarpZone_trace_forent);
if(cb)
cb(org, trace_endpos, end);
return;
nomonsters_adjusted = nomonsters;
break;
}
- if((contentshack = (forent.dphitcontentsmask && !(forent.dphitcontentsmask & DPCONTENTS_SOLID))))
- BITSET_ASSIGN(forent.dphitcontentsmask, DPCONTENTS_SOLID);
+ if((contentshack = (WarpZone_trace_forent.dphitcontentsmask && !(WarpZone_trace_forent.dphitcontentsmask & DPCONTENTS_SOLID))))
+ BITSET_ASSIGN(WarpZone_trace_forent.dphitcontentsmask, DPCONTENTS_SOLID);
// if starting in warpzone, first transform
wz = WarpZone_Find(org + mi, org + ma);
trace_ent = world;
break;
}
- tracebox(org, mi, ma, end, nomonsters_adjusted, forent);
+ tracebox(org, mi, ma, end, nomonsters_adjusted, WarpZone_trace_forent);
if(cb)
cb(org, trace_endpos, end);
if(sol < 0)
break;
if(trace_ent.classname != "trigger_warpzone")
{
- if((nomonsters == MOVE_NOTHING) || ((nomonsters == MOVE_WORLDONLY) && trace_ent) || (contentshack && (trace_dphitcontents & forent.dphitcontentsmask) == DPCONTENTS_SOLID))
+ if((nomonsters == MOVE_NOTHING) || ((nomonsters == MOVE_WORLDONLY) && trace_ent) || (contentshack && (trace_dphitcontents & WarpZone_trace_forent.dphitcontentsmask) == DPCONTENTS_SOLID))
{
// continue the trace, ignoring this hit (we only care for warpzones)
org = trace_endpos + normalize(end - org);
end = WarpZone_TransformOrigin(wz, end);
// we got warped, so let's step back a bit
- tracebox(org, mi, ma, org + normalize(org - end) * 32, nomonsters_adjusted, forent);
+ tracebox(org, mi, ma, org + normalize(org - end) * 32, nomonsters_adjusted, WarpZone_trace_forent);
org = trace_endpos;
}
WarpZone_MakeAllOther();
:fail
if(contentshack)
- BITCLR_ASSIGN(forent.dphitcontentsmask, DPCONTENTS_SOLID);
+ BITCLR_ASSIGN(WarpZone_trace_forent.dphitcontentsmask, DPCONTENTS_SOLID);
trace_startsolid = sol;
v_forward = vf;
v_right = vr;
o0 = e.origin;
v0 = e.velocity;
+ WarpZone_trace_forent = forent;
WarpZone_trace_firstzone = world;
WarpZone_trace_lastzone = world;
WarpZone_Trace_InitTransform();
WarpZone_tracetoss_time = 0;
if(!warpzone_warpzones_exist)
{
- tracetoss(e, forent);
+ tracetoss(e, WarpZone_trace_forent);
if(cb)
cb(e.origin, trace_endpos, trace_endpos);
dt = vlen(e.origin - o0) / vlen(e.velocity);
trace_ent = world;
break;
}
- tracetoss(e, forent);
+ tracetoss(e, WarpZone_trace_forent);
if(cb)
cb(e.origin, trace_endpos, trace_endpos);
dt = vlen(trace_endpos - e.origin) / vlen(e.velocity);
// we got warped, so let's step back a bit
e.velocity = -e.velocity;
- tracetoss(e, forent);
+ tracetoss(e, WarpZone_trace_forent);
dt = vlen(trace_endpos - e.origin) / vlen(e.velocity);
WarpZone_tracetoss_time -= dt;
e.origin = trace_endpos;
void WarpZone_MakeAllOther();
#define MOVE_NOTHING -1
+entity WarpZone_trace_forent; // temp, callback is allowed to change it
typedef void(vector start, vector hit, vector end) WarpZone_trace_callback_t; // called on every elementary trace
const var WarpZone_trace_callback_t WarpZone_trace_callback_t_null;
entity WarpZone_trace_transform; // transform accumulator during a trace