Forum

traceline "seeing" thru walls ?

Discuss programming in the QuakeC language.

Moderator: InsideQC Admins

traceline "seeing" thru walls ?

Postby frag.machine » Mon Mar 16, 2009 2:51 am

I am trying to make an item pointer for an isometric mod. Imagine the player walking by the map and, when he's facing a nearby item, npc or monster, a fancy arrow pops over the subject. After some tests everything worked as expected, until I realized that my item pointer was also "seeing" thru solid walls. In a test map with two completely separated rooms the item pointer can "sense" a nearby shambler in the other side of the wall. I am using the infront() and visible() functions from the stock ai.qc and, afaik they're correctly checking if trace_fraction returns 1 so this wasn't supposed to happen. Any ideas ?
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
User avatar
frag.machine
 
Posts: 2090
Joined: Sat Nov 25, 2006 1:49 pm

Postby Error » Mon Mar 16, 2009 9:13 am

should be easy to fix, paste your code here and we'll take a look.

bugs are always easy to fix if we see the problem ourselves
User avatar
Error
InsideQC Staff
 
Posts: 865
Joined: Fri Nov 05, 2004 5:15 am
Location: VA, USA

Postby frag.machine » Tue Mar 17, 2009 3:30 am

No problem. The cursor thing is toggled calling CursorSpawn () and CursorRemove () (at the end of the code) thru impulses in weapon.qc. Different skins tell to the player the target nature (monster, NPC, item, etc), and different frames are used according the target height. All this stuff works fine, but the cursor can "see" monsters thru the wall, which basically spoils things a bit from the gameplay viewpoint.

Code: Select all
void () CursorThink;
void (entity csr, entity target, float r) CursorUpdate =
{
    local float f;

    if (csr.oldenemy != target)
    {
        sprint (csr.owner, "DEBUG: cursor now tracking ");
        sprint (csr.owner, target.classname);
        sprint (csr.owner, "\n");
        csr.oldenemy = target;
    }

    setorigin (csr, target.origin);
    if (target.flags & FL_MONSTER)
    {
        if (r == RANGE_MELEE || r == RANGE_NEAR)
        {
            csr.skin = 7;
            csr.effects = csr.effects | EF_MUZZLEFLASH;
        }
        else
        {
            csr.skin = 6;
        }
    }

    if (target.flags & FL_NPC)
    {
        if (r == RANGE_MELEE || r == RANGE_NEAR)
        {
            csr.skin = 2;
            csr.effects = csr.effects | EF_MUZZLEFLASH;
        }
        else
        {
            csr.skin = 3;
        }

    }

    if (target.flags & FL_ITEM)
    {
        if (r == RANGE_MELEE || r == RANGE_NEAR)
        {
            csr.skin = 4;
        }
        else
        {
            csr.skin = 5;
        }

    }

    if (target.flags & FL_QUESTITEM)
    {
        if (r == RANGE_MELEE || r == RANGE_NEAR)
        {
            csr.skin = 0;
            csr.effects = csr.effects | EF_MUZZLEFLASH;
        }
        else
        {
            csr.skin = 1;
        }
    }

    f = target.absmax_z - target.absmin_z;
    f = f / 8;
    f = rint (f);
    f = 8 - f;
    if (f < 1)
    {
        f = 1;
    }
    if (f > 8)
    {
        f = 8;
    }

    csr.frame = f;
};

void () CursorThink =
{
    local entity    csr;
    local entity    tgt;
    local float     r, i;

    self.frame = 0;         // don't show by default
    csr = self;
    self = csr.owner;
    r = 50;
    while (r < 500)
    {
        tgt = findradius (self.origin, r);
        while (tgt)
        {
            i = range (tgt);
            if ((tgt.flags & FL_MONSTER) && (tgt.enemy == self))
            {
                // pissed off monsters have priority over any other stuff
                if ((visible (tgt)) && (infront (tgt)))
                {
                    CursorUpdate (csr, tgt, i);
                }
            }
            else if ((tgt.flags & FL_MONSTER) && (tgt.enemy != self))
            {
                // got an idle monster
                if ((visible (tgt)) && (infront (tgt)))
                {
                    CursorUpdate (csr, tgt, i);
                }
            }
            else if (tgt.flags & FL_NPC)
            {
                if ((visible (tgt)) && (infront (tgt)))
                {
                    CursorUpdate (csr, tgt, i);
                }
            }
            else if (tgt.flags & FL_ITEM)
            {
                if ((visible (tgt)) && (infront (tgt)))
                {
                    CursorUpdate (csr, tgt, i);
                }
            }

            tgt = tgt.chain;
        }

        r = r + 50;
    }

    self = csr;
    self.think = CursorThink;
    self.nextthink = time + 0.05;
};

void (entity pl) CursorSpawn =
{
    local   entity  csr;

    csr = spawn ();
    csr.classname = "func_cursor";
    csr.owner = pl;
    csr.solid = SOLID_NOT;
    csr.movetype = MOVETYPE_FLY;
    csr.flags = FL_ITEM;
    setmodel (csr, "progs/marker.mdl");
    setsize (self, '-16 -16 -24', '16 16 40');
    csr.skin = 0;
    csr.frame = 0;
    csr.think = CursorThink;
    csr.nextthink = time + 0.1;
};

void (entity pl) CursorRemove =
{
    local   entity  csr;

    csr = find (world, classname, "func_cursor");
    while (csr != world)
    {
        if (csr.owner == pl)
        {
            remove (csr);
            csr = find (world, classname, "func_cursor");
        }
        else
        {
            csr = find (csr, classname, "func_cursor");
        }
    }
};
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
User avatar
frag.machine
 
Posts: 2090
Joined: Sat Nov 25, 2006 1:49 pm

Postby Supa » Tue Mar 17, 2009 8:24 pm

Is the player that walks around the actual client or just a dummy? Because if the client is the third person camera and the .owner of the cursor, the 'self' that gets passed to visible() would point to the camera. If so, that'd explain why you're able to detect Shamblers through walls. :)
User avatar
Supa
 
Posts: 164
Joined: Tue Oct 26, 2004 8:10 am

Postby frag.machine » Wed Mar 18, 2009 1:00 am

Yes, the player is the actual client. I am using a modified engine with more flexible chase camera (so I can tilt, yaw and roll besides the stock Quake chase camera). Here follow some images to give more information about what I am doing here:

Image

Image

Image
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
User avatar
frag.machine
 
Posts: 2090
Joined: Sat Nov 25, 2006 1:49 pm

Postby Spike » Wed Mar 18, 2009 1:41 am

Blame your qcc.

if ((visible (tgt)) && (infront (tgt)))

will expand into:

storeent tgt->ofs_parm0
call1 visible
storeent tgt->ofs_parm0
call1 infront
and ofs_ret, ofs_ret -> temp <- here!!!
ifnot temp, jump over

qc functions store into an invisible global for their return value. The same one for every function.
Chances are, your 'infront' function call is clobbering the return value of 'visible'.
Solutions: use FrikQCC or a recent FTEQCC. Some older fteqcc builds have issues here, but the recent ones should be fine.

Or maybe I'm going crazy. I really have no idea any more.
Spike
 
Posts: 2892
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Postby Lardarse » Wed Mar 18, 2009 6:08 am

Spike wrote:Blame your qcc.

if ((visible (tgt)) && (infront (tgt)))

will expand into:

storeent tgt->ofs_parm0
call1 visible
storeent tgt->ofs_parm0
call1 infront
and ofs_ret, ofs_ret -> temp <- here!!!
ifnot temp, jump over

qc functions store into an invisible global for their return value. The same one for every function.
Chances are, your 'infront' function call is clobbering the return value of 'visible'.

I heard div0 discussing this issue about a year ago in #darkplaces. Easiest way to fix it that will work with all compilers is to split the line into 2 if statements. This is also possibly more efficient, as && and || don't have the short-circuiting behaviour in QuakeC that they do in C.

if(a && b) in C will not evaluate b if a is false, so it is better to put the one that is more likely to be false first. Also, if a might not be able to evaluate properly unless b is true, then it is better to put b first. || Is similar, but it stops evaluating after it finds something is true.

In QuakeC however, it would always evaluate a and b, so if one of them is much more likely to be false (or it is necessary to be true for the other expression to make sense) it should be placed as the first of 2 or more nested if statements. This also has the added benefit of the return values not clobbering each other. You may want to try and work out which is false more often - the profile command (prvm_profile server in DarkPlaces) may be helpful for this..
User avatar
Lardarse
 
Posts: 266
Joined: Sat Nov 05, 2005 1:58 pm
Location: Bristol, UK

Postby frag.machine » Wed Mar 18, 2009 10:55 pm

Spike wrote:Blame your qcc.

if ((visible (tgt)) && (infront (tgt)))

will expand into:

storeent tgt->ofs_parm0
call1 visible
storeent tgt->ofs_parm0
call1 infront
and ofs_ret, ofs_ret -> temp <- here!!!
ifnot temp, jump over

qc functions store into an invisible global for their return value. The same one for every function.
Chances are, your 'infront' function call is clobbering the return value of 'visible'.
Solutions: use FrikQCC or a recent FTEQCC. Some older fteqcc builds have issues here, but the recent ones should be fine.

Or maybe I'm going crazy. I really have no idea any more.


Err... I am using FrikQCC. Actually, it's the first time I give it a try, since proqccw used to be my default QC compiler. :/

But I'll check if your tip works, thanks!

EDIT: Yup, you got it. Splitting the test in two "if's" solved the bug. Many thanks again!
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
User avatar
frag.machine
 
Posts: 2090
Joined: Sat Nov 25, 2006 1:49 pm


Return to QuakeC Programming

Who is online

Users browsing this forum: No registered users and 1 guest