entity frag_attacker = M_ARGV(1, entity);
entity frag_target = M_ARGV(2, entity);
float frag_damage = M_ARGV(7, float);
- float damage_take = M_ARGV(4, float);
- float damage_save = M_ARGV(5, float);
+ float damage_take = bound(0, M_ARGV(4, float), GetResourceAmount(frag_target, RESOURCE_HEALTH));
+ float damage_save = bound(0, M_ARGV(5, float), GetResourceAmount(frag_target, RESOURCE_ARMOR));
float excess = max(0, frag_damage - damage_take - damage_save);
- if (frag_target != frag_attacker && IS_PLAYER(frag_attacker))
+ if (frag_target != frag_attacker && IS_PLAYER(frag_attacker) && DIFF_TEAM(frag_target, frag_attacker))
GameRules_scoring_add_team(frag_attacker, SCORE, (frag_damage - excess) * autocvar_g_ca_damage2score_multiplier);
}
this.active = ACTIVE_ACTIVE;
+ this.draggable = drag_undraggable;
+
// damage when blocked
setblocked(this, generic_plat_blocked);
if(this.dmg && (this.message == ""))
precache_sound(this.noise);
this.active = ACTIVE_ACTIVE;
+ this.draggable = drag_undraggable;
this.pos1 = this.origin;
this.pos2 = this.pos1 + this.movedir*(fabs(this.movedir*this.size) - this.lip);
this.angles = '0 0 0';
this.classname = "plat";
+ this.draggable = drag_undraggable;
if (!InitMovingBrushTrigger(this))
return;
this.effects |= EF_LOWPRECISION;
{
FOREACH_CLIENT(IS_PLAYER(it) && it != this && vdist(it.origin - this.origin, <=, autocvar_g_buffs_medic_heal_range),
{
- if (!SAME_TEAM(it, this))
+ if (DIFF_TEAM(it, this))
{
continue;
}
if (autocvar_g_spawn_near_teammate_ignore_spawnpoint_max && tested >= autocvar_g_spawn_near_teammate_ignore_spawnpoint_max) break;
if (PHYS_INPUT_BUTTON_CHAT(it)) continue;
- if (!SAME_TEAM(player, it)) continue;
+ if (DIFF_TEAM(player, it)) continue;
if (autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health && GetResourceAmount(it, RESOURCE_HEALTH) < autocvar_g_balance_health_regenstable) continue;
if (IS_DEAD(it)) continue;
if (time < it.msnt_timer) continue;
bool _Movetype_TestEntityPosition(vector ofs) // SV_TestEntityPosition
{
entity this = _Movetype_TestEntityPosition_ent;
-// vector org = this.origin + ofs;
+ vector org = this.origin + ofs;
int cont = this.dphitcontentsmask;
this.dphitcontentsmask = DPCONTENTS_SOLID;
- tracebox(this.origin, this.mins, this.maxs, this.origin, ((this.move_movetype == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), this);
+ tracebox(org, this.mins, this.maxs, org, ((this.move_movetype == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), this);
this.dphitcontentsmask = cont;
if(trace_startsolid)
return false;
}
-bool _Movetype_UnstickEntity(entity this) // SV_UnstickEntity
+int _Movetype_UnstickEntity(entity this) // SV_UnstickEntity
{
_Movetype_TestEntityPosition_ent = this;
if (!_Movetype_TestEntityPosition(' 0 0 0')) {
- return true;
+ return UNSTICK_FINE;
}
#define X(v) if (_Movetype_TestEntityPosition(v))
X('-1 0 0') X(' 1 0 0')
{
LOG_DEBUGF("Can't unstick an entity (edict: %d, classname: %s, origin: %s)",
etof(this), this.classname, vtos(this.origin));
- return false;
+ return UNSTICK_STUCK;
}
}
LOG_DEBUGF("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)",
etof(this), this.classname, vtos(this.origin));
_Movetype_LinkEdict(this, true);
- return true;
+ return UNSTICK_FIXED;
+}
+
+void _Movetype_CheckStuck(entity this) // SV_CheckStuck
+{
+ int unstick = _Movetype_UnstickEntity(this); // sets test position entity
+ switch(unstick)
+ {
+ case UNSTICK_FINE:
+ this.oldorigin = this.origin;
+ break;
+ case UNSTICK_FIXED:
+ break; // already sorted
+ case UNSTICK_STUCK:
+ vector offset = this.oldorigin - this.origin;
+ if(!_Movetype_TestEntityPosition(offset))
+ _Movetype_LinkEdict(this, false);
+ // couldn't unstick, should we warn about this?
+ break;
+ }
}
vector _Movetype_ClipVelocity(vector vel, vector norm, float f) // SV_ClipVelocity
.float move_suspendedinair;
.float move_didgravity;
+// unsticking
+const int UNSTICK_FINE = 0;
+const int UNSTICK_FIXED = 1;
+const int UNSTICK_STUCK = 2;
+
void _Movetype_WallFriction(entity this, vector stepnormal);
int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnormal, float stepheight);
void _Movetype_CheckVelocity(entity this);
void _Movetype_CheckWaterTransition(entity ent);
+void _Movetype_CheckStuck(entity this);
float _Movetype_CheckWater(entity ent);
void _Movetype_LinkEdict_TouchAreaGrid(entity this);
void _Movetype_LinkEdict(entity this, float touch_triggers);
void _Movetype_LinkEdict(entity this, float touch_triggers);
void _Movetype_LinkEdict_TouchAreaGrid(entity this);
-float _Movetype_UnstickEntity(entity this);
+int _Movetype_UnstickEntity(entity this);
const int MAX_CLIP_PLANES = 5;
const int MOVETYPE_ANGLECLIP = 2;
#endif
+const int MOVETYPE_QCPLAYER = 150; // QC-driven player physics, no think functions!
+
const int FL_ONSLICK = BIT(20);
const int MOVETYPE_FAKEPUSH = 13;
return;
if (GAMEPLAYFIX_UNSTICKPLAYERS(this))
- _Movetype_UnstickEntity(this);
+ _Movetype_CheckStuck(this);
bool applygravity = (!_Movetype_CheckWater(this) && this.move_movetype == MOVETYPE_WALK && !(this.flags & FL_WATERJUMP));
{
// this has been disabled so that you can't jump when you are stepping
// up while already jumping (also known as the Quake2 double jump bug)
+ // LordHavoc: disabled this check so you can walk on monsters/players
+ //if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP)
+ if(GAMEPLAYFIX_STEPDOWN(this) == 2)
+ {
+ SET_ONGROUND(this);
+ this.groundentity = trace_ent;
+ }
}
else
{
// client side physics
bool Physics_Valid(string thecvar)
{
- return autocvar_g_physics_clientselect && thecvar != "" && thecvar && thecvar != "default" && strhasword(autocvar_g_physics_clientselect_options, thecvar);
+ return thecvar != "" && thecvar && thecvar != "default" && strhasword(autocvar_g_physics_clientselect_options, thecvar);
}
float Physics_ClientOption(entity this, string option, float defaultval)
{
+ if(!autocvar_g_physics_clientselect)
+ return defaultval;
+
if(IS_REAL_CLIENT(this) && Physics_Valid(CS(this).cvar_cl_physics))
{
string s = strcat("g_physics_", CS(this).cvar_cl_physics, "_", option);
if(cvar_type(s) & CVAR_TYPEFLAG_EXISTS)
return cvar(s);
}
- if(autocvar_g_physics_clientselect && autocvar_g_physics_clientselect_default && autocvar_g_physics_clientselect_default != "")
+ if(autocvar_g_physics_clientselect_default && autocvar_g_physics_clientselect_default != "" && autocvar_g_physics_clientselect_default != "default")
{
+ // NOTE: not using Physics_Valid here, so the default can be forced to something normally unavailable
string s = strcat("g_physics_", autocvar_g_physics_clientselect_default, "_", option);
if(cvar_type(s) & CVAR_TYPEFLAG_EXISTS)
return cvar(s);
return 0;
// crude hack to enforce switching weapons
- if(g_cts && item.itemdef.instanceOfWeaponPickup)
+ if(g_cts && item.itemdef.instanceOfWeaponPickup && !CS(player).cvar_cl_cts_noautoswitch)
{
for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
{
#include "item/container.qh"
#include "item/borderimage.qh"
- METHOD(Item, destroy, void(Item this))
+ METHOD(MenuItem, destroy, void(MenuItem this))
{
// free memory associated with this
}
- METHOD(Item, relinquishFocus, void(Item this))
+ METHOD(MenuItem, relinquishFocus, void(MenuItem this))
{
entity par = this.parent;
if (!par) return;
if (par.instanceOfContainer) par.setFocus(par, NULL);
}
- METHOD(Item, resizeNotify, void(Item this, vector relOrigin, vector relSize, vector absOrigin, vector absSize))
+ METHOD(MenuItem, resizeNotify, void(MenuItem this, vector relOrigin, vector relSize, vector absOrigin, vector absSize))
{
this.origin = absOrigin;
this.size = absSize;
}
int autocvar_menu_showboxes;
- METHOD(Item, draw, void(Item this))
+ METHOD(MenuItem, draw, void(MenuItem this))
{
if (!autocvar_menu_showboxes) return;
vector rgb = '1 0 1';
}
}
- METHOD(Item, showNotify, void(Item this))
+ METHOD(MenuItem, showNotify, void(MenuItem this))
{}
- METHOD(Item, hideNotify, void(Item this))
+ METHOD(MenuItem, hideNotify, void(MenuItem this))
{}
- METHOD(Item, keyDown, float(Item this, float scan, float ascii, float shift))
+ METHOD(MenuItem, keyDown, float(MenuItem this, float scan, float ascii, float shift))
{
return 0; // unhandled
}
- METHOD(Item, keyUp, float(Item this, float scan, float ascii, float shift))
+ METHOD(MenuItem, keyUp, float(MenuItem this, float scan, float ascii, float shift))
{
return 0; // unhandled
}
- METHOD(Item, mouseMove, float(Item this, vector pos))
+ METHOD(MenuItem, mouseMove, float(MenuItem this, vector pos))
{
return 0; // unhandled
}
- METHOD(Item, mousePress, bool(Item this, vector pos))
+ METHOD(MenuItem, mousePress, bool(MenuItem this, vector pos))
{
return false; // unhandled
}
- METHOD(Item, mouseDrag, float(Item this, vector pos))
+ METHOD(MenuItem, mouseDrag, float(MenuItem this, vector pos))
{
return 0; // unhandled
}
- METHOD(Item, mouseRelease, float(Item this, vector pos))
+ METHOD(MenuItem, mouseRelease, float(MenuItem this, vector pos))
{
return 0; // unhandled
}
void m_play_focus_sound();
- METHOD(Item, focusEnter, void(Item this))
+ METHOD(MenuItem, focusEnter, void(MenuItem this))
{
if (this.allowFocusSound) m_play_focus_sound();
}
- METHOD(Item, focusLeave, void(Item this))
+ METHOD(MenuItem, focusLeave, void(MenuItem this))
{}
- METHOD(Item, toString, string(Item this))
+ METHOD(MenuItem, toString, string(MenuItem this))
{
return string_null;
}
#include "draw.qh"
#include "menu.qh"
-CLASS(Item, Object)
- METHOD(Item, draw, void(Item));
- METHOD(Item, keyDown, float(Item, float, float, float));
- METHOD(Item, keyUp, float(Item, float, float, float));
- METHOD(Item, mouseMove, float(Item, vector));
- METHOD(Item, mousePress, bool(Item this, vector pos));
- METHOD(Item, mouseDrag, float(Item, vector));
- METHOD(Item, mouseRelease, float(Item, vector));
- METHOD(Item, focusEnter, void(Item));
- METHOD(Item, focusLeave, void(Item));
- METHOD(Item, resizeNotify, void(Item, vector, vector, vector, vector));
- METHOD(Item, relinquishFocus, void(Item));
- METHOD(Item, showNotify, void(Item));
- METHOD(Item, hideNotify, void(Item));
- METHOD(Item, toString, string(Item));
- METHOD(Item, destroy, void(Item));
- ATTRIB(Item, focused, float, 0);
- ATTRIB(Item, focusable, float, 0);
- ATTRIB(Item, allowFocusSound, float, 0);
- ATTRIB(Item, parent, entity);
- ATTRIB(Item, preferredFocusPriority, float, 0);
- ATTRIB(Item, origin, vector, '0 0 0');
- ATTRIB(Item, size, vector, '0 0 0');
- ATTRIB(Item, tooltip, string);
-ENDCLASS(Item)
+CLASS(MenuItem, Object)
+ METHOD(MenuItem, draw, void(MenuItem));
+ METHOD(MenuItem, keyDown, float(MenuItem, float, float, float));
+ METHOD(MenuItem, keyUp, float(MenuItem, float, float, float));
+ METHOD(MenuItem, mouseMove, float(MenuItem, vector));
+ METHOD(MenuItem, mousePress, bool(MenuItem this, vector pos));
+ METHOD(MenuItem, mouseDrag, float(MenuItem, vector));
+ METHOD(MenuItem, mouseRelease, float(MenuItem, vector));
+ METHOD(MenuItem, focusEnter, void(MenuItem));
+ METHOD(MenuItem, focusLeave, void(MenuItem));
+ METHOD(MenuItem, resizeNotify, void(MenuItem, vector, vector, vector, vector));
+ METHOD(MenuItem, relinquishFocus, void(MenuItem));
+ METHOD(MenuItem, showNotify, void(MenuItem));
+ METHOD(MenuItem, hideNotify, void(MenuItem));
+ METHOD(MenuItem, toString, string(MenuItem));
+ METHOD(MenuItem, destroy, void(MenuItem));
+ ATTRIB(MenuItem, focused, float, 0);
+ ATTRIB(MenuItem, focusable, float, 0);
+ ATTRIB(MenuItem, allowFocusSound, float, 0);
+ ATTRIB(MenuItem, parent, entity);
+ ATTRIB(MenuItem, preferredFocusPriority, float, 0);
+ ATTRIB(MenuItem, origin, vector, '0 0 0');
+ ATTRIB(MenuItem, size, vector, '0 0 0');
+ ATTRIB(MenuItem, tooltip, string);
+ENDCLASS(MenuItem)
#include <menu/item.qh>
-CLASS(Container, Item)
+CLASS(Container, MenuItem)
METHOD(Container, draw, void(entity));
METHOD(Container, keyUp, float(entity, float, float, float));
METHOD(Container, keyDown, float(entity, float, float, float));
#pragma once
#include "../item.qh"
-CLASS(Image, Item)
+CLASS(Image, MenuItem)
METHOD(Image, configureImage, void(entity, string));
METHOD(Image, draw, void(entity));
METHOD(Image, toString, string(entity));
}
// skipping SUPER(InputBox).draw(me);
- Item_draw(me);
+ MenuItem_draw(me);
}
void InputBox_showNotify(entity me)
#pragma once
#include "../item.qh"
-CLASS(Label, Item)
+CLASS(Label, MenuItem)
METHOD(Label, configureLabel, void(entity, string, float, float));
METHOD(Label, draw, void(entity));
METHOD(Label, resizeNotify, void(entity, vector, vector, vector, vector));
#pragma once
#include "../item.qh"
-CLASS(ListBox, Item)
+CLASS(ListBox, MenuItem)
METHOD(ListBox, resizeNotify, void(entity, vector, vector, vector, vector));
METHOD(ListBox, configureListBox, void(entity, float, float));
METHOD(ListBox, draw, void(entity));
#pragma once
#include "../item.qh"
-CLASS(XonoticCrosshairPreview, Item)
+CLASS(XonoticCrosshairPreview, MenuItem)
METHOD(XonoticCrosshairPreview, configureXonoticCrosshairPreview, void(entity));
METHOD(XonoticCrosshairPreview, draw, void(entity));
ATTRIB(XonoticCrosshairPreview, src, string);
#pragma once
#include "../item.qh"
-CLASS(XonoticPicker, Item)
+CLASS(XonoticPicker, MenuItem)
METHOD(XonoticPicker, configureXonoticPicker, void(entity));
METHOD(XonoticPicker, mousePress, bool(XonoticPicker this, vector pos));
METHOD(XonoticPicker, mouseRelease, float(entity, vector));
}
// Find e and pick
if(e && pick)
- if(Drag_IsDraggable(e))
+ if(Drag_IsDraggable(e, this))
{
if(ischeat)
IS_CHEAT(this, 0, 0, CHRAME_DRAG);
}
}
-float Drag_IsDraggable(entity draggee)
+bool drag_undraggable(entity draggee, entity dragger)
+{
+ // stuff probably shouldn't need this, we should figure out why they do!
+ // exceptions of course are observers and weapon entities, where things mess up
+ return false;
+}
+
+float Drag_IsDraggable(entity draggee, entity dragger)
{
// TODO add more checks for bad stuff here
if(draggee == NULL)
return false;
- if(draggee.classname == "func_bobbing")
- return false;
if(draggee.classname == "door") // FIXME find out why these must be excluded, or work around the problem (trying to drag these causes like 4 fps)
- return false;
- if(draggee.classname == "plat")
- return false;
- if(draggee.classname == "func_button")
- return false;
+ return false; // probably due to BSP collision
// if(draggee.model == "")
// return false;
- if(IS_SPEC(draggee))
- return false;
- if(IS_OBSERVER(draggee))
- return false;
- if(draggee.classname == "exteriorweaponentity")
- return false;
- if(draggee.classname == "weaponentity")
- return false;
- return true;
+ return ((draggee.draggable) ? draggee.draggable(draggee, dragger) : true);
}
float Drag_MayChangeAngles(entity draggee)
dragger.dragentity = NULL;
return false;
}
- if(!Drag_CanDrag(dragger) || !Drag_IsDraggable(dragger.dragentity))
+ if(!Drag_CanDrag(dragger) || !Drag_IsDraggable(dragger.dragentity, dragger))
{
Drag_Finish(dragger);
return false;
const float CHRAME_DRAG = 8;
+bool drag_undraggable(entity draggee, entity dragger);
+
+.bool(entity this, entity dragger) draggable;
void Drag_MoveDrag(entity from, entity to); // call this from CopyBody
void DragBox_Think(entity this);
float Drag(entity this, float force_allow_pick, float ischeat);
void Drag_Begin(entity dragger, entity draggee, vector touchpoint);
void Drag_Finish(entity dragger);
-float Drag_IsDraggable(entity draggee);
+bool Drag_IsDraggable(entity draggee, entity dragger);
float Drag_MayChangeAngles(entity draggee);
void Drag_MoveForward(entity dragger);
void Drag_SetSpeed(entity dragger, float s);
this.crouch = false;
STAT(REVIVE_PROGRESS, this) = 0;
this.revival_time = 0;
+ this.draggable = drag_undraggable;
this.items = 0;
STAT(WEAPONS, this) = '0 0 0';
this.event_damage = PlayerDamage;
this.event_heal = PlayerHeal;
+ this.draggable = func_null;
+
if(!this.bot_attack)
IL_PUSH(g_bot_targets, this);
this.bot_attack = true;
if (IS_REAL_CLIENT(this))
sv_notice_join(this);
+ this.move_qcphysics = true;
+
// update physics stats (players can spawn before physics runs)
Physics_UpdateStats(this);
void Player_Physics(entity this)
{
- set_movetype(this, this.move_movetype);
+ this.movetype = (this.move_qcphysics) ? MOVETYPE_QCPLAYER : this.move_movetype;
if(!this.move_qcphysics)
return;
ATTRIB(Client, cvar_cl_accuracy_data_receive, bool, this.cvar_cl_accuracy_data_receive);
ATTRIBARRAY(Client, cvar_cl_weaponpriorities, string, 10);
ATTRIB(Client, cvar_cl_weaponpriority, string, this.cvar_cl_weaponpriority);
+ ATTRIB(Client, cvar_cl_cts_noautoswitch, bool, this.cvar_cl_cts_noautoswitch);
METHOD(Client, m_unwind, bool(Client this));
.float cvar_cl_jetpack_jump;
.float cvar_cl_movement_track_canjump;
.float cvar_cl_newusekeysupported;
+.float cvar_cl_cts_noautoswitch;
.string cvar_g_xonoticversion;
.string cvar_cl_weaponpriority;
REPLICATE(cvar_g_xonoticversion, string, "g_xonoticversion");
+REPLICATE(cvar_cl_cts_noautoswitch, bool, "cl_cts_noautoswitch");
+
/**
* @param f -1: cleanup, 0: request, 1: receive
*/
if (this != attacker) {
float realdmg = damage - excess;
- if (IS_PLAYER(attacker)) {
+ if (IS_PLAYER(attacker) && DIFF_TEAM(attacker, this)) {
GameRules_scoring_add(attacker, DMG, realdmg);
}
if (IS_PLAYER(this)) {
setthink(view, CL_Weaponentity_Think);
view.nextthink = time;
view.viewmodelforclient = actor;
+ view.draggable = drag_undraggable;
setcefc(view, CL_Weaponentity_CustomizeEntityForClient);
wepent_link(view);
entity exterior = actor.exteriorweaponentity = new(exteriorweaponentity);
exterior.solid = SOLID_NOT;
exterior.owner = actor;
+ exterior.draggable = drag_undraggable;
exterior.weaponentity_fld = weaponentity;
setorigin(exterior, '0 0 0');
setthink(exterior, CL_ExteriorWeaponentity_Think);
seta cl_race_cptimes_showself 1 "Always show your own times as well as the current best on checkpoints in Race/CTS"
seta cl_race_cptimes_onlyself 0 "Only show your own times on checkpoints in Race/CTS"
+seta cl_cts_noautoswitch 0 "Prevent forced switching to new weapons in CTS"
+
set cl_stripcolorcodes 0 "experimental feature (notes: strips ALL color codes from messages!)"
// Demo camera