Skip to content

Commit

Permalink
Fixed shambler bug (again). Added function to allow hellspawn to move…
Browse files Browse the repository at this point in the history
… to a lower height to avoid obstacles. May re-use this in the future to help flying monsters too.
  • Loading branch information
Vortex-Quake2 committed Sep 22, 2024
1 parent 1f721f4 commit 8843260
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 26 deletions.
169 changes: 149 additions & 20 deletions src/combat/abilities/flying_skull.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,37 +167,166 @@ void skull_move_vertical (edict_t *self, float dist)
VectorCopy(tr.endpos, self->s.origin);
}

float V_LookAheadCeilingHeight (edict_t *self, float lookAheadDist, int stepHeight, int stepSize)
{
vec3_t start, end, forward;
trace_t tr;

// trace forward
VectorCopy(self->s.origin, start);
AngleVectors(self->s.angles, forward, NULL, NULL);
VectorMA(start, lookAheadDist, forward, end);
tr = gi.trace(start, NULL, NULL, end, self, MASK_SOLID);
// check for obstruction
if (tr.fraction < 1)
{
/*
gi.WriteByte(svc_temp_entity);
gi.WriteByte(TE_DEBUGTRAIL);
gi.WritePosition(start);
gi.WritePosition(tr.endpos);
gi.multicast(tr.endpos, MULTICAST_ALL);
*/
VectorCopy(tr.endpos, start);
VectorCopy(tr.endpos, end);

while (1) // if we want to save cpu cycles, we could limit this to x vertical steps
{
// begin loop
end[2] -= stepHeight;
// trace down 1 step
tr = gi.trace(start, NULL, NULL, end, self, MASK_SOLID);
if (tr.fraction < 1)
{
//gi.dprintf("can't avoid obstruction with vertical move\n");
return self->s.origin[2]; // obstruction below, can't proceed any further (FIXME: returned value should be outside valid Z range, or just return current Z height)
}

// vertical line from wall to step below
/*
gi.WriteByte(svc_temp_entity);
gi.WriteByte(TE_DEBUGTRAIL);
gi.WritePosition(start);
gi.WritePosition(end);
gi.multicast(end, MULTICAST_ALL);
*/

start[2] = end[2]; // stepdown successful, start tracing at lower height
// trace forward 1 step
VectorMA(start, stepSize, forward, end);
tr = gi.trace(start, NULL, NULL, end, self, MASK_SOLID);
if (tr.fraction < 1)
continue; // obstruction ahead, continue loop until cleared or floor is reached

//gi.dprintf("cleared obstruction with vertical move!\n");

// horizontal line from step below to step forward below obstruction
/*
gi.WriteByte(svc_temp_entity);
gi.WriteByte(TE_DEBUGTRAIL);
gi.WritePosition(start);
gi.WritePosition(end);
gi.multicast(end, MULTICAST_ALL);
*/
//success = true; // found height that clears obstruction!
// now trace up from end to find the ceiling height
/*
VectorCopy(end, start);
end[2] += 8192;
tr = gi.trace(start, self->mins, self->maxs, end, self, MASK_SHOT);
// vertical line from cleared obstruction to ceiling
gi.WriteByte(svc_temp_entity);
gi.WriteByte(TE_DEBUGTRAIL);
gi.WritePosition(start);
gi.WritePosition(end);
gi.multicast(end, MULTICAST_ALL);
return tr.endpos[2];*/
return end[2];
}
}
// no obstruction
// now trace up from end to find the ceiling height
//gi.dprintf("no obstruction ahead\n");
VectorCopy(end, start);
end[2] += 8192;
tr = gi.trace(start, NULL, NULL, end, self, MASK_SOLID);
return tr.endpos[2];
}

void skull_move_vertical_goalpos(edict_t* self, float goalpos)
{
// move up or down to get to the desired height
if (goalpos > self->s.origin[2])
skull_move_vertical(self, SKULL_MOVE_VERTICAL_SPEED);
if (goalpos < self->s.origin[2])
skull_move_vertical(self, -SKULL_MOVE_VERTICAL_SPEED);
}

void skull_movetogoal (edict_t *self, edict_t *goal)
{
float temp, dist, speed, goalpos;
float temp, dist, speed, goalpos, ceilHeight;
vec3_t v;
que_t *slot=NULL;

self->style = SKULL_ATTACK;


// lock-on to enemy if he is visible, and we're not busy
// trying to avoid an obstruction
if (visible(self, goal) && (level.time > self->wait))
if (level.time > self->wait)
{
trace_t tr;
if (level.time > self->monsterinfo.Zchange_delay) // not trying to avoid obstruction vertically
{
ceilHeight = V_LookAheadCeilingHeight(self, 128, SKULL_MOVE_VERTICAL_SPEED, SKULL_MOVE_HORIZONTAL_SPEED);
if (ceilHeight < self->s.origin[2]) // found ceiling below us
{
ceilHeight -= 32; // subtract half-height of our bbox + 1, this is the highest we can go
//gi.dprintf("hellspawn is moving lower to clear obstruction\n");
goalpos = ceilHeight;
self->monsterinfo.eta = ceilHeight;
self->monsterinfo.Zchange_delay = level.time + 1.0;
skull_move_vertical_goalpos(self, goalpos);
}
else if (visible(self, goal)) // no obstructions found, goal is visible
{
//gi.dprintf("goal visible, move above it\n");
// set ideal yaw to look at the goal
VectorSubtract(goal->s.origin, self->s.origin, v);
self->ideal_yaw = vectoyaw(v);

// check for obstruction between goal origin and a position above it
goalpos = goal->absmax[2] + SKULL_HEIGHT; // float above enemy
VectorCopy(goal->s.origin, v);
v[2] = goalpos;
tr = gi.trace(goal->s.origin, NULL, NULL, v, goal, MASK_SOLID);
goalpos = tr.endpos[2];

// move up or down to get to the desired height
skull_move_vertical_goalpos(self, goalpos);

// strafe left or right around the goal once we get within desired range
skull_strafe(self, 0.5 * SKULL_MOVE_HORIZONTAL_SPEED);
}
else
{
//gi.dprintf("can't see goal. ceil %.0f height %.0f\n", ceilHeight, self->s.origin[2]);
}

//gi.dprintf("ceiling height %.1f\n", ceilHeight);
}
else // moving vertically to clear obstruction
{
goalpos = self->monsterinfo.eta; // this is the height of the ceiling below us
skull_move_vertical_goalpos(self, goalpos);
}

VectorSubtract(goal->s.origin, self->s.origin, v);
self->ideal_yaw = vectoyaw(v);

// vertical movement
goalpos = goal->absmax[2] + SKULL_HEIGHT; // float above enemy

// check for obstruction
VectorCopy(goal->s.origin, v);
v[2] = goalpos;
tr = gi.trace(goal->s.origin, NULL, NULL, v, goal, MASK_SOLID);
goalpos = tr.endpos[2];

if (goalpos > self->s.origin[2])
skull_move_vertical(self, SKULL_MOVE_VERTICAL_SPEED);
if (goalpos < self->s.origin[2])
skull_move_vertical(self, -SKULL_MOVE_VERTICAL_SPEED);

skull_strafe(self, 0.5*SKULL_MOVE_HORIZONTAL_SPEED);
}
else
{
//gi.dprintf("hellspawn is bumping around\n");
}

// horizontal movement
Expand Down
3 changes: 1 addition & 2 deletions src/entities/drone/baron_fire.c
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,7 @@ void baron_fire_die(edict_t* self, edict_t* inflictor, edict_t* attacker, int da
if (self->deadflag == DEAD_DEAD)
return;

//DroneList_Remove(self);
DroneList_Remove(self);

// regular death
if (random() > 0.5)
Expand All @@ -626,7 +626,6 @@ void baron_fire_die(edict_t* self, edict_t* inflictor, edict_t* attacker, int da
self->deadflag = DEAD_DEAD;
self->takedamage = DAMAGE_YES;
self->monsterinfo.currentmove = &baron_fire_move_death;
DroneList_Remove(self);
}

void baron_fire_melee(edict_t* self)
Expand Down
3 changes: 2 additions & 1 deletion src/entities/drone/drone_misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ edict_t *DroneList_Next(edict_t *ent)
if (next_drone != NULL && next_drone->monsterinfo.dronelist_index < ent->monsterinfo.dronelist_index) {
// wooee this is terrible
gi.dprintf("another horrible hack: next monster would have put us in an infinite loop. removing.\n");
DroneList_Remove(next_drone);
DroneList_Remove(next_drone); // note: this will fail because the next drone on the list is freed and the drone_list index is 0
return DroneList_Next(ent);
}

Expand All @@ -87,6 +87,7 @@ void DroneList_Insert(edict_t* new_ent)
}

/* we don't free it, we just remove it from the list */
//FIXME: add another parameter for index, so that if we accidentally pass it a freed entity on the list, it can actually remove it because the freed entity's dronelist_index is 0
void DroneList_Remove(edict_t *ent)
{
// is monster index within valid range of list?
Expand Down
6 changes: 3 additions & 3 deletions src/entities/drone/drone_shambler.c
Original file line number Diff line number Diff line change
Expand Up @@ -1031,7 +1031,7 @@ void shambler_die(edict_t* self, edict_t* inflictor, edict_t* attacker, int dama
ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
ThrowHead(self, "models/objects/gibs/head2/tris.md2", damage, GIB_ORGANIC);
//self->deadflag = DEAD_DEAD;
return;
//return;//FIXME: this will cause DroneList_Next to enter an infinite loop

#ifdef OLD_NOLAG_STYLE
M_Remove(self, false, false);
Expand All @@ -1048,13 +1048,13 @@ void shambler_die(edict_t* self, edict_t* inflictor, edict_t* attacker, int dama
if (self->deadflag == DEAD_DEAD)
return;

DroneList_Remove(self);

gi.sound(self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0);
self->deadflag = DEAD_DEAD;
self->takedamage = DAMAGE_YES;
self->monsterinfo.currentmove = &shambler_move_death;

DroneList_Remove(self);

if (self->activator && !self->activator->client)
{
self->activator->num_monsters_real--;
Expand Down

0 comments on commit 8843260

Please sign in to comment.