|| DEATH_ISWEAPON(dt, WEP_VAPORIZER))
float br_CalculatePlayerDropAngle(entity this);
+bool br_PositionDropMember(entity this, entity leader, int position, float disconnect_range);
void br_LastPlayerForSquad_Notify(entity squad);
void br_RemovePlayer(entity player);
void br_Revive(entity player);
const float br_drop_time_secs = 1;
const float drop_speed_vertical_max = 0.9;
+const float drop_distance_disconnect = 32;
+const float drop_speed_crash = 0.9;
bool br_started = false;
.bool br_ring_warned;
.float br_drop_time;
.float br_force_drop_distance;
.int br_drop_launch;
.int br_drop_detached;
+.float br_drop_position;
.bool br_drop_instructions;
.float br_ring_damage_time;
{
player.br_squad.br_squad_drop_leader = player;
- vector drop_base_offset;
- drop_base_offset.x = cos((player.angles.y + 90) * DEG2RAD);
- drop_base_offset.y = sin((player.angles.y + 90) * DEG2RAD);
- drop_base_offset.z = 0;
- drop_base_offset = drop_base_offset * vlen(vec2(player.maxs - player.mins)) + drop_base_offset * 32; // I hope individual players never get different mins/maxs
+ bool other_side = false;
+ int drop_position = 1;
- vector drop_offset = drop_base_offset;
+ if(random() < 0.5)
+ drop_position *= -1;
- FOREACH_CLIENT(IS_PLAYER(it) && (it != player) && SAME_SQUAD(it, player) && (STAT(DROP, it) == DROP_TRANSPORT), {
+ FOREACH_CLIENT_RANDOM(IS_PLAYER(it) && (it != player) && SAME_SQUAD(it, player) && (STAT(DROP, it) == DROP_TRANSPORT), {
it.effects &= ~EF_NODRAW;
it.takedamage = DAMAGE_AIM;
it.solid = SOLID_SLIDEBOX;
it.br_drop_detached = 0;
Send_Notification(NOTIF_ONE_ONLY, it, MSG_CENTER, CENTER_BR_DROP_DETACH);
- setorigin(it, player.origin + drop_offset); // FIXME: this can teleport players into brushes/void
- drop_offset += drop_base_offset;
+ it.br_drop_position = drop_position;
+ if(other_side)
+ drop_position += copysign(1, drop_position);
+ drop_position *= -1;
+ other_side = !other_side;
+
+ br_PositionDropMember(it, player, it.br_drop_position, -1);
it.velocity = player.velocity;
it.angles = player.angles;
// TODO: improve dropping physics
if(STAT(DROP, player) == DROP_FALLING){
- if(!IS_ONGROUND(player) && (player.waterlevel < WATERLEVEL_SWIMMING) && ((tracebox(player.origin, player.mins, player.maxs, player.origin - '0 0 1', MOVE_NOMONSTERS, player), trace_fraction) >= 1)) // IS_ONGROUND doesn't work if jump is held (jump is theoretically blocked until landed)
+ float maxairspeed = PHYS_MAXAIRSPEED(player) * max(maxspeed_mod, 1);
+ float mindropspeed = maxairspeed * max(autocvar_g_br_drop_speed_min, 0);
+ float dropspeed = vlen(vec2(player.velocity) + eZ * min(player.velocity.z, 0));
+ if(player.velocity.z > 0)
+ dropspeed -= vlen(player.velocity) - dropspeed;
+ if(!IS_ONGROUND(player) && (player.waterlevel < WATERLEVEL_SWIMMING) && (dropspeed >= (mindropspeed * drop_speed_crash)) && ((tracebox(player.origin, player.mins, player.maxs, player.origin - '0 0 1', MOVE_NOMONSTERS, player), trace_fraction) >= 1)) // IS_ONGROUND doesn't work if jump is held (jump is theoretically blocked until landed)
{
ITEMS_STAT(player) |= IT_USING_JETPACK;
bool has_drop_leader = IN_SQUAD(player) && (player.br_drop_detached != 2) && (player.br_squad.br_squad_drop_leader && (STAT(DROP, player.br_squad.br_squad_drop_leader) == DROP_FALLING));
bool player_is_drop_leader = has_drop_leader && (player == player.br_squad.br_squad_drop_leader);
if(player_is_drop_leader || !has_drop_leader)
{
- float maxairspeed = PHYS_MAXAIRSPEED(player) * max(maxspeed_mod, 1);
float maxdropspeed = maxairspeed * max(autocvar_g_br_drop_speed_max, 0);
- float mindropspeed = maxairspeed * max(autocvar_g_br_drop_speed_min, 0);
float maxdropspeed_ratio = drop_speed_vertical_max; // moving straight down is glitchy
float mindropspeed_ratio = bound(0, autocvar_g_br_drop_speed_vertical_min, drop_speed_vertical_max);
float accel_dive = max(autocvar_g_br_drop_accel_dive, 0);
float accel_turn = max(autocvar_g_br_drop_accel_turn, 0);
- float dropspeed = vlen(player.velocity);
float dropspeed_xy = vlen(vec2(player.velocity));
float pitch_current = br_CalculatePlayerDropAngle(player);
float pitch_view = max(player.v_angle.x, 0);
// vertical wishvel using forward movement and the previously calculated ratio
wishvel.z = pitch_ratio_wish * bound(0, CS(player).movement.x / maxairspeed, 1);
// apply turn acceleration to wishvel
- wishvel.x *= accel_turn;
- wishvel.y *= accel_turn;
- wishvel.z *= accel_turn;
+ wishvel *= accel_turn;
player.velocity += wishvel * dt;
player.velocity = normalize(eZ * player.velocity.z + normalize(vec2(player.velocity)) * dropspeed_xy);
if(player_is_drop_leader)
{
FOREACH_CLIENT(IS_PLAYER(it) && (it != player) && SAME_SQUAD(it, player) && (it.br_drop_detached != 2) && (STAT(DROP, it) == DROP_FALLING), {
+ if(!br_PositionDropMember(it, player, it.br_drop_position, drop_distance_disconnect))
+ {
+ it.br_drop_detached = 2;
+ Kill_Notification(NOTIF_ONE_ONLY, it, MSG_CENTER, CPID_BR_DROP);
+ continue;
+ }
+
it.velocity = player.velocity;
it.angles = player.angles;
});
}
else
{
+ if(!br_PositionDropMember(player, player.br_squad.br_squad_drop_leader, player.br_drop_position, drop_distance_disconnect))
+ {
+ player.br_drop_detached = 2;
+ Kill_Notification(NOTIF_ONE_ONLY, player, MSG_CENTER, CPID_BR_DROP);
+ }
+
player.velocity = player.br_squad.br_squad_drop_leader.velocity;
player.angles = player.br_squad.br_squad_drop_leader.angles; // no fixangles, only moves the player model not the player view
}
return 0;
}
+bool br_PositionDropMember(entity this, entity leader, int position, float disconnect_range) {
+ float pl_mins = min(leader.mins.x, leader.mins.y);
+ float pl_maxs = max(leader.maxs.x, leader.maxs.y);
+
+ vector member_offset;
+ member_offset.x = cos((leader.angles.y + 90) * DEG2RAD);
+ member_offset.y = sin((leader.angles.y + 90) * DEG2RAD);
+ member_offset.z = 0;
+ member_offset *= pl_maxs - pl_mins + 32; // I hope individual players never get different mins/maxs
+ member_offset *= position;
+
+ vector member_destination = leader.origin + member_offset;
+
+ tracebox(this.origin, this.mins, this.maxs, member_destination, MOVE_NORMAL, this);
+
+ if((trace_fraction < 1) && (disconnect_range >= 0))
+ {
+ if(vlen(member_destination - trace_endpos) > disconnect_range)
+ return false;
+ }
+
+ setorigin(this, trace_endpos);
+ return true;
+}
+
void br_LastPlayerForSquad_Notify(entity squad)
{
entity player = br_SquadFindLastAlive(squad, false);
br_SquadMember_Add(current_squad, it);
GameRules_scoring_add(it, BR_SQUAD, current_squad.br_squad_id);
- setorigin(it, dropship.origin + eZ * (dropship.mins.z - it.maxs.z - 64)); // FIXME: this can teleport players into brushes/void
+ setorigin(it, dropship.origin + eZ * (dropship.mins.z - it.maxs.z - 64));
it.angles = vectoangles(dropship_path_direction) + '45 0 0';
it.fixangle = true;
it.velocity = '0 0 0';
{
entity this = dropship_spawn(VEH_RACER, autocvar_g_br_dropship_scale, autocvar_g_br_dropship_color);
+ vector pl_mins = '0 0 0';
+ vector pl_maxs = '0 0 0';
+
+ FOREACH_CLIENT(IS_PLAYER(it),
+ {
+ pl_mins = STAT(PL_MIN, it);
+ pl_maxs = STAT(PL_MAX, it);
+ break;
+ });
+
+ vector path_mins;
+ vector path_maxs;
+
+ path_mins.x = min(pl_mins.x, this.mins.x);
+ path_maxs.x = max(pl_maxs.x, this.maxs.x);
+
+ path_mins.y = min(pl_mins.y, this.mins.y);
+ path_maxs.y = max(pl_maxs.y, this.maxs.y);
+
+ float z_ofs = this.mins.z - pl_maxs.z - 64;
+ path_mins.z = min(pl_mins.z + z_ofs, this.mins.z);
+ path_maxs.z = max(pl_maxs.z + z_ofs, this.maxs.z);
+
+ vector saved_mins = this.mins;
+ vector saved_maxs = this.maxs;
+ setsize(this, path_mins, path_maxs);
+
for(int i = 0; i < 100; ++i) // try to find a dropship path multiple times
{
if(!MoveToRandomLocationWithinBounds(this, world.mins, world.maxs, this.dphitcontentsmask, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, 100, 8192, 1024, false))
vector mult;
- vector startorigin;
- startorigin = dropship_seekPoint(this, this.origin, 2, 2, 1);
- startorigin = dropship_seekPoint(this, startorigin, 0, 0, 1);
- startorigin = dropship_seekPoint(this, startorigin, 1, 0, 1);
+ vector startorigin = this.origin;
+ do {
+ startorigin = dropship_seekPoint(this, startorigin, 2, 2, 1);
+ startorigin = dropship_seekPoint(this, startorigin, 0, 0, 1);
+ startorigin = dropship_seekPoint(this, startorigin, 1, 0, 1);
+ } while((tracebox(startorigin, this.mins, this.maxs, startorigin + '0 0 1', MOVE_NORMAL, this), trace_fraction) >= 1);
+
mult = dropship_getMultipliers();
startorigin = dropship_seekPoint(this, startorigin, 0, 1, mult.x);
startorigin = dropship_seekPoint(this, startorigin, 1, 1, mult.y);
endorigin = dropship_seekPoint(this, startorigin, 0, 1, 1 - mult.x);
endorigin = dropship_seekPoint(this, endorigin, 1, 1, 1 - mult.y);
- endorigin = startorigin + normalize(endorigin - startorigin) * vlen(vec2(world.maxs - world.mins));
+ endorigin = startorigin + normalize(endorigin - startorigin) * 65536;
tracebox(startorigin, this.mins, this.maxs, endorigin, MOVE_NORMAL, this);
dropship_path_length = trace_fraction * vlen(endorigin - startorigin);
endorigin = trace_endpos;
dropship_path_direction = normalize(endorigin - startorigin);
+ setsize(this, saved_mins, saved_maxs);
+
setorigin(this, startorigin);
this.angles = vectoangles(dropship_path_direction);
this.velocity = '0 0 0';
vector second_end = '0 0 0';
first_end = orig;
- first_end = first_end - first_end * vec_axis * vec_axis + world.maxs * vec_axis * vec_axis;
+ first_end = first_end - first_end * vec_axis * vec_axis + 65536 * vec_axis;
first_fraction = (tracebox(orig, this.mins, this.maxs, first_end, MOVE_NORMAL, this), trace_fraction);
if(direction != 2)
{
second_end = orig;
- second_end = second_end - second_end * vec_axis * vec_axis + world.mins * vec_axis * vec_axis;
+ second_end = second_end - second_end * vec_axis * vec_axis + -65536 * vec_axis;
second_fraction = (tracebox(orig, this.mins, this.maxs, second_end, MOVE_NORMAL, this), trace_fraction);
}