I need maybe two arrays to store values and quickly have access to them being sorted.
For example float scan_fraction[4][2]
as the main array and int fraction_order[4]
as a order storage. The point is so I can use the order array to quickly access directions with highest values.
In the first dimension I store direction info, like fraction[FR_LEFT][VALUE]
and fraction[FR_LEFT][FR_ENABLED]
In the second dimension I store a float and another float (but could use int)that tells if the direction is enabled or conditionally enabled.
So in the end I could probably use a struct for this, and I would appreciate if someone gave examples how to do this also.
This is what I got:
void monster_jump(edict_t *self)
{
if(!(self->monsterinfo.aiflags & AI_JUMPDODGE) && !(self->monsterinfo.aiflags & AI_JUMPDODGEPROJ))
return;
if (!self->groundentity)
return;
vec3_t left, right;
vec3_t jump_dir = { 0 };
vec3_t back, front;
float scan_fraction[NUM_J_DIRS][2] =
{
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f
};
int fraction_order[NUM_J_DIRS] =
{
0, 0, 0, 0
};
int i = 0, each_tr_incomplete = 0, num_fullyenabled = 0, no_random_dir = 1, random_count = 0;
if (self->monsterinfo.aiflags & AI_JUMPDODGEPROJ && self->monsterinfo.jump_ent)
{
VectorSubtract(self->s.origin, self->monsterinfo.jump_ent->s.origin, jump_dir);
gi.bprintf(PRINT_HIGH, "monster_jump: should jump away from projectile!\n");
jump_dir[2] *= 0.25;
VectorNormalize(jump_dir);
vec3_t end;
VectorMA(self->s.origin, 128, jump_dir, end);
trace_t tr = gi.trace(self->s.origin, NULL, NULL, end, self, MASK_SHOT);
if (tr.fraction != 1)
{
VectorInverse(jump_dir);
jump_dir[0] *= 1 + (crandom() * 0.25);
jump_dir[0] *= 1 + (crandom() * 0.25);
VectorNormalize(jump_dir);
gi.bprintf(PRINT_HIGH, "monster_jump: should jump away from projectile!, path is blocked, so go randomly towards projectile\n");
}
//else
//gi.bprintf(PRINT_HIGH, "monster_jump: should jump away from projectile!\n");
}
else
{
gi.bprintf(PRINT_HIGH, "monster_jump: should jump away from danger!\n");
scan_fraction[FRAC_LEFT][FR_VAL] = scan_dir(self, SCAN_LEFT, 128, left);
scan_fraction[FRAC_RIGHT][FR_VAL] = scan_dir(self, SCAN_RIGHT, 128, right);
scan_fraction[FRAC_BACK][FR_VAL] = scan_dir(self, SCAN_BACKWARDS, 128, back);
scan_fraction[FRAC_FORWARD][FR_VAL] = scan_dir(self, SCAN_FORWARD, 128, front);
gi.bprintf(PRINT_HIGH, "monster_jump: left = %f, right = %f, back = %f, forw = %f!\n", scan_fraction[FRAC_LEFT][FR_VAL], scan_fraction[FRAC_RIGHT][FR_VAL], scan_fraction[FRAC_BACK][FR_VAL], scan_fraction[FRAC_FORWARD][FR_VAL]);
if (scan_fraction[FRAC_LEFT][FR_VAL] != COMPLETE_TR_FRACTION && scan_fraction[FRAC_RIGHT][FR_VAL] != COMPLETE_TR_FRACTION && scan_fraction[FRAC_BACK][FR_VAL] != COMPLETE_TR_FRACTION && scan_fraction[FRAC_FORWARD][FR_VAL] != COMPLETE_TR_FRACTION)
each_tr_incomplete = 1;
if (scan_fraction[FRAC_LEFT][FR_VAL] == COMPLETE_TR_FRACTION)
{
num_fullyenabled++;
scan_fraction[FRAC_LEFT][FR_ENABLED] = ENABLED_FULLY;
}
if (scan_fraction[FRAC_RIGHT][FR_VAL] == COMPLETE_TR_FRACTION)
{
num_fullyenabled++;
scan_fraction[FRAC_RIGHT][FR_ENABLED] = ENABLED_FULLY;
}
if (scan_fraction[FRAC_BACK][FR_VAL] == COMPLETE_TR_FRACTION)
{
num_fullyenabled++;
scan_fraction[FRAC_BACK][FR_ENABLED] = ENABLED_FULLY;
}
if (scan_fraction[FRAC_FORWARD][FR_VAL] == COMPLETE_TR_FRACTION)
{
num_fullyenabled++;
scan_fraction[FRAC_FORWARD][FR_ENABLED] = ENABLED_FULLY;
}
if (self->monsterinfo.aiflags & AI_JUMPATTACK && scan_fraction[FRAC_FORWARD][FR_VAL] > 1.0f) //let's skip this for now
{
self->monsterinfo.aiflags &= ~AI_JUMPATTACK;
gi.bprintf(PRINT_HIGH, "monster_jump: should jump forward!\n");
//goto jump_attack;
VectorSubtract(self->s.origin, front, jump_dir);
}
else
{
gi.bprintf(PRINT_HIGH, "monster_jump: starting qsort!\n");
size_t element_count = sizeof scan_fraction / sizeof scan_fraction[0]; // or NUM_J_DIRS
size_t element_size = sizeof scan_fraction[0];
qsort(scan_fraction, element_count, element_size, cmp2);
for (i = 0; i < NUM_J_DIRS; i++)
{
gi.bprintf(PRINT_HIGH, "monster_jump: running qsort! i = %i, \n", i);
fraction_order[i] = i;
if (each_tr_incomplete)
{
gi.bprintf(PRINT_HIGH, "monster_jump: each trace was incomplete\n", i);
scan_fraction[i][FR_ENABLED] = ENABLED_CONDITIONALLY;
}
else if (scan_fraction[i][FR_VAL] > 0.85f)
scan_fraction[i][FR_ENABLED] = ENABLED_FULLY;
}
gi.bprintf(PRINT_HIGH, "monster_jump: ended qsort! %f %f %f %f\n", scan_fraction[fraction_order[0]][FR_VAL], scan_fraction[fraction_order[1]][FR_VAL], scan_fraction[fraction_order[2]][FR_VAL], scan_fraction[fraction_order[3]][FR_VAL]);
if (num_fullyenabled) //there is at least one direction that can be used
{
full_traces:
random_count++;
gi.bprintf(PRINT_HIGH, "monster_jump: choosing from full traces, num = %i!\n", random_count);
if (random_count >= RANDOM_FR_MAX_COUNT)
goto random_direction;
switch(fraction_order[rand() % num_fullyenabled])
{
case FRAC_LEFT:
if (scan_fraction[FRAC_LEFT][FR_ENABLED] != ENABLED_FULLY)
{
gi.bprintf(PRINT_HIGH, "monster_jump: LEFT is disabled, retry\n");
goto full_traces;
}
gi.bprintf(PRINT_HIGH, "monster_jump: JUMP LEFT(FULL + RANDOM)\n");
VectorSubtract(self->s.origin, right, jump_dir);
break;
case FRAC_RIGHT:
if (scan_fraction[FRAC_RIGHT][FR_ENABLED] != ENABLED_FULLY)
{
gi.bprintf(PRINT_HIGH, "monster_jump: RIGHT is disabled, retry\n");
goto full_traces;
}
gi.bprintf(PRINT_HIGH, "monster_jump: JUMP RIGHT(FULL + RANDOM)\n");
VectorSubtract(self->s.origin, left, jump_dir);
break;
case FRAC_BACK:
if (scan_fraction[FRAC_BACK][FR_ENABLED] != ENABLED_FULLY)
{
gi.bprintf(PRINT_HIGH, "monster_jump: BACK is disabled, retry\n");
goto full_traces;
}
gi.bprintf(PRINT_HIGH, "monster_jump: JUMP BACK(FULL + RANDOM)\n");
VectorSubtract(self->s.origin, front, jump_dir);
break;
case FRAC_FORWARD:
if (scan_fraction[FRAC_FORWARD][FR_ENABLED] != ENABLED_FULLY)
{
gi.bprintf(PRINT_HIGH, "monster_jump: FORWARD is disabled, retry\n");
goto full_traces;
}
gi.bprintf(PRINT_HIGH, "monster_jump: JUMP FRONT(FULL + RANDOM)\n");
VectorSubtract(self->s.origin, back, jump_dir);
break;
}
}
else if (no_random_dir)
{
sorted_traces:
random_count++;
gi.bprintf(PRINT_HIGH, "monster_jump: choosing from sorted, num = %i!\n", random_count);
if (random_count >= RANDOM_FR_MAX_COUNT)
goto random_direction;
switch (fraction_order[0])
{
case FRAC_LEFT:
if (!scan_fraction[FRAC_LEFT][FR_ENABLED])
goto sorted_traces;
gi.bprintf(PRINT_HIGH, "monster_jump: JUMP LEFT(sorted + RANDOM)\n");
VectorSubtract(self->s.origin, right, jump_dir);
break;
case FRAC_RIGHT:
if (!scan_fraction[FRAC_RIGHT][FR_ENABLED])
goto sorted_traces;
gi.bprintf(PRINT_HIGH, "monster_jump: JUMP RIGHT(sorted + RANDOM)\n");
VectorSubtract(self->s.origin, left, jump_dir);
break;
case FRAC_BACK:
if (!scan_fraction[FRAC_BACK][FR_ENABLED])
goto sorted_traces;
gi.bprintf(PRINT_HIGH, "monster_jump: JUMP BACK(sorted + RANDOM)\n");
VectorSubtract(self->s.origin, front, jump_dir);
break;
case FRAC_FORWARD:
if (!scan_fraction[FRAC_FORWARD][FR_ENABLED])
goto sorted_traces;
gi.bprintf(PRINT_HIGH, "monster_jump: JUMP FRONT(sorted + RANDOM)\n");
VectorSubtract(self->s.origin, back, jump_dir);
break;
}
}
else
{
random_direction:
gi.bprintf(PRINT_HIGH, "monster_jump: choosing from random dir!\n");
switch (rand() % 4)
{
case FRAC_LEFT:
gi.bprintf(PRINT_HIGH, "monster_jump: JUMP LEFT(RANDOM)\n");
VectorSubtract(self->s.origin, right, jump_dir);
break;
case FRAC_RIGHT:
gi.bprintf(PRINT_HIGH, "monster_jump: JUMP RIGHT(RANDOM)\n");
VectorSubtract(self->s.origin, left, jump_dir);
break;
case FRAC_BACK:
gi.bprintf(PRINT_HIGH, "monster_jump: JUMP BACK(RANDOM)\n");
VectorSubtract(self->s.origin, front, jump_dir);
break;
case FRAC_FORWARD:
gi.bprintf(PRINT_HIGH, "monster_jump: JUMP FRONT(RANDOM)\n");
VectorSubtract(self->s.origin, back, jump_dir);
break;
}
}
}
VectorNormalize(jump_dir);
}
VectorMA(self->velocity, 300 + (100 * random()), jump_dir, self->velocity);
self->velocity[2] += 150 + random() * 50;
self->monsterinfo.jump_ent = NULL;
self->monsterinfo.aiflags &= ~AI_JUMPDODGEPROJ;
self->monsterinfo.aiflags &= ~AI_JUMPDODGE;
gi.sound(self, CHAN_AUTO, gi.soundindex(va("player/step%i.wav", rand() % 5)), 1, ATTN_IDLE, 0);
gi.sound(self, CHAN_AUTO, gi.soundindex(va("player/step%i.wav", rand() % 5)), 1, ATTN_IDLE, 0.05f);
}
Would be also nice to move from something medieval like:
random_direction: switch (rand() % 4) { case FRAC_LEFT: if (!scan_fraction[FRAC_LEFT][FR_ENABLED]) goto random_direction;
To something that doesn't use random numbers to make use of that one have a case (or cases) from a switch disabled.