r_mipsprites 1
r_mipskins 1
r_shadow_realtime_world_lightmaps 1
+r_shadow_realtime_world_importlightentitiesfrommap 0 // Whether build process uses keepLights is nontransparent and may change, so better make keepLights not matter.
cl_decals_fadetime 5
cl_decals_time 1
seta cl_gunalign 3 "Gun alignment; 1 = center (if allowed by g_shootfromclient) or right, 2 = center (if allowed by g_shootfromclient) or left, 3 = right only, 4 = left only"
set g_overkill_100a_anyway 1
set g_overkill_100h_anyway 1
set g_overkill_powerups_replace 1
-set g_overkill_superguns_respawn_time 20
+set g_overkill_superguns_respawn_time 120
set g_overkill_ammo_charge 0
set g_overkill_ammo_charge_notice 1
// Nades
// =======
set g_nades 0 "enable off-hand grenades"
+set g_nades_throw_offset "0 0 0" "nade throwing offset"
set g_nades_spawn 1 "give nades right away when player spawns rather than delaying entire refire"
set g_nades_client_select 0 "allow client side selection of nade type"
set g_nades_nade_lifetime 3.5
if(length < 1)
return;
- steps = floor(length / seglength);
+ // Use at most 16 te_lightning1 segments, as these eat up beam list segments.
+ // TODO: Change this to R_BeginPolygon code, then we no longer have this limit.
+ steps = min(16, floor(length / seglength));
if(steps < 1)
{
te_lightning1(world,from,to);
dirnew = normalize(direction * (1 - drift) + randomvec() * drift);
pos = pos_l + dirnew * steplength;
te_lightning1(world,pos_l,pos);
- if(random() < branchfactor)
- cl_effects_lightningarc(pos, pos + (dirnew * length * 0.25),seglength,drifts,drifte,min(branchfactor + branchfactor_add,1),branchfactor_add);
+ // WTF endless recursion if branchfactor is 1.0 (possibly due to adding branchfactor_add). FIXME
+ // if(random() < branchfactor)
+ // cl_effects_lightningarc(pos, pos + (dirnew * length * 0.25),seglength,drifts,drifte,min(branchfactor + branchfactor_add,1),branchfactor_add);
pos_l = pos;
}
return bound(1, floor((sqrt(4 * item_aspect * aspect * item_count + aspect * aspect) + aspect + 0.5) / 2), item_count);
}
+vector HUD_GetTableSize(float item_count, vector psize, float item_aspect)
+{
+ float columns, rows;
+ float ratio, best_ratio = 0;
+ float best_columns = 1, best_rows = 1;
+ bool vertical = (psize.x / psize.y >= item_aspect);
+ if(vertical)
+ {
+ psize = eX * psize.y + eY * psize.x;
+ item_aspect = 1 / item_aspect;
+ }
+
+ rows = ceil(sqrt(item_count));
+ columns = ceil(item_count/rows);
+ while(columns >= 1)
+ {
+ ratio = (psize.x/columns) / (psize.y/rows);
+ if(ratio > item_aspect)
+ ratio = item_aspect * item_aspect / ratio;
+
+ if(ratio <= best_ratio)
+ break; // ratio starts decreasing by now, skip next configurations
+
+ best_columns = columns;
+ best_rows = rows;
+ best_ratio = ratio;
+
+ if(columns == 1)
+ break;
+
+ --columns;
+ rows = ceil(item_count/columns);
+ }
+
+ if(vertical)
+ return eX * best_rows + eY * best_columns;
+ else
+ return eX * best_columns + eY * best_rows;
+}
+
float stringwidth_colors(string s, vector theSize)
{
return stringwidth(s, true, theSize);
float screen_ar;
vector center = '0 0 0';
float weapon_count, weapon_id;
- float row, column, rows = 0, columns;
+ float row, column, rows = 0, columns = 0;
+ bool vertical_order = true;
float aspect = autocvar_hud_panel_weapons_aspect;
float timeout = autocvar_hud_panel_weapons_timeout;
if(!weapons_stat)
for(i = WEP_FIRST; i <= WEP_LAST; i += floor((WEP_LAST-WEP_FIRST)/5))
weapons_stat |= WepSet_FromWeapon(i);
+
+ #if 0
+ /// debug code
+ if(cvar("wep_add"))
+ {
+ weapons_stat = '0 0 0';
+ float countw = 1 + floor((floor(time * cvar("wep_add"))) % WEP_COUNT);
+ for(i = WEP_FIRST; i <= countw; ++i)
+ weapons_stat |= WepSet_FromWeapon(i);
+ }
+ #endif
}
// determine which weapons are going to be shown
if((weapons_stat & WepSet_FromWeapon(weaponorder[i].weapon)) || (weaponorder[i].weapon == complain_weapon))
++weapon_count;
+
// might as well commit suicide now, no reason to live ;)
if (weapon_count == 0)
{
vector padded_panel_size = panel_size - '2 2 0' * panel_bg_padding;
// get the all-weapons layout
- rows = HUD_GetRowCount(WEP_COUNT, padded_panel_size, aspect);
- columns = ceil(WEP_COUNT / rows);
+ vector table_size = HUD_GetTableSize(WEP_COUNT, padded_panel_size, aspect);
+ columns = table_size.x;
+ rows = table_size.y;
weapon_size.x = padded_panel_size.x / columns;
weapon_size.y = padded_panel_size.y / rows;
- // reduce rows and columns as needed
// NOTE: although weapons should aways look the same even if onlyowned is enabled,
// we enlarge them a bit when possible to better match the desired aspect ratio
- if(padded_panel_size.y > padded_panel_size.x)
+ if(padded_panel_size.x / padded_panel_size.y < aspect)
{
- columns = ceil(weapon_count / rows);
+ // maximum number of rows that allows to display items with the desired aspect ratio
+ float max_rows = floor(padded_panel_size.y / (weapon_size.x / aspect));
+ columns = min(columns, ceil(weapon_count / max_rows));
rows = ceil(weapon_count / columns);
weapon_size.y = min(padded_panel_size.y / rows, weapon_size.x / aspect);
weapon_size.x = min(padded_panel_size.x / columns, aspect * weapon_size.y);
+ vertical_order = false;
}
else
{
- rows = ceil(weapon_count / columns);
+ float max_columns = floor(padded_panel_size.x / (weapon_size.y * aspect));
+ rows = min(rows, ceil(weapon_count / max_columns));
columns = ceil(weapon_count / rows);
weapon_size.x = min(padded_panel_size.x / columns, aspect * weapon_size.y);
weapon_size.y = min(padded_panel_size.y / rows, weapon_size.x / aspect);
+ vertical_order = true;
}
// reduce size of the panel
if(!rows) // if rows is > 0 onlyowned code has already updated these vars
{
- rows = HUD_GetRowCount(weapon_count, panel_size, aspect);
- columns = ceil(weapon_count/rows);
- weapon_size = eX * panel_size.x*(1/columns) + eY * panel_size.y*(1/rows);
+ vector table_size = HUD_GetTableSize(WEP_COUNT, panel_size, aspect);
+ columns = table_size.x;
+ rows = table_size.y;
+ weapon_size.x = panel_size.x / columns;
+ weapon_size.y = panel_size.y / rows;
+ vertical_order = (panel_size.x / panel_size.y >= aspect);
}
// calculate position/size for visual bar displaying ammount of ammo status
drawstring_aspect(weapon_pos + '1 1 0' * padding, s, weapon_size - '2 2 0' * padding, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
}
+ #if 0
+ /// debug code
+ if(!autocvar_hud_panel_weapons_onlyowned)
+ {
+ drawfill(weapon_pos + '1 1 0', weapon_size - '2 2 0', '1 1 1', panel_fg_alpha * 0.2, DRAWFLAG_NORMAL);
+ drawstring(weapon_pos, ftos(i + 1), label_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
+ #endif
+
// continue with new position for the next weapon
- ++row;
- if(row >= rows)
+ if(vertical_order)
{
- row = 0;
++column;
+ if(column >= columns)
+ {
+ column = 0;
+ ++row;
+ }
+ }
+ else
+ {
+ ++row;
+ if(row >= rows)
+ {
+ row = 0;
+ ++column;
+ }
}
}
wcross_alpha_goal_prev = wcross_alpha;
wcross_color_goal_prev = wcross_color;
- if(shottype == SHOTTYPE_HITTEAM || (shottype == SHOTTYPE_HITOBSTRUCTION && autocvar_crosshair_hittest_blur && !autocvar_chase_active))
+ if(spectatee_status == -1 && shottype == SHOTTYPE_HITTEAM || (shottype == SHOTTYPE_HITOBSTRUCTION && autocvar_crosshair_hittest_blur && !autocvar_chase_active))
{
wcross_blur = 1;
wcross_alpha *= 0.75;
return valstr;
}
-float dotproduct(vector a, vector b)
-{
- return a.x * b.x + a.y * b.y + a.z * b.z;
-}
-
-vector cross(vector a, vector b)
-{
- return
- '1 0 0' * (a.y * b.z - a.z * b.y)
- + '0 1 0' * (a.z * b.x - a.x * b.z)
- + '0 0 1' * (a.x * b.y - a.y * b.x);
-}
-
// compressed vector format:
// like MD3, just even shorter
// 4 bit pitch (16 angles), 0 is -90, 8 is 0, 16 would be 90
string ScoreString(float vflags, float value);
-float dotproduct(vector a, vector b);
-vector cross(vector a, vector b);
-
void compressShortVector_init();
vector decompressShortVector(float data);
float compressShortVector(vector vec);
center = CENTER_OR_VIEWOFS(head);
// find the closest point on the enemy to the center of the attack
- float ang; // angle between shotdir and h
float h; // hypotenuse, which is the distance between attacker to head
float a; // adjacent side, which is the distance between attacker and the point on w_shotdir that is closest to head.origin
h = vlen(center - self.origin);
- ang = acos(dotproduct(normalize(center - self.origin), w_shotdir));
- a = h * cos(ang);
+ a = h * (normalize(center - self.origin) * w_shotdir);
// WEAPONTODO: replace with simpler method
vector nearest_on_line = (w_shotorg + a * w_shotdir);
W_DecreaseAmmo(WEP_CVAR_SEC(vaporizer, ammo));
// ugly instagib hack to reuse the fire mode of the laser
+ int oldwep = self.weapon; // we can't avoid this hack
+ self.weapon = WEP_BLASTER;
W_Blaster_Attack(
- WEP_VAPORIZER | HITTYPE_SECONDARY,
+ WEP_BLASTER | HITTYPE_SECONDARY,
WEP_CVAR_SEC(vaporizer, shotangle),
WEP_CVAR_SEC(vaporizer, damage),
WEP_CVAR_SEC(vaporizer, edgedamage),
WEP_CVAR_SEC(vaporizer, delay),
WEP_CVAR_SEC(vaporizer, lifetime)
);
+ self.weapon = oldwep;
// now do normal refire
weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(vaporizer, animtime), w_ready);
void HUDSetup_Start(entity me, entity btn)
{
if (!(gamestatus & (GAME_CONNECTED | GAME_ISSERVER)))
- localcmd("map hudsetup/hudsetup", "\n");
+ localcmd("map hudsetup", "\n");
else
localcmd("togglemenu 0\n");
METHOD(XonoticResolutionSlider, saveCvars, void(entity))
METHOD(XonoticResolutionSlider, draw, void(entity))
ATTRIB(XonoticResolutionSlider, vid_fullscreen, float, -1)
+ ATTRIB(XonoticResolutionSlider, maxAllowedWidth, float, 0)
+ ATTRIB(XonoticResolutionSlider, maxAllowedHeight, float, 0)
ENDCLASS(XonoticResolutionSlider)
entity makeXonoticResolutionSlider();
float updateConwidths(float width, float height, float pixelheight);
}
void XonoticResolutionSlider_addResolution(entity me, float w, float h, float pixelheight)
{
+ if (me.maxAllowedWidth && w > me.maxAllowedWidth)
+ return;
+ if (me.maxAllowedHeight && h > me.maxAllowedHeight)
+ return;
float i;
for (i = 0; i < me.nValues; ++i)
{
}
// NOW we can safely clear.
me.clearValues(me);
+ me.maxAllowedWidth = 0;
+ me.maxAllowedHeight = 0;
if (fullscreen)
{
if(me.nValues == 0)
{
+ // Get workarea.
+ r = getresolution(-2);
+ // If workarea is not supported, get desktop size.
+ if(r.x == 0 && r.y == 0)
+ r = getresolution(-1);
+
+ // Add it, and limit all other resolutions to the workarea/desktop size.
+ if(r.x != 0 || r.y != 0)
+ {
+ me.maxAllowedWidth = r.x;
+ me.maxAllowedHeight = r.y;
+ me.addResolution(me, r.x, r.y, r.z);
+ }
+
+ // Add nice hardcoded defaults.
me.addResolution(me, 640, 480, 1); // pc res
#if 0
me.addResolution(me, 720, 480, 1.125); // DVD NTSC 4:3
float autocvar_g_random_gravity_negative;
float autocvar_g_random_gravity_delay;
float autocvar_g_nades;
+vector autocvar_g_nades_throw_offset;
float autocvar_g_nades_spawn;
float autocvar_g_nades_spawn_count;
float autocvar_g_nades_client_select;
void SV_ParseClientCommand(string command)
{
+ // If invalid UTF-8, don't even parse it
+ string command2 = "";
+ float len = strlen(command);
+ float i;
+ for (i = 0; i < len; ++i)
+ command2 = strcat(command2, chr2str(str2chr(command, i)));
+ if (command != command2)
+ return;
+
// if we're banned, don't even parse the command
if(Ban_MaybeEnforceBanOnce(self))
return;
// does nothing visible
BADCVAR("captureleadlimit_override");
BADCVAR("g_balance_kill_delay");
+ BADCVAR("g_ca_point_limit");
BADCVAR("g_ca_point_leadlimit");
BADCVAR("g_ctf_captimerecord_always");
BADCVAR("g_ctf_flag_glowtrails");
BADCVAR("g_ctf_flag_pickup_verbosename");
BADCVAR("g_domination_point_leadlimit");
BADCVAR("g_forced_respawn");
+ BADCVAR("g_freezetag_point_limit");
+ BADCVAR("g_freezetag_point_leadlimit");
BADCVAR("g_keyhunt_point_leadlimit");
BADPREFIX("g_mod_");
+ BADCVAR("g_invasion_point_limit");
BADCVAR("g_nexball_goalleadlimit");
+ BADCVAR("g_tdm_point_limit");
+ BADCVAR("g_tdm_point_leadlimit");
BADCVAR("leadlimit_and_fraglimit");
BADCVAR("leadlimit_override");
BADCVAR("pausable");
makevectors(passer_angle);
// find the closest point on the enemy to the center of the attack
- float ang; // angle between shotdir and h
float h; // hypotenuse, which is the distance between attacker to head
float a; // adjacent side, which is the distance between attacker and the point on w_shotdir that is closest to head.origin
h = vlen(head_center - passer_center);
- ang = acos(dotproduct(normalize(head_center - passer_center), v_forward));
- a = h * cos(ang);
+ a = h * (normalize(head_center - passer_center) * v_forward);
vector nearest_on_line = (passer_center + a * v_forward);
float distance_from_line = vlen(nearest_to_passer - nearest_on_line);
if(IS_PLAYER(frag_attacker))
if(DEATH_ISWEAPON(frag_deathtype, WEP_VAPORIZER))
+ {
+ if(frag_target.armorvalue)
+ {
+ frag_target.armorvalue -= 1;
+ frag_damage = 0;
+ frag_target.damage_dealt += 1;
+ frag_attacker.damage_dealt += 1; // TODO: change this to a specific hitsound for armor hit
+ Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, frag_target.armorvalue);
+ }
+ }
+
+ if(IS_PLAYER(frag_attacker) && DEATH_ISWEAPON(frag_deathtype, WEP_BLASTER))
{
if(frag_deathtype & HITTYPE_SECONDARY)
{
frag_force = '0 0 0';
}
}
- else if(frag_target.armorvalue)
- {
- frag_target.armorvalue -= 1;
- frag_damage = 0;
- frag_target.damage_dealt += 1;
- frag_attacker.damage_dealt += 1; // TODO: change this to a specific hitsound for armor hit
- Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, frag_target.armorvalue);
- }
}
}
frag_mirrordamage = 0;
}
- if(frag_target.items & IT_STRENGTH)
+ if((frag_target.buffs & BUFF_INVISIBLE) || (frag_target.items & IT_STRENGTH))
yoda = 1;
return false;
void nade_touch()
{
- float is_weapclip = 0;
+ /*float is_weapclip = 0;
if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NODRAW)
if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NONSOLID))
if (!(trace_dphitcontents & DPCONTENTS_OPAQUE))
- is_weapclip = 1;
- if(ITEM_TOUCH_NEEDKILL() || is_weapclip)
+ is_weapclip = 1;*/
+ if(ITEM_TOUCH_NEEDKILL()) // || is_weapclip)
{
remove(self);
return;
Kill_Notification(NOTIF_ONE_ONLY, e, MSG_CENTER_CPID, CPID_NADES);
- setorigin(_nade, w_shotorg + (v_right * 25) * -1);
+ vector offset = (v_forward * autocvar_g_nades_throw_offset.x)
+ + (v_right * autocvar_g_nades_throw_offset.y)
+ + (v_up * autocvar_g_nades_throw_offset.z);
+ if(autocvar_g_nades_throw_offset == '0 0 0')
+ offset = '0 0 0';
+
+ setorigin(_nade, w_shotorg + offset + (v_right * 25) * -1);
//setmodel(_nade, "models/weapons/v_ok_grenade.md3");
//setattachment(_nade, world, "");
PROJECTILE_MAKETRIGGER(_nade);
if (trace_startsolid)
setorigin(_nade, e.origin);
- if(self.v_angle.x >= 70 && self.v_angle.x <= 110)
+ if(self.v_angle.x >= 70 && self.v_angle.x <= 110 && self.BUTTON_CROUCH)
_nade.velocity = '0 0 100';
else if(autocvar_g_nades_nade_newton_style == 1)
_nade.velocity = e.velocity + _velocity;
MUTATOR_HOOKFUNCTION(ok_PlayerDies)
{
entity oldself = self;
+ entity targ = ((frag_attacker) ? frag_attacker : frag_target);
if(self.flags & FL_MONSTER)
{
self.gravity = 1;
self.reset = SUB_Remove;
setorigin(self, frag_target.origin + '0 0 32');
- self.velocity = '0 0 200' + normalize(frag_attacker.origin - self.origin) * 500;
+ self.velocity = '0 0 200' + normalize(targ.origin - self.origin) * 500;
self.classname = "droppedweapon"; // hax
SUB_SetFade(self, time + 5, 1);
self = oldself;
self.jump_interval = time + WEP_CVAR_PRI(blaster, refire) * W_WeaponRateFactor();
makevectors(self.v_angle);
+ int oldwep = self.weapon;
+ self.weapon = WEP_BLASTER;
W_Blaster_Attack(
WEP_BLASTER | HITTYPE_SECONDARY,
WEP_CVAR_SEC(vaporizer, shotangle),
WEP_CVAR_SEC(vaporizer, delay),
WEP_CVAR_SEC(vaporizer, lifetime)
);
+ self.weapon = oldwep;
}
self.weapon_blocked = false;
{
return !(x < y || x == y || x > y);
}
+
+vector cross(vector a, vector b)
+{
+ return
+ '1 0 0' * (a.y * b.z - a.z * b.y)
+ + '0 1 0' * (a.z * b.x - a.x * b.z)
+ + '0 0 1' * (a.x * b.y - a.y * b.x);
+}
const float M_2_SQRTPI = 1.12837916709551257390; /* 2/sqrt(pi) */
const float M_SQRT2 = 1.41421356237309504880; /* sqrt(2) */
const float M_SQRT1_2 = 0.70710678118654752440; /* 1/sqrt(2) */
+
+// Non-<math.h> stuff follows here.
+vector cross(vector a, vector b);
+
#endif
{
vector org, ang, norm, point;
float area;
- vector tri, a, b, c, p, q, n;
+ vector tri, a, b, c, n;
float i_s, i_t, n_t;
string tex;
a = getsurfacepoint(self, i_s, tri.x);
b = getsurfacepoint(self, i_s, tri.y);
c = getsurfacepoint(self, i_s, tri.z);
- p = b - a;
- q = c - a;
- n = '1 0 0' * (q.y * p.z - q.z * p.y)
- + '0 1 0' * (q.z * p.x - q.x * p.z)
- + '0 0 1' * (q.x * p.y - q.y * p.x);
+ n = cross(c - a, b - a);
area = area + vlen(n);
norm = norm + n;
point = point + vlen(n) * (a + b + c);
makevectors (self.angles);
self.movedir = v_forward;
}
- self.warpzone_isboxy = 1;
- if(self.model != "")
+ if(self.model == "")
+ {
+ // It's a box! No need to match with exacttriggers.
+ self.warpzone_isboxy = 1;
+ }
+ else
{
mi = self.mins;
ma = self.maxs;
// let mapper-set mins/maxs override the model's bounds if set
if(mi != '0 0 0' || ma != '0 0 0')
{
+ // It's a box! No need to match with exacttriggers.
self.mins = mi;
self.maxs = ma;
+ self.warpzone_isboxy = 1;
}
- else
- self.warpzone_isboxy = 0; // enable exacttrigger matching
}
setorigin(self, self.origin);
if(self.scale)