Пример #1
0
        /// <summary>
        /// SV_RunClients
        /// </summary>
        public static void RunClients()
        {
            for (int i = 0; i < svs.maxclients; i++)
            {
                Host.HostClient = svs.clients[i];
                if (!Host.HostClient.active)
                    continue;

                _Player = Host.HostClient.edict;

                if (!ReadClientMessage())
                {
                    DropClient(false);	// client misbehaved...
                    continue;
                }

                if (!Host.HostClient.spawned)
                {
                    // clear client movement until a new packet is received
                    Host.HostClient.cmd.Clear();
                    continue;
                }

                // always pause in single player if in console or menus
                if (!sv.paused && (svs.maxclients > 1 || Key.Destination == keydest_t.key_game))
                    ClientThink();
            }
        }
Пример #2
0
        /// <summary>
        /// SV_CheckBottom
        /// </summary>
        public static bool CheckBottom(edict_t ent)
        {
            v3f mins, maxs;
            Mathlib.VectorAdd(ref ent.v.origin, ref ent.v.mins, out mins);
            Mathlib.VectorAdd(ref ent.v.origin, ref ent.v.maxs, out maxs);

            // if all of the points under the corners are solid world, don't bother
            // with the tougher checks
            // the corners must be within 16 of the midpoint
            Vector3 start;
            start.Z = mins.z - 1;
            for (int x = 0; x <= 1; x++)
                for (int y = 0; y <= 1; y++)
                {
                    start.X = (x != 0 ? maxs.x : mins.x);
                    start.Y = (y != 0 ? maxs.y : mins.y);
                    if (PointContents(ref start) != Contents.CONTENTS_SOLID)
                        goto RealCheck;
                }

            return true;		// we got out easy

            RealCheck:

            //
            // check it for real...
            //
            start.Z = mins.z;

            // the midpoint must be within 16 of the bottom
            start.X = (mins.x + maxs.x) * 0.5f;
            start.Y = (mins.y + maxs.y) * 0.5f;
            Vector3 stop = start;
            stop.Z -= 2 * STEPSIZE;
            trace_t trace = Move(ref start, ref Common.ZeroVector, ref Common.ZeroVector, ref stop, 1, ent);

            if (trace.fraction == 1.0)
                return false;

            float mid = trace.endpos.Z;
            float bottom = mid;

            // the corners must be within 16 of the midpoint
            for (int x = 0; x <= 1; x++)
                for (int y = 0; y <= 1; y++)
                {
                    start.X = stop.X = (x != 0 ? maxs.x : mins.x);
                    start.Y = stop.Y = (y != 0 ? maxs.y : mins.y);

                    trace = Move(ref start, ref Common.ZeroVector, ref Common.ZeroVector, ref stop, 1, ent);

                    if (trace.fraction != 1.0 && trace.endpos.Z > bottom)
                        bottom = trace.endpos.Z;
                    if (trace.fraction == 1.0 || mid - trace.endpos.Z > STEPSIZE)
                        return false;
                }

            return true;
        }
Пример #3
0
        public bool startsolid; // if true, the initial point was in a solid area

        #endregion Fields

        #region Methods

        public void CopyFrom(trace_t src)
        {
            this.allsolid = src.allsolid;
            this.startsolid = src.startsolid;
            this.inopen = src.inopen;
            this.inwater = src.inwater;
            this.fraction = src.fraction;
            this.endpos = src.endpos;
            this.plane = src.plane;
            this.ent = src.ent;
        }
Пример #4
0
        /// <summary>
        /// SV_Impact
        /// Two entities have touched, so run their touch functions
        /// </summary>
        private static void Impact(edict_t e1, edict_t e2)
        {
            int old_self  = progs.GlobalStruct.self;
            int old_other = progs.GlobalStruct.other;

            progs.GlobalStruct.time = (float)sv.time;
            if (e1.v.touch != 0 && e1.v.solid != Solids.SOLID_NOT)
            {
                progs.GlobalStruct.self  = EdictToProg(e1);
                progs.GlobalStruct.other = EdictToProg(e2);
                progs.Execute(e1.v.touch);
            }

            if (e2.v.touch != 0 && e2.v.solid != Solids.SOLID_NOT)
            {
                progs.GlobalStruct.self  = EdictToProg(e2);
                progs.GlobalStruct.other = EdictToProg(e1);
                progs.Execute(e2.v.touch);
            }

            progs.GlobalStruct.self  = old_self;
            progs.GlobalStruct.other = old_other;
        }
Пример #5
0
        /*
         * =================
         * PF_findradius
         *
         * Returns a chain of entities that have origins within a spherical area
         *
         * findradius (origin, radius)
         * =================
         */
        static unsafe void PF_findradius()
        {
            edict_t chain = Server.sv.edicts[0];

            float *org = GetVector(OFS.OFS_PARM0);
            float  rad = GetFloat(OFS.OFS_PARM1);

            Vector3 vorg;

            Copy(org, out vorg);

            for (int i = 1; i < Server.sv.num_edicts; i++)
            {
                edict_t ent = Server.sv.edicts[i];
                if (ent.free)
                {
                    continue;
                }

                if (ent.v.solid == Solids.SOLID_NOT)
                {
                    continue;
                }

                Vector3 v = vorg - (Common.ToVector(ref ent.v.origin) +
                                    (Common.ToVector(ref ent.v.mins) + Common.ToVector(ref ent.v.maxs)) * 0.5f);
                if (v.Length > rad)
                {
                    continue;
                }

                ent.v.chain = Server.EdictToProg(chain);
                chain       = ent;
            }

            ReturnEdict(chain);
        }
Пример #6
0
        /// <summary>
        /// SV_Physics_Pusher
        /// </summary>
        static void Physics_Pusher(edict_t ent)
        {
            float oldltime  = ent.v.ltime;
            float thinktime = ent.v.nextthink;
            float movetime;

            if (thinktime < ent.v.ltime + Host.FrameTime)
            {
                movetime = thinktime - ent.v.ltime;
                if (movetime < 0)
                {
                    movetime = 0;
                }
            }
            else
            {
                movetime = (float)Host.FrameTime;
            }

            if (movetime != 0)
            {
                PushMove(ent, movetime);        // advances ent.v.ltime if not blocked
            }

            if (thinktime > oldltime && thinktime <= ent.v.ltime)
            {
                ent.v.nextthink          = 0;
                Progs.GlobalStruct.time  = (float)sv.time;
                Progs.GlobalStruct.self  = EdictToProg(ent);
                Progs.GlobalStruct.other = EdictToProg(sv.edicts[0]);
                Progs.Execute(ent.v.think);
                if (ent.free)
                {
                    return;
                }
            }
        }
Пример #7
0
        /// <summary>
        /// SV_RunThink
        /// Runs thinking code if time.  There is some play in the exact time the think
        /// function will be called, because it is called before any movement is done
        /// in a frame.  Not used for pushmove objects, because they must be exact.
        /// Returns false if the entity removed itself.
        /// </summary>
        static bool RunThink(edict_t ent)
        {
            float thinktime;

            thinktime = ent.v.nextthink;
            if (thinktime <= 0 || thinktime > sv.time + Host.FrameTime)
            {
                return(true);
            }

            if (thinktime < sv.time)
            {
                thinktime = (float)sv.time;     // don't let things stay in the past.
            }
            // it is possible to start that way
            // by a trigger with a local time.
            ent.v.nextthink          = 0;
            Progs.GlobalStruct.time  = thinktime;
            Progs.GlobalStruct.self  = EdictToProg(ent);
            Progs.GlobalStruct.other = EdictToProg(sv.edicts[0]);
            Progs.Execute(ent.v.think);

            return(!ent.free);
        }
Пример #8
0
        /// <summary>
        /// SV_MoveToGoal
        /// </summary>
        public static void MoveToGoal()
        {
            edict_t ent  = ProgToEdict(Progs.GlobalStruct.self);
            edict_t goal = ProgToEdict(ent.v.goalentity);
            float   dist = QBuiltins.GetFloat(OFS.OFS_PARM0);

            if (((int)ent.v.flags & (EdictFlags.FL_ONGROUND | EdictFlags.FL_FLY | EdictFlags.FL_SWIM)) == 0)
            {
                QBuiltins.ReturnFloat(0);
                return;
            }

            // if the next step hits the enemy, return immediately
            if (ProgToEdict(ent.v.enemy) != sv.edicts[0] && CloseEnough(ent, goal, dist))
            {
                return;
            }

            // bump around...
            if ((Sys.Random() & 3) == 1 || !StepDirection(ent, ent.v.ideal_yaw, dist))
            {
                NewChaseDir(ent, goal, dist);
            }
        }
Пример #9
0
        /// <summary>
        /// SV_LinkEdict
        ///
        /// Needs to be called any time an entity changes origin, mins, maxs, or solid
        /// flags ent->v.modified
        /// sets ent->v.absmin and ent->v.absmax
        /// if touchtriggers, calls prog functions for the intersected triggers
        /// </summary>
        public static void LinkEdict(edict_t ent, bool touch_triggers)
        {
            if (ent.area.Prev != null)
            {
                UnlinkEdict(ent);       // unlink from old position
            }
            if (ent == sv.edicts[0])
            {
                return;         // don't add the world
            }
            if (ent.free)
            {
                return;
            }

            // set the abs box
            Mathlib.VectorAdd(ref ent.v.origin, ref ent.v.mins, out ent.v.absmin);
            Mathlib.VectorAdd(ref ent.v.origin, ref ent.v.maxs, out ent.v.absmax);

            //
            // to make items easier to pick up and allow them to be grabbed off
            // of shelves, the abs sizes are expanded
            //
            if (((int)ent.v.flags & EdictFlags.FL_ITEM) != 0)
            {
                ent.v.absmin.x -= 15;
                ent.v.absmin.y -= 15;
                ent.v.absmax.x += 15;
                ent.v.absmax.y += 15;
            }
            else
            {   // because movement is clipped an epsilon away from an actual edge,
                // we must fully check even when bounding boxes don't quite touch
                ent.v.absmin.x -= 1;
                ent.v.absmin.y -= 1;
                ent.v.absmin.z -= 1;
                ent.v.absmax.x += 1;
                ent.v.absmax.y += 1;
                ent.v.absmax.z += 1;
            }

            // link to PVS leafs
            ent.num_leafs = 0;
            if (ent.v.modelindex != 0)
            {
                FindTouchedLeafs(ent, sv.worldmodel.nodes[0]);
            }

            if (ent.v.solid == Solids.SOLID_NOT)
            {
                return;
            }

            // find the first node that the ent's box crosses
            areanode_t node = _AreaNodes[0];

            while (true)
            {
                if (node.axis == -1)
                {
                    break;
                }
                if (Mathlib.Comp(ref ent.v.absmin, node.axis) > node.dist)
                {
                    node = node.children[0];
                }
                else if (Mathlib.Comp(ref ent.v.absmax, node.axis) < node.dist)
                {
                    node = node.children[1];
                }
                else
                {
                    break;              // crosses the node
                }
            }

            // link it in

            if (ent.v.solid == Solids.SOLID_TRIGGER)
            {
                ent.area.InsertBefore(node.trigger_edicts);
            }
            else
            {
                ent.area.InsertBefore(node.solid_edicts);
            }

            // if touch_triggers, touch all entities at this node and decend for more
            if (touch_triggers)
            {
                TouchLinks(ent, _AreaNodes[0]);
            }
        }
Пример #10
0
        /// <summary>
        /// SV_TouchLinks
        /// </summary>
        static void TouchLinks(edict_t ent, areanode_t node)
        {
            // touch linked edicts
            link_t next;
            for (link_t l = node.trigger_edicts.Next; l != node.trigger_edicts; l = next)
            {
                next = l.Next;
                edict_t touch = (edict_t)l.Owner;// EDICT_FROM_AREA(l);
                if (touch == ent)
                    continue;
                if (touch.v.touch == 0 || touch.v.solid != Solids.SOLID_TRIGGER)
                    continue;
                if (ent.v.absmin.x > touch.v.absmax.x || ent.v.absmin.y > touch.v.absmax.y ||
                    ent.v.absmin.z > touch.v.absmax.z || ent.v.absmax.x < touch.v.absmin.x ||
                    ent.v.absmax.y < touch.v.absmin.y || ent.v.absmax.z < touch.v.absmin.z)
                    continue;

                int old_self = Progs.GlobalStruct.self;
                int old_other = Progs.GlobalStruct.other;

                Progs.GlobalStruct.self = EdictToProg(touch);
                Progs.GlobalStruct.other = EdictToProg(ent);
                Progs.GlobalStruct.time = (float)sv.time;
                Progs.Execute(touch.v.touch);

                Progs.GlobalStruct.self = old_self;
                Progs.GlobalStruct.other = old_other;
            }

            // recurse down both sides
            if (node.axis == -1)
                return;

            if (Mathlib.Comp(ref ent.v.absmax, node.axis) > node.dist)
                TouchLinks(ent, node.children[0]);
            if (Mathlib.Comp(ref ent.v.absmin, node.axis) < node.dist)
                TouchLinks(ent, node.children[1]);
        }
Пример #11
0
        /// <summary>
        /// SV_HullForEntity
        /// Returns a hull that can be used for testing or clipping an object of mins/maxs size.
        /// Offset is filled in to contain the adjustment that must be added to the
        /// testing object's origin to get a point to use with the returned hull.
        /// </summary>
        static hull_t HullForEntity(edict_t ent, ref Vector3 mins, ref Vector3 maxs, out Vector3 offset)
        {
            hull_t hull = null;

            // decide which clipping hull to use, based on the size
            if (ent.v.solid == Solids.SOLID_BSP)
            {	// explicit hulls in the BSP model
                if (ent.v.movetype != Movetypes.MOVETYPE_PUSH)
                    Sys.Error("SOLID_BSP without MOVETYPE_PUSH");

                model_t model = sv.models[(int)ent.v.modelindex];

                if (model == null || model.type != modtype_t.mod_brush)
                    Sys.Error("MOVETYPE_PUSH with a non bsp model");

                Vector3 size = maxs - mins;
                if (size.X < 3)
                    hull = model.hulls[0];
                else if (size.X <= 32)
                    hull = model.hulls[1];
                else
                    hull = model.hulls[2];

                // calculate an offset value to center the origin
                offset = hull.clip_mins - mins;
                offset += Common.ToVector(ref ent.v.origin);
            }
            else
            {
                // create a temp hull from bounding box sizes
                Vector3 hullmins = Common.ToVector(ref ent.v.mins) - maxs;
                Vector3 hullmaxs = Common.ToVector(ref ent.v.maxs) - mins;
                hull = HullForBox(ref hullmins, ref hullmaxs);

                offset = Common.ToVector(ref ent.v.origin);
            }

            return hull;
        }
Пример #12
0
 static trace_t Move(ref v3f start, ref v3f mins, ref v3f maxs, ref v3f end, int type, edict_t passedict)
 {
     Vector3 vstart, vmins, vmaxs, vend;
     Mathlib.Copy(ref start, out vstart);
     Mathlib.Copy(ref mins, out vmins);
     Mathlib.Copy(ref maxs, out vmaxs);
     Mathlib.Copy(ref end, out vend);
     return Move(ref vstart, ref vmins, ref vmaxs, ref vend, type, passedict);
 }
Пример #13
0
 /// <summary>
 /// SV_Physics_None
 /// Non moving objects can only think
 /// </summary>
 static void Physics_None(edict_t ent)
 {
     // regular thinking
     RunThink(ent);
 }
Пример #14
0
        /// <summary>
        /// SV_PushMove
        /// </summary>
        static void PushMove(edict_t pusher, float movetime)
        {
            if (pusher.v.velocity.IsEmpty)
            {
                pusher.v.ltime += movetime;
                return;
            }

            v3f move, mins, maxs;

            Mathlib.VectorScale(ref pusher.v.velocity, movetime, out move);
            Mathlib.VectorAdd(ref pusher.v.absmin, ref move, out mins);
            Mathlib.VectorAdd(ref pusher.v.absmax, ref move, out maxs);

            v3f pushorig = pusher.v.origin;

            edict_t[] moved_edict = new edict_t[QDef.MAX_EDICTS];
            v3f[]     moved_from  = new v3f[QDef.MAX_EDICTS];

            // move the pusher to it's final position

            Mathlib.VectorAdd(ref pusher.v.origin, ref move, out pusher.v.origin);
            pusher.v.ltime += movetime;
            LinkEdict(pusher, false);


            // see if any solid entities are inside the final position
            int num_moved = 0;

            for (int e = 1; e < sv.num_edicts; e++)
            {
                edict_t check = sv.edicts[e];
                if (check.free)
                {
                    continue;
                }
                if (check.v.movetype == Movetypes.MOVETYPE_PUSH ||
                    check.v.movetype == Movetypes.MOVETYPE_NONE ||
                    check.v.movetype == Movetypes.MOVETYPE_NOCLIP)
                {
                    continue;
                }

                // if the entity is standing on the pusher, it will definately be moved
                if (!(((int)check.v.flags & EdictFlags.FL_ONGROUND) != 0 && ProgToEdict(check.v.groundentity) == pusher))
                {
                    if (check.v.absmin.x >= maxs.x || check.v.absmin.y >= maxs.y ||
                        check.v.absmin.z >= maxs.z || check.v.absmax.x <= mins.x ||
                        check.v.absmax.y <= mins.y || check.v.absmax.z <= mins.z)
                    {
                        continue;
                    }

                    // see if the ent's bbox is inside the pusher's final position
                    if (TestEntityPosition(check) == null)
                    {
                        continue;
                    }
                }

                // remove the onground flag for non-players
                if (check.v.movetype != Movetypes.MOVETYPE_WALK)
                {
                    check.v.flags = (int)check.v.flags & ~EdictFlags.FL_ONGROUND;
                }

                v3f entorig = check.v.origin;
                moved_from[num_moved]  = entorig;
                moved_edict[num_moved] = check;
                num_moved++;

                // try moving the contacted entity
                pusher.v.solid = Solids.SOLID_NOT;
                PushEntity(check, ref move);
                pusher.v.solid = Solids.SOLID_BSP;

                // if it is still inside the pusher, block
                edict_t block = TestEntityPosition(check);
                if (block != null)
                {
                    // fail the move
                    if (check.v.mins.x == check.v.maxs.x)
                    {
                        continue;
                    }
                    if (check.v.solid == Solids.SOLID_NOT || check.v.solid == Solids.SOLID_TRIGGER)
                    {
                        // corpse
                        check.v.mins.x = check.v.mins.y = 0;
                        check.v.maxs   = check.v.mins;
                        continue;
                    }

                    check.v.origin = entorig;
                    LinkEdict(check, true);

                    pusher.v.origin = pushorig;
                    LinkEdict(pusher, false);
                    pusher.v.ltime -= movetime;

                    // if the pusher has a "blocked" function, call it
                    // otherwise, just stay in place until the obstacle is gone
                    if (pusher.v.blocked != 0)
                    {
                        Progs.GlobalStruct.self  = EdictToProg(pusher);
                        Progs.GlobalStruct.other = EdictToProg(check);
                        Progs.Execute(pusher.v.blocked);
                    }

                    // move back any entities we already moved
                    for (int i = 0; i < num_moved; i++)
                    {
                        moved_edict[i].v.origin = moved_from[i];
                        LinkEdict(moved_edict[i], false);
                    }
                    return;
                }
            }
        }
Пример #15
0
        /// <summary>
        /// SV_StartSound
        /// Each entity can have eight independant sound sources, like voice,
        /// weapon, feet, etc.
        ///
        /// Channel 0 is an auto-allocate channel, the others override anything
        /// allready running on that entity/channel pair.
        ///
        /// An attenuation of 0 will play full volume everywhere in the level.
        /// Larger attenuations will drop off.  (max 4 attenuation)
        /// </summary>
        public static void StartSound(edict_t entity, int channel, string sample, int volume, float attenuation)
        {
            if (volume < 0 || volume > 255)
            {
                sys.Error("SV_StartSound: volume = {0}", volume);
            }

            if (attenuation < 0 || attenuation > 4)
            {
                sys.Error("SV_StartSound: attenuation = {0}", attenuation);
            }

            if (channel < 0 || channel > 7)
            {
                sys.Error("SV_StartSound: channel = {0}", channel);
            }

            if (sv.datagram.Length > QDef.MAX_DATAGRAM - 16)
            {
                return;
            }

            // find precache number for sound
            int sound_num;

            for (sound_num = 1; sound_num < QDef.MAX_SOUNDS && sv.sound_precache[sound_num] != null; sound_num++)
            {
                if (sample == sv.sound_precache[sound_num])
                {
                    break;
                }
            }

            if (sound_num == QDef.MAX_SOUNDS || String.IsNullOrEmpty(sv.sound_precache[sound_num]))
            {
                Con.Print("SV_StartSound: {0} not precacheed\n", sample);
                return;
            }

            int ent = NumForEdict(entity);

            channel = (ent << 3) | channel;

            int field_mask = 0;

            if (volume != snd.DEFAULT_SOUND_PACKET_VOLUME)
            {
                field_mask |= protocol.SND_VOLUME;
            }
            if (attenuation != snd.DEFAULT_SOUND_PACKET_ATTENUATION)
            {
                field_mask |= protocol.SND_ATTENUATION;
            }

            // directed messages go only to the entity the are targeted on
            sv.datagram.WriteByte(protocol.svc_sound);
            sv.datagram.WriteByte(field_mask);
            if ((field_mask & protocol.SND_VOLUME) != 0)
            {
                sv.datagram.WriteByte(volume);
            }
            if ((field_mask & protocol.SND_ATTENUATION) != 0)
            {
                sv.datagram.WriteByte((int)(attenuation * 64));
            }
            sv.datagram.WriteShort(channel);
            sv.datagram.WriteByte(sound_num);
            v3f v;

            mathlib.VectorAdd(ref entity.v.mins, ref entity.v.maxs, out v);
            mathlib.VectorMA(ref entity.v.origin, 0.5f, ref v, out v);
            sv.datagram.WriteCoord(v.x);
            sv.datagram.WriteCoord(v.y);
            sv.datagram.WriteCoord(v.z);
        }
Пример #16
0
        /// <summary>
        /// SV_RunThink
        /// Runs thinking code if time.  There is some play in the exact time the think
        /// function will be called, because it is called before any movement is done
        /// in a frame.  Not used for pushmove objects, because they must be exact.
        /// Returns false if the entity removed itself.
        /// </summary>
        static bool RunThink(edict_t ent)
        {
            float thinktime;

            thinktime = ent.v.nextthink;
            if (thinktime <= 0 || thinktime > sv.time + Host.FrameTime)
                return true;

            if (thinktime < sv.time)
                thinktime = (float)sv.time;	// don't let things stay in the past.

            // it is possible to start that way
            // by a trigger with a local time.
            ent.v.nextthink = 0;
            Progs.GlobalStruct.time = thinktime;
            Progs.GlobalStruct.self = EdictToProg(ent);
            Progs.GlobalStruct.other = EdictToProg(sv.edicts[0]);
            Progs.Execute(ent.v.think);

            return !ent.free;
        }
Пример #17
0
        /// <summary>
        /// SV_PushMove
        /// </summary>
        static void PushMove(edict_t pusher, float movetime)
        {
            if (pusher.v.velocity.IsEmpty)
            {
                pusher.v.ltime += movetime;
                return;
            }

            v3f move, mins, maxs;
            Mathlib.VectorScale(ref pusher.v.velocity, movetime, out move);
            Mathlib.VectorAdd(ref pusher.v.absmin, ref move, out mins);
            Mathlib.VectorAdd(ref pusher.v.absmax, ref move, out maxs);

            v3f pushorig = pusher.v.origin;

            edict_t[] moved_edict = new edict_t[QDef.MAX_EDICTS];
            v3f[] moved_from = new v3f[QDef.MAX_EDICTS];

            // move the pusher to it's final position

            Mathlib.VectorAdd(ref pusher.v.origin, ref move, out pusher.v.origin);
            pusher.v.ltime += movetime;
            LinkEdict(pusher, false);

            // see if any solid entities are inside the final position
            int num_moved = 0;
            for (int e = 1; e < sv.num_edicts; e++)
            {
                edict_t check = sv.edicts[e];
                if (check.free)
                    continue;
                if (check.v.movetype == Movetypes.MOVETYPE_PUSH ||
                    check.v.movetype == Movetypes.MOVETYPE_NONE ||
                    check.v.movetype == Movetypes.MOVETYPE_NOCLIP)
                    continue;

                // if the entity is standing on the pusher, it will definately be moved
                if (!(((int)check.v.flags & EdictFlags.FL_ONGROUND) != 0 && ProgToEdict(check.v.groundentity) == pusher))
                {
                    if (check.v.absmin.x >= maxs.x || check.v.absmin.y >= maxs.y ||
                        check.v.absmin.z >= maxs.z || check.v.absmax.x <= mins.x ||
                        check.v.absmax.y <= mins.y || check.v.absmax.z <= mins.z)
                        continue;

                    // see if the ent's bbox is inside the pusher's final position
                    if (TestEntityPosition(check) == null)
                        continue;
                }

                // remove the onground flag for non-players
                if (check.v.movetype != Movetypes.MOVETYPE_WALK)
                    check.v.flags = (int)check.v.flags & ~EdictFlags.FL_ONGROUND;

                v3f entorig = check.v.origin;
                moved_from[num_moved] = entorig;
                moved_edict[num_moved] = check;
                num_moved++;

                // try moving the contacted entity
                pusher.v.solid = Solids.SOLID_NOT;
                PushEntity(check, ref move);
                pusher.v.solid = Solids.SOLID_BSP;

                // if it is still inside the pusher, block
                edict_t block = TestEntityPosition(check);
                if (block != null)
                {
                    // fail the move
                    if (check.v.mins.x == check.v.maxs.x)
                        continue;
                    if (check.v.solid == Solids.SOLID_NOT || check.v.solid == Solids.SOLID_TRIGGER)
                    {
                        // corpse
                        check.v.mins.x = check.v.mins.y = 0;
                        check.v.maxs = check.v.mins;
                        continue;
                    }

                    check.v.origin = entorig;
                    LinkEdict(check, true);

                    pusher.v.origin = pushorig;
                    LinkEdict(pusher, false);
                    pusher.v.ltime -= movetime;

                    // if the pusher has a "blocked" function, call it
                    // otherwise, just stay in place until the obstacle is gone
                    if (pusher.v.blocked != 0)
                    {
                        Progs.GlobalStruct.self = EdictToProg(pusher);
                        Progs.GlobalStruct.other = EdictToProg(check);
                        Progs.Execute(pusher.v.blocked);
                    }

                    // move back any entities we already moved
                    for (int i = 0; i < num_moved; i++)
                    {
                        moved_edict[i].v.origin = moved_from[i];
                        LinkEdict(moved_edict[i], false);
                    }
                    return;
                }
            }
        }
Пример #18
0
        /// <summary>
        /// PushEntity
        /// Does not change the entities velocity at all
        /// </summary>
        private static trace_t PushEntity(edict_t ent, ref v3f push)
        {
            v3f end;
            Mathlib.VectorAdd(ref ent.v.origin, ref push, out end);

            trace_t trace;
            if (ent.v.movetype == Movetypes.MOVETYPE_FLYMISSILE)
                trace = Move(ref ent.v.origin, ref ent.v.mins, ref ent.v.maxs, ref end, MOVE_MISSILE, ent);
            else if (ent.v.solid == Solids.SOLID_TRIGGER || ent.v.solid == Solids.SOLID_NOT)
                // only clip against bmodels
                trace = Move(ref ent.v.origin, ref ent.v.mins, ref ent.v.maxs, ref end, MOVE_NOMONSTERS, ent);
            else
                trace = Move(ref ent.v.origin, ref ent.v.mins, ref ent.v.maxs, ref end, MOVE_NORMAL, ent);

            Mathlib.Copy(ref trace.endpos, out ent.v.origin);
            LinkEdict(ent, true);

            if (trace.ent != null)
                Impact(ent, trace.ent);

            return trace;
        }
Пример #19
0
        /// <summary>
        /// SV_Physics_Toss
        /// Toss, bounce, and fly movement.  When onground, do nothing.
        /// </summary>
        private static void Physics_Toss(edict_t ent)
        {
            // regular thinking
            if (!RunThink(ent))
                return;

            // if onground, return without moving
            if (((int)ent.v.flags & EdictFlags.FL_ONGROUND) != 0)
                return;

            CheckVelocity(ent);

            // add gravity
            if (ent.v.movetype != Movetypes.MOVETYPE_FLY && ent.v.movetype != Movetypes.MOVETYPE_FLYMISSILE)
                AddGravity(ent);

            // move angles
            Mathlib.VectorMA(ref ent.v.angles, (float)Host.FrameTime, ref ent.v.avelocity, out ent.v.angles);

            // move origin
            v3f move;
            Mathlib.VectorScale(ref ent.v.velocity, (float)Host.FrameTime, out move);
            trace_t trace = PushEntity(ent, ref move);

            if (trace.fraction == 1)
                return;
            if (ent.free)
                return;

            float backoff;
            if (ent.v.movetype == Movetypes. MOVETYPE_BOUNCE)
                backoff = 1.5f;
            else
                backoff = 1;

            ClipVelocity(ref ent.v.velocity, ref trace.plane.normal, out ent.v.velocity, backoff);

            // stop if on ground
            if (trace.plane.normal.Z > 0.7f)
            {
                if (ent.v.velocity.z < 60 || ent.v.movetype != Movetypes. MOVETYPE_BOUNCE)
                {
                    ent.v.flags = (int)ent.v.flags | EdictFlags.FL_ONGROUND;
                    ent.v.groundentity = EdictToProg(trace.ent);
                    ent.v.velocity = default(v3f);
                    ent.v.avelocity = default(v3f);
                }
            }

            // check for in water
            CheckWaterTransition(ent);
        }
Пример #20
0
        /// <summary>
        /// SV_Physics_Step
        /// </summary>
        static void Physics_Step(edict_t ent)
        {
            bool hitsound;

            // freefall if not onground
            if (((int)ent.v.flags & (EdictFlags.FL_ONGROUND | EdictFlags.FL_FLY | EdictFlags.FL_SWIM)) == 0)
            {
                if (ent.v.velocity.z < _Gravity.Value * -0.1)
                    hitsound = true;
                else
                    hitsound = false;

                AddGravity(ent);
                CheckVelocity(ent);
                FlyMove(ent, (float)Host.FrameTime, null);
                LinkEdict(ent, true);

                if (((int)ent.v.flags & EdictFlags.FL_ONGROUND) != 0)	// just hit ground
                {
                    if (hitsound)
                        StartSound(ent, 0, "demon/dland2.wav", 255, 1);
                }
            }

            // regular thinking
            RunThink(ent);

            CheckWaterTransition(ent);
        }
Пример #21
0
        /// <summary>
        /// SV_Physics_Pusher
        /// </summary>
        static void Physics_Pusher(edict_t ent)
        {
            float oldltime = ent.v.ltime;
            float thinktime = ent.v.nextthink;
            float movetime;
            if (thinktime < ent.v.ltime + Host.FrameTime)
            {
                movetime = thinktime - ent.v.ltime;
                if (movetime < 0)
                    movetime = 0;
            }
            else
                movetime = (float)Host.FrameTime;

            if (movetime != 0)
            {
                PushMove(ent, movetime);	// advances ent.v.ltime if not blocked
            }

            if (thinktime > oldltime && thinktime <= ent.v.ltime)
            {
                ent.v.nextthink = 0;
                Progs.GlobalStruct.time = (float)sv.time;
                Progs.GlobalStruct.self = EdictToProg(ent);
                Progs.GlobalStruct.other = EdictToProg(sv.edicts[0]);
                Progs.Execute(ent.v.think);
                if (ent.free)
                    return;
            }
        }
Пример #22
0
        /// <summary>
        /// SV_ClipToLinks
        /// Mins and maxs enclose the entire area swept by the move
        /// </summary>
        static void ClipToLinks(areanode_t node, moveclip_t clip)
        {
            link_t  next;
            trace_t trace;

            // touch linked edicts
            for (link_t l = node.solid_edicts.Next; l != node.solid_edicts; l = next)
            {
                next = l.Next;
                edict_t touch = (edict_t)l.Owner;// EDICT_FROM_AREA(l);
                if (touch.v.solid == Solids.SOLID_NOT)
                {
                    continue;
                }
                if (touch == clip.passedict)
                {
                    continue;
                }
                if (touch.v.solid == Solids.SOLID_TRIGGER)
                {
                    Sys.Error("Trigger in clipping list");
                }

                if (clip.type == MOVE_NOMONSTERS && touch.v.solid != Solids.SOLID_BSP)
                {
                    continue;
                }

                if (clip.boxmins.X > touch.v.absmax.x || clip.boxmins.Y > touch.v.absmax.y ||
                    clip.boxmins.Z > touch.v.absmax.z || clip.boxmaxs.X < touch.v.absmin.x ||
                    clip.boxmaxs.Y < touch.v.absmin.y || clip.boxmaxs.Z < touch.v.absmin.z)
                {
                    continue;
                }

                if (clip.passedict != null && clip.passedict.v.size.x != 0 && touch.v.size.x == 0)
                {
                    continue;   // points never interact
                }
                // might intersect, so do an exact clip
                if (clip.trace.allsolid)
                {
                    return;
                }
                if (clip.passedict != null)
                {
                    if (ProgToEdict(touch.v.owner) == clip.passedict)
                    {
                        continue;       // don't clip against own missiles
                    }
                    if (ProgToEdict(clip.passedict.v.owner) == touch)
                    {
                        continue;       // don't clip against owner
                    }
                }

                if (((int)touch.v.flags & EdictFlags.FL_MONSTER) != 0)
                {
                    trace = ClipMoveToEntity(touch, ref clip.start, ref clip.mins2, ref clip.maxs2, ref clip.end);
                }
                else
                {
                    trace = ClipMoveToEntity(touch, ref clip.start, ref clip.mins, ref clip.maxs, ref clip.end);
                }

                if (trace.allsolid || trace.startsolid || trace.fraction < clip.trace.fraction)
                {
                    trace.ent = touch;
                    if (clip.trace.startsolid)
                    {
                        clip.trace            = trace;
                        clip.trace.startsolid = true;
                    }
                    else
                    {
                        clip.trace = trace;
                    }
                }
                else if (trace.startsolid)
                {
                    clip.trace.startsolid = true;
                }
            }

            // recurse down both sides
            if (node.axis == -1)
            {
                return;
            }

            if (Mathlib.Comp(ref clip.boxmaxs, node.axis) > node.dist)
            {
                ClipToLinks(node.children[0], clip);
            }
            if (Mathlib.Comp(ref clip.boxmins, node.axis) < node.dist)
            {
                ClipToLinks(node.children[1], clip);
            }
        }
Пример #23
0
        /// <summary>
        /// ED_ParseEdict
        /// Parses an edict out of the given string, returning the new position
        /// ed should be a properly initialized empty edict.
        /// Used for initial level load and for savegames.
        /// </summary>
        public static string ParseEdict(string data, edict_t ent)
        {
            bool init = false;

            // clear it
            if (ent != Server.sv.edicts[0]) // hack
            {
                ent.Clear();
            }

            // go through all the dictionary pairs
            bool anglehack;

            while (true)
            {
                // parse key
                data = Common.Parse(data);
                if (Common.Token.StartsWith("}"))
                {
                    break;
                }

                if (data == null)
                {
                    Sys.Error("ED_ParseEntity: EOF without closing brace");
                }

                string token = Common.Token;

                // anglehack is to allow QuakeEd to write single scalar angles
                // and allow them to be turned into vectors. (FIXME...)
                if (token == "angle")
                {
                    token     = "angles";
                    anglehack = true;
                }
                else
                {
                    anglehack = false;
                }

                // FIXME: change light to _light to get rid of this hack
                if (token == "light")
                {
                    token = "light_lev";   // hack for single light def
                }

                string keyname = token.TrimEnd();

                // parse value
                data = Common.Parse(data);
                if (data == null)
                {
                    Sys.Error("ED_ParseEntity: EOF without closing brace");
                }

                if (Common.Token.StartsWith("}"))
                {
                    Sys.Error("ED_ParseEntity: closing brace without data");
                }

                init = true;

                // keynames with a leading underscore are used for utility comments,
                // and are immediately discarded by quake
                if (keyname[0] == '_')
                {
                    continue;
                }

                ddef_t key = FindField(keyname);
                if (key == null)
                {
                    Con.Print("'{0}' is not a field\n", keyname);
                    continue;
                }

                token = Common.Token;
                if (anglehack)
                {
                    token = "0 " + token + " 0";
                }

                if (!ParsePair(ent, key, token))
                {
                    Host.Error("ED_ParseEdict: parse error");
                }
            }

            if (!init)
            {
                ent.free = true;
            }

            return(data);
        }
Пример #24
0
        /// <summary>
        /// SV_TryUnstick
        /// Player has come to a dead stop, possibly due to the problem with limited
        /// float precision at some angle joins in the BSP hull.
        ///
        /// Try fixing by pushing one pixel in each direction.
        ///
        /// This is a hack, but in the interest of good gameplay...
        /// </summary>
        static int TryUnstick(edict_t ent, ref v3f oldvel)
        {
            v3f oldorg = ent.v.origin;
            v3f dir = Common.ZeroVector3f;

            trace_t steptrace = new trace_t();
            for (int i = 0; i < 8; i++)
            {
                // try pushing a little in an axial direction
                switch (i)
                {
                    case 0: dir.x = 2; dir.y = 0; break;
                    case 1: dir.x = 0; dir.y = 2; break;
                    case 2: dir.x = -2; dir.y = 0; break;
                    case 3: dir.x = 0; dir.y = -2; break;
                    case 4: dir.x = 2; dir.y = 2; break;
                    case 5: dir.x = -2; dir.y = 2; break;
                    case 6: dir.x = 2; dir.y = -2; break;
                    case 7: dir.x = -2; dir.y = -2; break;
                }

                PushEntity(ent, ref dir);

                // retry the original move
                ent.v.velocity.x = oldvel.x;
                ent.v.velocity.y = oldvel.y;
                ent.v.velocity.z = 0;
                int clip = FlyMove(ent, 0.1f, steptrace);

                if (Math.Abs(oldorg.y - ent.v.origin.y) > 4 || Math.Abs(oldorg.x - ent.v.origin.x) > 4)
                {
                    return clip;
                }

                // go back to the original pos and try again
                ent.v.origin = oldorg;
            }

            ent.v.velocity = Common.ZeroVector3f;
            return 7;		// still not moving
        }
Пример #25
0
        /// <summary>
        /// SV_WriteEntitiesToClient
        /// </summary>
        private static void WriteEntitiesToClient(edict_t clent, MsgWriter msg)
        {
            // find the client's PVS
            Vector3 org = common.ToVector(ref clent.v.origin) + common.ToVector(ref clent.v.view_ofs);

            byte[] pvs = FatPVS(ref org);

            // send over all entities (except the client) that touch the pvs
            for (int e = 1; e < sv.num_edicts; e++)
            {
                edict_t ent = sv.edicts[e];
                // ignore if not touching a PV leaf
                if (ent != clent)       // clent is ALLWAYS sent
                {
                    // ignore ents without visible models
                    string mname = progs.GetString(ent.v.model);
                    if (String.IsNullOrEmpty(mname))
                    {
                        continue;
                    }

                    int i;
                    for (i = 0; i < ent.num_leafs; i++)
                    {
                        if ((pvs[ent.leafnums[i] >> 3] & (1 << (ent.leafnums[i] & 7))) != 0)
                        {
                            break;
                        }
                    }

                    if (i == ent.num_leafs)
                    {
                        continue;               // not visible
                    }
                }

                if (msg.Capacity - msg.Length < 16)
                {
                    Con.Print("packet overflow\n");
                    return;
                }

                // send an update
                int bits = 0;
                v3f miss;
                mathlib.VectorSubtract(ref ent.v.origin, ref ent.baseline.origin, out miss);
                if (miss.x < -0.1f || miss.x > 0.1f)
                {
                    bits |= protocol.U_ORIGIN1;
                }
                if (miss.y < -0.1f || miss.y > 0.1f)
                {
                    bits |= protocol.U_ORIGIN2;
                }
                if (miss.z < -0.1f || miss.z > 0.1f)
                {
                    bits |= protocol.U_ORIGIN3;
                }

                if (ent.v.angles.x != ent.baseline.angles.x)
                {
                    bits |= protocol.U_ANGLE1;
                }

                if (ent.v.angles.y != ent.baseline.angles.y)
                {
                    bits |= protocol.U_ANGLE2;
                }

                if (ent.v.angles.z != ent.baseline.angles.z)
                {
                    bits |= protocol.U_ANGLE3;
                }

                if (ent.v.movetype == Movetypes.MOVETYPE_STEP)
                {
                    bits |= protocol.U_NOLERP;  // don't mess up the step animation
                }
                if (ent.baseline.colormap != ent.v.colormap)
                {
                    bits |= protocol.U_COLORMAP;
                }

                if (ent.baseline.skin != ent.v.skin)
                {
                    bits |= protocol.U_SKIN;
                }

                if (ent.baseline.frame != ent.v.frame)
                {
                    bits |= protocol.U_FRAME;
                }

                if (ent.baseline.effects != ent.v.effects)
                {
                    bits |= protocol.U_EFFECTS;
                }

                if (ent.baseline.modelindex != ent.v.modelindex)
                {
                    bits |= protocol.U_MODEL;
                }

                if (e >= 256)
                {
                    bits |= protocol.U_LONGENTITY;
                }

                if (bits >= 256)
                {
                    bits |= protocol.U_MOREBITS;
                }

                //
                // write the message
                //
                msg.WriteByte(bits | protocol.U_SIGNAL);

                if ((bits & protocol.U_MOREBITS) != 0)
                {
                    msg.WriteByte(bits >> 8);
                }
                if ((bits & protocol.U_LONGENTITY) != 0)
                {
                    msg.WriteShort(e);
                }
                else
                {
                    msg.WriteByte(e);
                }

                if ((bits & protocol.U_MODEL) != 0)
                {
                    msg.WriteByte((int)ent.v.modelindex);
                }
                if ((bits & protocol.U_FRAME) != 0)
                {
                    msg.WriteByte((int)ent.v.frame);
                }
                if ((bits & protocol.U_COLORMAP) != 0)
                {
                    msg.WriteByte((int)ent.v.colormap);
                }
                if ((bits & protocol.U_SKIN) != 0)
                {
                    msg.WriteByte((int)ent.v.skin);
                }
                if ((bits & protocol.U_EFFECTS) != 0)
                {
                    msg.WriteByte((int)ent.v.effects);
                }
                if ((bits & protocol.U_ORIGIN1) != 0)
                {
                    msg.WriteCoord(ent.v.origin.x);
                }
                if ((bits & protocol.U_ANGLE1) != 0)
                {
                    msg.WriteAngle(ent.v.angles.x);
                }
                if ((bits & protocol.U_ORIGIN2) != 0)
                {
                    msg.WriteCoord(ent.v.origin.y);
                }
                if ((bits & protocol.U_ANGLE2) != 0)
                {
                    msg.WriteAngle(ent.v.angles.y);
                }
                if ((bits & protocol.U_ORIGIN3) != 0)
                {
                    msg.WriteCoord(ent.v.origin.z);
                }
                if ((bits & protocol.U_ANGLE3) != 0)
                {
                    msg.WriteAngle(ent.v.angles.z);
                }
            }
        }
Пример #26
0
        /// <summary>
        /// SV_WalkMove
        /// Only used by players
        /// </summary>
        static void WalkMove(edict_t ent)
        {
            //
            // do a regular slide move unless it looks like you ran into a step
            //
            int oldonground = (int)ent.v.flags & EdictFlags.FL_ONGROUND;
            ent.v.flags = (int)ent.v.flags & ~EdictFlags.FL_ONGROUND;

            v3f oldorg = ent.v.origin;
            v3f oldvel = ent.v.velocity;
            trace_t steptrace = new trace_t();
            int clip = FlyMove(ent, (float)Host.FrameTime, steptrace);

            if ((clip & 2) == 0)
                return;		// move didn't block on a step

            if (oldonground == 0 && ent.v.waterlevel == 0)
                return;		// don't stair up while jumping

            if (ent.v.movetype != Movetypes.MOVETYPE_WALK)
                return;		// gibbed by a trigger

            if (_NoStep.Value != 0)
                return;

            if (((int)_Player.v.flags & EdictFlags.FL_WATERJUMP) != 0)
                return;

            v3f nosteporg = ent.v.origin;
            v3f nostepvel = ent.v.velocity;

            //
            // try moving up and forward to go up a step
            //
            ent.v.origin = oldorg;	// back to start pos

            v3f upmove = Common.ZeroVector3f;
            v3f downmove = upmove;
            upmove.z = STEPSIZE;
            downmove.z = (float)(-STEPSIZE + oldvel.z * Host.FrameTime);

            // move up
            PushEntity(ent, ref upmove);	// FIXME: don't link?

            // move forward
            ent.v.velocity.x = oldvel.x;
            ent.v.velocity.y = oldvel.y;
            ent.v.velocity.z = 0;
            clip = FlyMove(ent, (float)Host.FrameTime, steptrace);

            // check for stuckness, possibly due to the limited precision of floats
            // in the clipping hulls
            if (clip != 0)
            {
                if (Math.Abs(oldorg.y - ent.v.origin.y) < 0.03125 && Math.Abs(oldorg.x - ent.v.origin.x) < 0.03125)
                {
                    // stepping up didn't make any progress
                    clip = TryUnstick(ent, ref oldvel);
                }
            }

            // extra friction based on view angle
            if ((clip & 2) != 0)
                WallFriction(ent, steptrace);

            // move down
            trace_t downtrace = PushEntity(ent, ref downmove);	// FIXME: don't link?

            if (downtrace.plane.normal.Z > 0.7)
            {
                if (ent.v.solid == Solids.SOLID_BSP)
                {
                    ent.v.flags = (int)ent.v.flags | EdictFlags.FL_ONGROUND;
                    ent.v.groundentity = EdictToProg(downtrace.ent);
                }
            }
            else
            {
                // if the push down didn't end up on good ground, use the move without
                // the step up.  This happens near wall / slope combinations, and can
                // cause the player to hop up higher on a slope too steep to climb
                ent.v.origin = nosteporg;
                ent.v.velocity = nostepvel;
            }
        }
Пример #27
0
 /// <summary>
 /// SV_Physics_None
 /// Non moving objects can only think
 /// </summary>
 static void Physics_None(edict_t ent)
 {
     // regular thinking
     RunThink(ent);
 }
Пример #28
0
        /// <summary>
        /// SV_WallFriction
        /// </summary>
        static void WallFriction(edict_t ent, trace_t trace)
        {
            Vector3 forward, right, up, vangle = Common.ToVector(ref ent.v.v_angle);
            Mathlib.AngleVectors(ref vangle, out forward, out right, out up);
            float d = Vector3.Dot(trace.plane.normal, forward);

            d += 0.5f;
            if (d >= 0)
                return;

            // cut the tangential velocity
            Vector3 vel = Common.ToVector(ref ent.v.velocity);
            float i = Vector3.Dot(trace.plane.normal, vel);
            Vector3 into = trace.plane.normal * i;
            Vector3 side = vel - into;

            ent.v.velocity.x = side.X * (1 + d);
            ent.v.velocity.y = side.Y * (1 + d);
        }
Пример #29
0
        /// <summary>
        /// ED_Free
        /// Marks the edict as free
        /// FIXME: walk all entities and NULL out references to this entity
        /// </summary>
        public static void FreeEdict(edict_t ed)
        {
            UnlinkEdict(ed);		// unlink from world bsp

            ed.free = true;
            ed.v.model = 0;
            ed.v.takedamage = 0;
            ed.v.modelindex = 0;
            ed.v.colormap = 0;
            ed.v.skin = 0;
            ed.v.frame = 0;
            ed.v.origin = default(v3f);
            ed.v.angles = default(v3f);
            ed.v.nextthink = -1;
            ed.v.solid = 0;

            ed.freetime = (float)sv.time;
        }
Пример #30
0
 /// <summary>
 /// SV_FixCheckBottom
 /// </summary>
 static void FixCheckBottom(edict_t ent)
 {
     ent.v.flags = (int)ent.v.flags | EdictFlags.FL_PARTIALGROUND;
 }
Пример #31
0
        /// <summary>
        /// SV_FindTouchedLeafs
        /// </summary>
        static void FindTouchedLeafs(edict_t ent, mnodebase_t node)
        {
            if (node.contents == Contents.CONTENTS_SOLID)
                return;

            // add an efrag if the node is a leaf

            if (node.contents < 0)
            {
                if (ent.num_leafs == Progs.MAX_ENT_LEAFS)
                    return;

                mleaf_t leaf = (mleaf_t)node;
                int leafnum = Array.IndexOf(sv.worldmodel.leafs, leaf) - 1;

                ent.leafnums[ent.num_leafs] = (short)leafnum;
                ent.num_leafs++;
                return;
            }

            // NODE_MIXED
            mnode_t n = (mnode_t)node;
            mplane_t splitplane = n.plane;
            int sides = Mathlib.BoxOnPlaneSide(ref ent.v.absmin, ref ent.v.absmax, splitplane);

            // recurse down the contacted sides
            if ((sides & 1) != 0)
                FindTouchedLeafs(ent, n.children[0]);

            if ((sides & 2) != 0)
                FindTouchedLeafs(ent, n.children[1]);
        }
Пример #32
0
        /// <summary>
        /// SV_Physics_Client
        /// Player character actions
        /// </summary>
        static void Physics_Client(edict_t ent, int num)
        {
            if (!svs.clients[num - 1].active)
                return;		// unconnected slot

            //
            // call standard client pre-think
            //
            Progs.GlobalStruct.time = (float)sv.time;
            Progs.GlobalStruct.self = EdictToProg(ent);
            Progs.Execute(Progs.GlobalStruct.PlayerPreThink);

            //
            // do a move
            //
            CheckVelocity(ent);

            //
            // decide which move function to call
            //
            switch ((int)ent.v.movetype)
            {
                case Movetypes.MOVETYPE_NONE:
                    if (!RunThink(ent))
                        return;
                    break;

                case Movetypes.MOVETYPE_WALK:
                    if (!RunThink(ent))
                        return;
                    if (!CheckWater(ent) && ((int)ent.v.flags & EdictFlags.FL_WATERJUMP) == 0)
                        AddGravity(ent);
                    CheckStuck(ent);

                    WalkMove(ent);
                    break;

                case Movetypes.MOVETYPE_TOSS:
                case Movetypes.MOVETYPE_BOUNCE:
                    Physics_Toss(ent);
                    break;

                case Movetypes.MOVETYPE_FLY:
                    if (!RunThink(ent))
                        return;
                    FlyMove(ent, (float)Host.FrameTime, null);
                    break;

                case Movetypes.MOVETYPE_NOCLIP:
                    if (!RunThink(ent))
                        return;
                    Mathlib.VectorMA(ref ent.v.origin, (float)Host.FrameTime, ref ent.v.velocity, out ent.v.origin);
                    break;

                default:
                    Sys.Error("SV_Physics_client: bad movetype {0}", (int)ent.v.movetype);
                    break;
            }

            //
            // call standard player post-think
            //
            LinkEdict(ent, true);

            Progs.GlobalStruct.time = (float)sv.time;
            Progs.GlobalStruct.self = EdictToProg(ent);
            Progs.Execute(Progs.GlobalStruct.PlayerPostThink);
        }
Пример #33
0
        /// <summary>
        /// SV_TestEntityPosition
        /// This could be a lot more efficient...
        /// </summary>
        static edict_t TestEntityPosition(edict_t ent)
        {
            trace_t trace = Move(ref ent.v.origin, ref ent.v.mins, ref ent.v.maxs, ref ent.v.origin, 0, ent);

            if (trace.startsolid)
                return sv.edicts[0];

            return null;
        }
Пример #34
0
        static void PF_Spawn()
        {
            edict_t ed = Server.AllocEdict();

            ReturnEdict(ed);
        }
Пример #35
0
        /// <summary>
        /// SV_LinkEdict
        /// 
        /// Needs to be called any time an entity changes origin, mins, maxs, or solid
        /// flags ent->v.modified
        /// sets ent->v.absmin and ent->v.absmax
        /// if touchtriggers, calls prog functions for the intersected triggers
        /// </summary>
        public static void LinkEdict(edict_t ent, bool touch_triggers)
        {
            if (ent.area.Prev != null)
                UnlinkEdict(ent);	// unlink from old position

            if (ent == sv.edicts[0])
                return;		// don't add the world

            if (ent.free)
                return;

            // set the abs box
            Mathlib.VectorAdd(ref ent.v.origin, ref ent.v.mins, out ent.v.absmin);
            Mathlib.VectorAdd(ref ent.v.origin, ref ent.v.maxs, out ent.v.absmax);

            //
            // to make items easier to pick up and allow them to be grabbed off
            // of shelves, the abs sizes are expanded
            //
            if (((int)ent.v.flags & EdictFlags.FL_ITEM) != 0)
            {
                ent.v.absmin.x -= 15;
                ent.v.absmin.y -= 15;
                ent.v.absmax.x += 15;
                ent.v.absmax.y += 15;
            }
            else
            {	// because movement is clipped an epsilon away from an actual edge,
                // we must fully check even when bounding boxes don't quite touch
                ent.v.absmin.x -= 1;
                ent.v.absmin.y -= 1;
                ent.v.absmin.z -= 1;
                ent.v.absmax.x += 1;
                ent.v.absmax.y += 1;
                ent.v.absmax.z += 1;
            }

            // link to PVS leafs
            ent.num_leafs = 0;
            if (ent.v.modelindex != 0)
                FindTouchedLeafs(ent, sv.worldmodel.nodes[0]);

            if (ent.v.solid == Solids.SOLID_NOT)
                return;

            // find the first node that the ent's box crosses
            areanode_t node = _AreaNodes[0];
            while (true)
            {
                if (node.axis == -1)
                    break;
                if (Mathlib.Comp(ref ent.v.absmin, node.axis) > node.dist)
                    node = node.children[0];
                else if (Mathlib.Comp(ref ent.v.absmax, node.axis) < node.dist)
                    node = node.children[1];
                else
                    break;		// crosses the node
            }

            // link it in

            if (ent.v.solid == Solids.SOLID_TRIGGER)
                ent.area.InsertBefore(node.trigger_edicts);
            else
                ent.area.InsertBefore(node.solid_edicts);

            // if touch_triggers, touch all entities at this node and decend for more
            if (touch_triggers)
                TouchLinks(ent, _AreaNodes[0]);
        }
Пример #36
0
        static void PF_Remove()
        {
            edict_t ed = GetEdict(OFS.OFS_PARM0);

            Server.FreeEdict(ed);
        }
Пример #37
0
        /// <summary>
        /// SV_Move
        /// mins and maxs are relative
        /// if the entire move stays in a solid volume, trace.allsolid will be set
        /// if the starting point is in a solid, it will be allowed to move out to an open area
        /// nomonsters is used for line of sight or edge testing, where mosnters
        /// shouldn't be considered solid objects
        /// passedict is explicitly excluded from clipping checks (normally NULL)
        /// </summary>
        public static trace_t Move(ref Vector3 start, ref Vector3 mins, ref Vector3 maxs, ref Vector3 end, int type, edict_t passedict)
        {
            moveclip_t clip = new moveclip_t();

            // clip to world
            clip.trace = ClipMoveToEntity(sv.edicts[0], ref start, ref mins, ref maxs, ref end);

            clip.start     = start;
            clip.end       = end;
            clip.mins      = mins;
            clip.maxs      = maxs;
            clip.type      = type;
            clip.passedict = passedict;

            if (type == MOVE_MISSILE)
            {
                clip.mins2 = Vector3.One * -15;
                clip.maxs2 = Vector3.One * 15;
            }
            else
            {
                clip.mins2 = mins;
                clip.maxs2 = maxs;
            }

            // create the bounding box of the entire move
            MoveBounds(ref start, ref clip.mins2, ref clip.maxs2, ref end, out clip.boxmins, out clip.boxmaxs);

            // clip to entities
            ClipToLinks(_AreaNodes[0], clip);

            return(clip.trace);
        }
Пример #38
0
        /// <summary>
        /// PF_checkbottom
        /// </summary>
        static void PF_checkbottom()
        {
            edict_t ent = GetEdict(OFS.OFS_PARM0);

            ReturnFloat(Server.CheckBottom(ent) ? 1 : 0);
        }
Пример #39
0
        /// <summary>
        /// ED_LoadFromFile
        /// The entities are directly placed in the array, rather than allocated with
        /// ED_Alloc, because otherwise an error loading the map would have entity
        /// number references out of order.
        ///
        /// Creates a server's entity / program execution context by
        /// parsing textual entity definitions out of an ent file.
        ///
        /// Used for both fresh maps and savegame loads.  A fresh map would also need
        /// to call ED_CallSpawnFunctions () to let the objects initialize themselves.
        /// </summary>
        public static void LoadFromFile(string data)
        {
            edict_t ent     = null;
            int     inhibit = 0;

            Progs.GlobalStruct.time = (float)Server.sv.time;

            // parse ents
            while (true)
            {
                // parse the opening brace
                data = Common.Parse(data);
                if (data == null)
                {
                    break;
                }

                if (Common.Token != "{")
                {
                    Sys.Error("ED_LoadFromFile: found {0} when expecting {", Common.Token);
                }

                if (ent == null)
                {
                    ent = Server.EdictNum(0);
                }
                else
                {
                    ent = Server.AllocEdict();
                }

                data = ParseEdict(data, ent);

                // remove things from different skill levels or deathmatch
                if (Host.Deathmatch != 0)
                {
                    if (((int)ent.v.spawnflags & SpawnFlags.SPAWNFLAG_NOT_DEATHMATCH) != 0)
                    {
                        Server.FreeEdict(ent);
                        inhibit++;
                        continue;
                    }
                }
                else if ((Host.CurrentSkill == 0 && ((int)ent.v.spawnflags & SpawnFlags.SPAWNFLAG_NOT_EASY) != 0) ||
                         (Host.CurrentSkill == 1 && ((int)ent.v.spawnflags & SpawnFlags.SPAWNFLAG_NOT_MEDIUM) != 0) ||
                         (Host.CurrentSkill >= 2 && ((int)ent.v.spawnflags & SpawnFlags.SPAWNFLAG_NOT_HARD) != 0))
                {
                    Server.FreeEdict(ent);
                    inhibit++;
                    continue;
                }

                //
                // immediately call spawn function
                //
                if (ent.v.classname == 0)
                {
                    Con.Print("No classname for:\n");
                    Print(ent);
                    Server.FreeEdict(ent);
                    continue;
                }

                // look for the spawn function
                int func = IndexOfFunction(GetString(ent.v.classname));
                if (func == -1)
                {
                    Con.Print("No spawn function for:\n");
                    Print(ent);
                    Server.FreeEdict(ent);
                    continue;
                }

                Progs.GlobalStruct.self = Server.EdictToProg(ent);
                Execute(func);
            }

            Con.DPrint("{0} entities inhibited\n", inhibit);
        }
Пример #40
0
        /*
         * =============
         * PF_aim
         *
         * Pick a vector for the player to shoot along
         * vector aim(entity, missilespeed)
         * =============
         */
        static void PF_aim()
        {
            edict_t ent   = GetEdict(OFS.OFS_PARM0);
            float   speed = GetFloat(OFS.OFS_PARM1);

            Vector3 start = Common.ToVector(ref ent.v.origin);

            start.Z += 20;

            // try sending a trace straight
            Vector3 dir;

            Mathlib.Copy(ref Progs.GlobalStruct.v_forward, out dir);
            Vector3 end = start + dir * 2048;
            trace_t tr  = Server.Move(ref start, ref Common.ZeroVector, ref Common.ZeroVector, ref end, 0, ent);

            if (tr.ent != null && tr.ent.v.takedamage == Damages.DAMAGE_AIM &&
                (Host.TeamPlay == 0 || ent.v.team <= 0 || ent.v.team != tr.ent.v.team))
            {
                ReturnVector(ref Progs.GlobalStruct.v_forward);
                return;
            }

            // try all possible entities
            Vector3 bestdir  = dir;
            float   bestdist = Server.Aim;
            edict_t bestent  = null;

            for (int i = 1; i < Server.sv.num_edicts; i++)
            {
                edict_t check = Server.sv.edicts[i];
                if (check.v.takedamage != Damages.DAMAGE_AIM)
                {
                    continue;
                }

                if (check == ent)
                {
                    continue;
                }

                if (Host.TeamPlay != 0 && ent.v.team > 0 && ent.v.team == check.v.team)
                {
                    continue;   // don't aim at teammate
                }

                v3f tmp;
                Mathlib.VectorAdd(ref check.v.mins, ref check.v.maxs, out tmp);
                Mathlib.VectorMA(ref check.v.origin, 0.5f, ref tmp, out tmp);
                Mathlib.Copy(ref tmp, out end);

                dir = end - start;
                Mathlib.Normalize(ref dir);
                float dist = Vector3.Dot(dir, Common.ToVector(ref Progs.GlobalStruct.v_forward));
                if (dist < bestdist)
                {
                    continue;   // to far to turn
                }

                tr = Server.Move(ref start, ref Common.ZeroVector, ref Common.ZeroVector, ref end, 0, ent);
                if (tr.ent == check)
                {       // can shoot at this one
                    bestdist = dist;
                    bestent  = check;
                }
            }

            if (bestent != null)
            {
                v3f dir2, end2;
                Mathlib.VectorSubtract(ref bestent.v.origin, ref ent.v.origin, out dir2);
                float dist = Mathlib.DotProduct(ref dir2, ref Progs.GlobalStruct.v_forward);
                Mathlib.VectorScale(ref Progs.GlobalStruct.v_forward, dist, out end2);
                end2.z = dir2.z;
                Mathlib.Normalize(ref end2);
                ReturnVector(ref end2);
            }
            else
            {
                ReturnVector(ref bestdir);
            }
        }
Пример #41
0
        /// <summary>
        /// Host_Loadgame_f
        /// </summary>
        private static void Loadgame_f()
        {
            if (Cmd.Source != cmd_source_t.src_command)
            {
                return;
            }

            if (Cmd.Argc != 2)
            {
                Con.Print("load <savename> : load a game\n");
                return;
            }

            Client.cls.demonum = -1;            // stop demo loop in case this fails

            string name = Path.ChangeExtension(Path.Combine(Common.GameDir, Cmd.Argv(1)), ".sav");

            // we can't call SCR_BeginLoadingPlaque, because too much stack space has
            // been used.  The menu calls it before stuffing loadgame command
            //	SCR_BeginLoadingPlaque ();

            Con.Print("Loading game from {0}...\n", name);
            FileStream fs = Sys.FileOpenRead(name);

            if (fs == null)
            {
                Con.Print("ERROR: couldn't open.\n");
                return;
            }

            using (StreamReader reader = new StreamReader(fs, Encoding.ASCII))
            {
                string line    = reader.ReadLine();
                int    version = Common.atoi(line);
                if (version != SAVEGAME_VERSION)
                {
                    Con.Print("Savegame is version {0}, not {1}\n", version, SAVEGAME_VERSION);
                    return;
                }
                line = reader.ReadLine();

                float[] spawn_parms = new float[Server.NUM_SPAWN_PARMS];
                for (int i = 0; i < spawn_parms.Length; i++)
                {
                    line           = reader.ReadLine();
                    spawn_parms[i] = Common.atof(line);
                }
                // this silliness is so we can load 1.06 save files, which have float skill values
                line = reader.ReadLine();
                float tfloat = Common.atof(line);
                Host.CurrentSkill = (int)(tfloat + 0.1);
                Cvar.Set("skill", (float)Host.CurrentSkill);

                string mapname = reader.ReadLine();
                line = reader.ReadLine();
                float time = Common.atof(line);

                Client.Disconnect_f();
                Server.SpawnServer(mapname);

                if (!Server.sv.active)
                {
                    Con.Print("Couldn't load map\n");
                    return;
                }
                Server.sv.paused   = true;              // pause until all clients connect
                Server.sv.loadgame = true;

                // load the light styles

                for (int i = 0; i < QDef.MAX_LIGHTSTYLES; i++)
                {
                    line = reader.ReadLine();
                    Server.sv.lightstyles[i] = line;
                }

                // load the edicts out of the savegame file
                int           entnum = -1;      // -1 is the globals
                StringBuilder sb     = new StringBuilder(32768);
                while (!reader.EndOfStream)
                {
                    line = reader.ReadLine();
                    if (line == null)
                    {
                        Sys.Error("EOF without closing brace");
                    }

                    sb.AppendLine(line);
                    int idx = line.IndexOf('}');
                    if (idx != -1)
                    {
                        int    length = 1 + sb.Length - (line.Length - idx);
                        string data   = Common.Parse(sb.ToString(0, length));
                        if (String.IsNullOrEmpty(Common.Token))
                        {
                            break; // end of file
                        }
                        if (Common.Token != "{")
                        {
                            Sys.Error("First token isn't a brace");
                        }

                        if (entnum == -1)
                        {
                            // parse the global vars
                            Progs.ParseGlobals(data);
                        }
                        else
                        {
                            // parse an edict
                            edict_t ent = Server.EdictNum(entnum);
                            ent.Clear();
                            Progs.ParseEdict(data, ent);

                            // link it into the bsp tree
                            if (!ent.free)
                            {
                                Server.LinkEdict(ent, false);
                            }
                        }

                        entnum++;
                        sb.Remove(0, length);
                    }
                }

                Server.sv.num_edicts = entnum;
                Server.sv.time       = time;

                for (int i = 0; i < Server.NUM_SPAWN_PARMS; i++)
                {
                    Server.svs.clients[0].spawn_parms[i] = spawn_parms[i];
                }
            }

            if (Client.cls.state != cactive_t.ca_dedicated)
            {
                Client.EstablishConnection("local");
                Reconnect_f();
            }
        }
Пример #42
0
        /// <summary>
        /// RETURN_EDICT(e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(e))
        /// </summary>
        public static unsafe void ReturnEdict(edict_t e)
        {
            int prog = Server.EdictToProg(e);

            ReturnInt(prog);
        }
Пример #43
0
        /// <summary>
        /// SV_WriteClientdataToMessage
        /// </summary>
        public static void WriteClientDataToMessage(edict_t ent, MsgWriter msg)
        {
            //
            // send a damage message
            //
            if (ent.v.dmg_take != 0 || ent.v.dmg_save != 0)
            {
                edict_t other = ProgToEdict(ent.v.dmg_inflictor);
                msg.WriteByte(protocol.svc_damage);
                msg.WriteByte((int)ent.v.dmg_save);
                msg.WriteByte((int)ent.v.dmg_take);
                msg.WriteCoord(other.v.origin.x + 0.5f * (other.v.mins.x + other.v.maxs.x));
                msg.WriteCoord(other.v.origin.y + 0.5f * (other.v.mins.y + other.v.maxs.y));
                msg.WriteCoord(other.v.origin.z + 0.5f * (other.v.mins.z + other.v.maxs.z));

                ent.v.dmg_take = 0;
                ent.v.dmg_save = 0;
            }

            //
            // send the current viewpos offset from the view entity
            //
            SetIdealPitch();            // how much to look up / down ideally

            // a fixangle might get lost in a dropped packet.  Oh well.
            if (ent.v.fixangle != 0)
            {
                msg.WriteByte(protocol.svc_setangle);
                msg.WriteAngle(ent.v.angles.x);
                msg.WriteAngle(ent.v.angles.y);
                msg.WriteAngle(ent.v.angles.z);
                ent.v.fixangle = 0;
            }

            int bits = 0;

            if (ent.v.view_ofs.z != protocol.DEFAULT_VIEWHEIGHT)
            {
                bits |= protocol.SU_VIEWHEIGHT;
            }

            if (ent.v.idealpitch != 0)
            {
                bits |= protocol.SU_IDEALPITCH;
            }

            // stuff the sigil bits into the high bits of items for sbar, or else
            // mix in items2
            float val = progs.GetEdictFieldFloat(ent, "items2", 0);
            int   items;

            if (val != 0)
            {
                items = (int)ent.v.items | ((int)val << 23);
            }
            else
            {
                items = (int)ent.v.items | ((int)progs.GlobalStruct.serverflags << 28);
            }

            bits |= protocol.SU_ITEMS;

            if (((int)ent.v.flags & EdictFlags.FL_ONGROUND) != 0)
            {
                bits |= protocol.SU_ONGROUND;
            }

            if (ent.v.waterlevel >= 2)
            {
                bits |= protocol.SU_INWATER;
            }

            if (ent.v.punchangle.x != 0)
            {
                bits |= protocol.SU_PUNCH1;
            }
            if (ent.v.punchangle.y != 0)
            {
                bits |= protocol.SU_PUNCH2;
            }
            if (ent.v.punchangle.z != 0)
            {
                bits |= protocol.SU_PUNCH3;
            }

            if (ent.v.velocity.x != 0)
            {
                bits |= protocol.SU_VELOCITY1;
            }
            if (ent.v.velocity.y != 0)
            {
                bits |= protocol.SU_VELOCITY2;
            }
            if (ent.v.velocity.z != 0)
            {
                bits |= protocol.SU_VELOCITY3;
            }

            if (ent.v.weaponframe != 0)
            {
                bits |= protocol.SU_WEAPONFRAME;
            }

            if (ent.v.armorvalue != 0)
            {
                bits |= protocol.SU_ARMOR;
            }

            //	if (ent.v.weapon)
            bits |= protocol.SU_WEAPON;

            // send the data

            msg.WriteByte(protocol.svc_clientdata);
            msg.WriteShort(bits);

            if ((bits & protocol.SU_VIEWHEIGHT) != 0)
            {
                msg.WriteChar((int)ent.v.view_ofs.z);
            }

            if ((bits & protocol.SU_IDEALPITCH) != 0)
            {
                msg.WriteChar((int)ent.v.idealpitch);
            }

            if ((bits & protocol.SU_PUNCH1) != 0)
            {
                msg.WriteChar((int)ent.v.punchangle.x);
            }
            if ((bits & protocol.SU_VELOCITY1) != 0)
            {
                msg.WriteChar((int)(ent.v.velocity.x / 16));
            }

            if ((bits & protocol.SU_PUNCH2) != 0)
            {
                msg.WriteChar((int)ent.v.punchangle.y);
            }
            if ((bits & protocol.SU_VELOCITY2) != 0)
            {
                msg.WriteChar((int)(ent.v.velocity.y / 16));
            }

            if ((bits & protocol.SU_PUNCH3) != 0)
            {
                msg.WriteChar((int)ent.v.punchangle.z);
            }
            if ((bits & protocol.SU_VELOCITY3) != 0)
            {
                msg.WriteChar((int)(ent.v.velocity.z / 16));
            }

            // always sent
            msg.WriteLong(items);

            if ((bits & protocol.SU_WEAPONFRAME) != 0)
            {
                msg.WriteByte((int)ent.v.weaponframe);
            }
            if ((bits & protocol.SU_ARMOR) != 0)
            {
                msg.WriteByte((int)ent.v.armorvalue);
            }
            if ((bits & protocol.SU_WEAPON) != 0)
            {
                msg.WriteByte(ModelIndex(progs.GetString(ent.v.weaponmodel)));
            }

            msg.WriteShort((int)ent.v.health);
            msg.WriteByte((int)ent.v.currentammo);
            msg.WriteByte((int)ent.v.ammo_shells);
            msg.WriteByte((int)ent.v.ammo_nails);
            msg.WriteByte((int)ent.v.ammo_rockets);
            msg.WriteByte((int)ent.v.ammo_cells);

            if (common.GameKind == GameKind.StandardQuake)
            {
                msg.WriteByte((int)ent.v.weapon);
            }
            else
            {
                for (int i = 0; i < 32; i++)
                {
                    if ((((int)ent.v.weapon) & (1 << i)) != 0)
                    {
                        msg.WriteByte(i);
                        break;
                    }
                }
            }
        }
Пример #44
0
        /// <summary>
        /// SV_Physics
        /// </summary>
        public static void Physics()
        {
            // let the progs know that a new frame has started
            Progs.GlobalStruct.self  = EdictToProg(sv.edicts[0]);
            Progs.GlobalStruct.other = Progs.GlobalStruct.self;
            Progs.GlobalStruct.time  = (float)sv.time;
            Progs.Execute(Progs.GlobalStruct.StartFrame);

            //
            // treat each object in turn
            //
            for (int i = 0; i < sv.num_edicts; i++)
            {
                edict_t ent = sv.edicts[i];
                if (ent.free)
                {
                    continue;
                }

                if (Progs.GlobalStruct.force_retouch != 0)
                {
                    LinkEdict(ent, true);       // force retouch even for stationary
                }

                if (i > 0 && i <= svs.maxclients)
                {
                    Physics_Client(ent, i);
                }
                else
                {
                    switch ((int)ent.v.movetype)
                    {
                    case Movetypes.MOVETYPE_PUSH:
                        Physics_Pusher(ent);
                        break;

                    case Movetypes.MOVETYPE_NONE:
                        Physics_None(ent);
                        break;

                    case Movetypes.MOVETYPE_NOCLIP:
                        Physics_Noclip(ent);
                        break;

                    case Movetypes.MOVETYPE_STEP:
                        Physics_Step(ent);
                        break;

                    case Movetypes.MOVETYPE_TOSS:
                    case Movetypes.MOVETYPE_BOUNCE:
                    case Movetypes.MOVETYPE_FLY:
                    case Movetypes.MOVETYPE_FLYMISSILE:
                        Physics_Toss(ent);
                        break;

                    default:
                        Sys.Error("SV_Physics: bad movetype {0}", (int)ent.v.movetype);
                        break;
                    }
                }
            }

            if (Progs.GlobalStruct.force_retouch != 0)
            {
                Progs.GlobalStruct.force_retouch -= 1;
            }

            sv.time += Host.FrameTime;
        }
Пример #45
0
        /// <summary>
        /// SV_TryUnstick
        /// Player has come to a dead stop, possibly due to the problem with limited
        /// float precision at some angle joins in the BSP hull.
        ///
        /// Try fixing by pushing one pixel in each direction.
        ///
        /// This is a hack, but in the interest of good gameplay...
        /// </summary>
        private static int TryUnstick(edict_t ent, ref v3f oldvel)
        {
            v3f oldorg = ent.v.origin;
            v3f dir    = Common.ZeroVector3f;

            trace_t steptrace = new trace_t();

            for (int i = 0; i < 8; i++)
            {
                // try pushing a little in an axial direction
                switch (i)
                {
                case 0:
                    dir.x = 2;
                    dir.y = 0;
                    break;

                case 1:
                    dir.x = 0;
                    dir.y = 2;
                    break;

                case 2:
                    dir.x = -2;
                    dir.y = 0;
                    break;

                case 3:
                    dir.x = 0;
                    dir.y = -2;
                    break;

                case 4:
                    dir.x = 2;
                    dir.y = 2;
                    break;

                case 5:
                    dir.x = -2;
                    dir.y = 2;
                    break;

                case 6:
                    dir.x = 2;
                    dir.y = -2;
                    break;

                case 7:
                    dir.x = -2;
                    dir.y = -2;
                    break;
                }

                PushEntity(ent, ref dir);

                // retry the original move
                ent.v.velocity.x = oldvel.x;
                ent.v.velocity.y = oldvel.y;
                ent.v.velocity.z = 0;
                int clip = FlyMove(ent, 0.1f, steptrace);

                if (Math.Abs(oldorg.y - ent.v.origin.y) > 4 || Math.Abs(oldorg.x - ent.v.origin.x) > 4)
                {
                    return(clip);
                }

                // go back to the original pos and try again
                ent.v.origin = oldorg;
            }

            ent.v.velocity = Common.ZeroVector3f;
            return(7);           // still not moving
        }
Пример #46
0
        /// <summary>
        /// SV_WalkMove
        /// Only used by players
        /// </summary>
        static void WalkMove(edict_t ent)
        {
            //
            // do a regular slide move unless it looks like you ran into a step
            //
            int oldonground = (int)ent.v.flags & EdictFlags.FL_ONGROUND;

            ent.v.flags = (int)ent.v.flags & ~EdictFlags.FL_ONGROUND;

            v3f     oldorg    = ent.v.origin;
            v3f     oldvel    = ent.v.velocity;
            trace_t steptrace = new trace_t();
            int     clip      = FlyMove(ent, (float)Host.FrameTime, steptrace);

            if ((clip & 2) == 0)
            {
                return;         // move didn't block on a step
            }
            if (oldonground == 0 && ent.v.waterlevel == 0)
            {
                return;         // don't stair up while jumping
            }
            if (ent.v.movetype != Movetypes.MOVETYPE_WALK)
            {
                return;         // gibbed by a trigger
            }
            if (_NoStep.Value != 0)
            {
                return;
            }

            if (((int)_Player.v.flags & EdictFlags.FL_WATERJUMP) != 0)
            {
                return;
            }

            v3f nosteporg = ent.v.origin;
            v3f nostepvel = ent.v.velocity;

            //
            // try moving up and forward to go up a step
            //
            ent.v.origin = oldorg;      // back to start pos

            v3f upmove   = Common.ZeroVector3f;
            v3f downmove = upmove;

            upmove.z   = STEPSIZE;
            downmove.z = (float)(-STEPSIZE + oldvel.z * Host.FrameTime);

            // move up
            PushEntity(ent, ref upmove);        // FIXME: don't link?

            // move forward
            ent.v.velocity.x = oldvel.x;
            ent.v.velocity.y = oldvel.y;
            ent.v.velocity.z = 0;
            clip             = FlyMove(ent, (float)Host.FrameTime, steptrace);

            // check for stuckness, possibly due to the limited precision of floats
            // in the clipping hulls
            if (clip != 0)
            {
                if (Math.Abs(oldorg.y - ent.v.origin.y) < 0.03125 && Math.Abs(oldorg.x - ent.v.origin.x) < 0.03125)
                {
                    // stepping up didn't make any progress
                    clip = TryUnstick(ent, ref oldvel);
                }
            }

            // extra friction based on view angle
            if ((clip & 2) != 0)
            {
                WallFriction(ent, steptrace);
            }

            // move down
            trace_t downtrace = PushEntity(ent, ref downmove);  // FIXME: don't link?

            if (downtrace.plane.normal.Z > 0.7)
            {
                if (ent.v.solid == Solids.SOLID_BSP)
                {
                    ent.v.flags        = (int)ent.v.flags | EdictFlags.FL_ONGROUND;
                    ent.v.groundentity = EdictToProg(downtrace.ent);
                }
            }
            else
            {
                // if the push down didn't end up on good ground, use the move without
                // the step up.  This happens near wall / slope combinations, and can
                // cause the player to hop up higher on a slope too steep to climb
                ent.v.origin   = nosteporg;
                ent.v.velocity = nostepvel;
            }
        }
Пример #47
0
        /// <summary>
        /// SV_Physics_Toss
        /// Toss, bounce, and fly movement.  When onground, do nothing.
        /// </summary>
        private static void Physics_Toss(edict_t ent)
        {
            // regular thinking
            if (!RunThink(ent))
            {
                return;
            }

            // if onground, return without moving
            if (((int)ent.v.flags & EdictFlags.FL_ONGROUND) != 0)
            {
                return;
            }

            CheckVelocity(ent);

            // add gravity
            if (ent.v.movetype != Movetypes.MOVETYPE_FLY && ent.v.movetype != Movetypes.MOVETYPE_FLYMISSILE)
            {
                AddGravity(ent);
            }


            // move angles
            Mathlib.VectorMA(ref ent.v.angles, (float)Host.FrameTime, ref ent.v.avelocity, out ent.v.angles);

            // move origin
            v3f move;

            Mathlib.VectorScale(ref ent.v.velocity, (float)Host.FrameTime, out move);
            trace_t trace = PushEntity(ent, ref move);

            if (trace.fraction == 1)
            {
                return;
            }
            if (ent.free)
            {
                return;
            }

            float backoff;

            if (ent.v.movetype == Movetypes.MOVETYPE_BOUNCE)
            {
                backoff = 1.5f;
            }
            else
            {
                backoff = 1;
            }

            ClipVelocity(ref ent.v.velocity, ref trace.plane.normal, out ent.v.velocity, backoff);

            // stop if on ground
            if (trace.plane.normal.Z > 0.7f)
            {
                if (ent.v.velocity.z < 60 || ent.v.movetype != Movetypes.MOVETYPE_BOUNCE)
                {
                    ent.v.flags        = (int)ent.v.flags | EdictFlags.FL_ONGROUND;
                    ent.v.groundentity = EdictToProg(trace.ent);
                    ent.v.velocity     = default(v3f);
                    ent.v.avelocity    = default(v3f);
                }
            }

            // check for in water
            CheckWaterTransition(ent);
        }
Пример #48
0
        /// <summary>
        /// SV_Impact
        /// Two entities have touched, so run their touch functions
        /// </summary>
        static void Impact(edict_t e1, edict_t e2)
        {
            int old_self = Progs.GlobalStruct.self;
            int old_other = Progs.GlobalStruct.other;

            Progs.GlobalStruct.time = (float)sv.time;
            if (e1.v.touch != 0 && e1.v.solid != Solids.SOLID_NOT)
            {
                Progs.GlobalStruct.self = EdictToProg(e1);
                Progs.GlobalStruct.other = EdictToProg(e2);
                Progs.Execute(e1.v.touch);
            }

            if (e2.v.touch != 0 && e2.v.solid != Solids.SOLID_NOT)
            {
                Progs.GlobalStruct.self = EdictToProg(e2);
                Progs.GlobalStruct.other = EdictToProg(e1);
                Progs.Execute(e2.v.touch);
            }

            Progs.GlobalStruct.self = old_self;
            Progs.GlobalStruct.other = old_other;
        }
Пример #49
0
        static void SetMinMaxSize(edict_t e, ref Vector3 min, ref Vector3 max, bool rotate)
        {
            if (min.X > max.X || min.Y > max.Y || min.Z > max.Z)
            {
                Progs.RunError("backwards mins/maxs");
            }

            rotate = false;             // FIXME: implement rotation properly again

            Vector3 rmin = min, rmax = max;

            if (!rotate)
            {
                //rmin = min;
                //rmax = max;
            }
            else
            {
                // find min / max for rotations
                //angles = e.v.angles;

                //a = angles[1] / 180 * M_PI;

                //xvector[0] = cos(a);
                //xvector[1] = sin(a);
                //yvector[0] = -sin(a);
                //yvector[1] = cos(a);

                //VectorCopy(min, bounds[0]);
                //VectorCopy(max, bounds[1]);

                //rmin[0] = rmin[1] = rmin[2] = 9999;
                //rmax[0] = rmax[1] = rmax[2] = -9999;

                //for (i = 0; i <= 1; i++)
                //{
                //    base[0] = bounds[i][0];
                //    for (j = 0; j <= 1; j++)
                //    {
                //        base[1] = bounds[j][1];
                //        for (k = 0; k <= 1; k++)
                //        {
                //            base[2] = bounds[k][2];

                //            // transform the point
                //            transformed[0] = xvector[0] * base[0] + yvector[0] * base[1];
                //            transformed[1] = xvector[1] * base[0] + yvector[1] * base[1];
                //            transformed[2] = base[2];

                //            for (l = 0; l < 3; l++)
                //            {
                //                if (transformed[l] < rmin[l])
                //                    rmin[l] = transformed[l];
                //                if (transformed[l] > rmax[l])
                //                    rmax[l] = transformed[l];
                //            }
                //        }
                //    }
                //}
            }

            // set derived values
            Mathlib.Copy(ref rmin, out e.v.mins);
            Mathlib.Copy(ref rmax, out e.v.maxs);
            Vector3 s = max - min;

            Mathlib.Copy(ref s, out e.v.size);

            Server.LinkEdict(e, false);
        }
Пример #50
0
        /// <summary>
        /// SV_Physics_Noclip
        /// A moving object that doesn't obey physics
        /// </summary>
        static void Physics_Noclip(edict_t ent)
        {
            // regular thinking
            if (!RunThink(ent))
                return;

            Mathlib.VectorMA(ref ent.v.angles, (float)Host.FrameTime, ref ent.v.avelocity, out ent.v.angles);
            Mathlib.VectorMA(ref ent.v.origin, (float)Host.FrameTime, ref ent.v.velocity, out ent.v.origin);
            LinkEdict(ent, false);
        }
Пример #51
0
        /// <summary>
        /// SV_Physics_Client
        /// Player character actions
        /// </summary>
        static void Physics_Client(edict_t ent, int num)
        {
            if (!svs.clients[num - 1].active)
            {
                return;         // unconnected slot
            }
            //
            // call standard client pre-think
            //
            Progs.GlobalStruct.time = (float)sv.time;
            Progs.GlobalStruct.self = EdictToProg(ent);
            Progs.Execute(Progs.GlobalStruct.PlayerPreThink);

            //
            // do a move
            //
            CheckVelocity(ent);

            //
            // decide which move function to call
            //
            switch ((int)ent.v.movetype)
            {
            case Movetypes.MOVETYPE_NONE:
                if (!RunThink(ent))
                {
                    return;
                }
                break;

            case Movetypes.MOVETYPE_WALK:
                if (!RunThink(ent))
                {
                    return;
                }
                if (!CheckWater(ent) && ((int)ent.v.flags & EdictFlags.FL_WATERJUMP) == 0)
                {
                    AddGravity(ent);
                }
                CheckStuck(ent);

                WalkMove(ent);
                break;

            case Movetypes.MOVETYPE_TOSS:
            case Movetypes.MOVETYPE_BOUNCE:
                Physics_Toss(ent);
                break;

            case Movetypes.MOVETYPE_FLY:
                if (!RunThink(ent))
                {
                    return;
                }
                FlyMove(ent, (float)Host.FrameTime, null);
                break;

            case Movetypes.MOVETYPE_NOCLIP:
                if (!RunThink(ent))
                {
                    return;
                }
                Mathlib.VectorMA(ref ent.v.origin, (float)Host.FrameTime, ref ent.v.velocity, out ent.v.origin);
                break;

            default:
                Sys.Error("SV_Physics_client: bad movetype {0}", (int)ent.v.movetype);
                break;
            }

            //
            // call standard player post-think
            //
            LinkEdict(ent, true);

            Progs.GlobalStruct.time = (float)sv.time;
            Progs.GlobalStruct.self = EdictToProg(ent);
            Progs.Execute(Progs.GlobalStruct.PlayerPostThink);
        }
Пример #52
0
        /// <summary>
        /// SV_FlyMove
        /// The basic solid body movement clip that slides along multiple planes
        /// Returns the clipflags if the velocity was modified (hit something solid)
        /// 1 = floor
        /// 2 = wall / step
        /// 4 = dead stop
        /// If steptrace is not NULL, the trace of any vertical wall hit will be stored
        /// </summary>
        static int FlyMove(edict_t ent, float time, trace_t steptrace)
        {
            v3f original_velocity = ent.v.velocity;
            v3f primal_velocity   = ent.v.velocity;

            int numbumps = 4;
            int blocked  = 0;

            Vector3[] planes    = new Vector3[MAX_CLIP_PLANES];
            int       numplanes = 0;
            float     time_left = time;

            for (int bumpcount = 0; bumpcount < numbumps; bumpcount++)
            {
                if (ent.v.velocity.IsEmpty)
                {
                    break;
                }

                v3f end;
                Mathlib.VectorMA(ref ent.v.origin, time_left, ref ent.v.velocity, out end);

                trace_t trace = Move(ref ent.v.origin, ref ent.v.mins, ref ent.v.maxs, ref end, 0, ent);

                if (trace.allsolid)
                {       // entity is trapped in another solid
                    ent.v.velocity = default(v3f);
                    return(3);
                }

                if (trace.fraction > 0)
                {       // actually covered some distance
                    Mathlib.Copy(ref trace.endpos, out ent.v.origin);
                    original_velocity = ent.v.velocity;
                    numplanes         = 0;
                }

                if (trace.fraction == 1)
                {
                    break;              // moved the entire distance
                }
                if (trace.ent == null)
                {
                    Sys.Error("SV_FlyMove: !trace.ent");
                }

                if (trace.plane.normal.Z > 0.7)
                {
                    blocked |= 1;               // floor
                    if (trace.ent.v.solid == Solids.SOLID_BSP)
                    {
                        ent.v.flags        = (int)ent.v.flags | EdictFlags.FL_ONGROUND;
                        ent.v.groundentity = EdictToProg(trace.ent);
                    }
                }

                if (trace.plane.normal.Z == 0)
                {
                    blocked |= 2;               // step
                    if (steptrace != null)
                    {
                        steptrace.CopyFrom(trace);      // save for player extrafriction
                    }
                }

                //
                // run the impact function
                //
                Impact(ent, trace.ent);
                if (ent.free)
                {
                    break;              // removed by the impact function
                }
                time_left -= time_left * trace.fraction;

                // cliped to another plane
                if (numplanes >= MAX_CLIP_PLANES)
                {
                    // this shouldn't really happen
                    ent.v.velocity = default(v3f);
                    return(3);
                }

                planes[numplanes] = trace.plane.normal;
                numplanes++;

                //
                // modify original_velocity so it parallels all of the clip planes
                //
                v3f new_velocity = default(v3f);
                int i, j;
                for (i = 0; i < numplanes; i++)
                {
                    ClipVelocity(ref original_velocity, ref planes[i], out new_velocity, 1);
                    for (j = 0; j < numplanes; j++)
                    {
                        if (j != i)
                        {
                            float dot = new_velocity.x * planes[j].X + new_velocity.y * planes[j].Y + new_velocity.z * planes[j].Z;
                            if (dot < 0)
                            {
                                break;  // not ok
                            }
                        }
                    }
                    if (j == numplanes)
                    {
                        break;
                    }
                }

                if (i != numplanes)
                {
                    // go along this plane
                    ent.v.velocity = new_velocity;
                }
                else
                {
                    // go along the crease
                    if (numplanes != 2)
                    {
                        ent.v.velocity = default(v3f);
                        return(7);
                    }
                    Vector3 dir = Vector3.Cross(planes[0], planes[1]);
                    float   d   = dir.X * ent.v.velocity.x + dir.Y * ent.v.velocity.y + dir.Z * ent.v.velocity.z;
                    Mathlib.Copy(ref dir, out ent.v.velocity);
                    Mathlib.VectorScale(ref ent.v.velocity, d, out ent.v.velocity);
                }

                //
                // if original velocity is against the original velocity, stop dead
                // to avoid tiny occilations in sloping corners
                //
                if (Mathlib.DotProduct(ref ent.v.velocity, ref primal_velocity) <= 0)
                {
                    ent.v.velocity = default(v3f);
                    return(blocked);
                }
            }

            return(blocked);
        }
Пример #53
0
 public void Clear()
 {
     this.active = false;
     this.spawned = false;
     this.dropasap = false;
     this.privileged = false;
     this.sendsignon = false;
     this.last_message = 0;
     this.netconnection = null;
     this.cmd.Clear();
     this.wishdir = Vector3.Zero;
     this.message.Clear();
     this.edict = null;
     this.name = null;
     this.colors = 0;
     Array.Clear(this.ping_times, 0, this.ping_times.Length);
     this.num_pings = 0;
     Array.Clear(this.spawn_parms, 0, this.spawn_parms.Length);
     this.old_frags = 0;
 }
Пример #54
0
        /// <summary>
        /// SV_FlyMove
        /// The basic solid body movement clip that slides along multiple planes
        /// Returns the clipflags if the velocity was modified (hit something solid)
        /// 1 = floor
        /// 2 = wall / step
        /// 4 = dead stop
        /// If steptrace is not NULL, the trace of any vertical wall hit will be stored
        /// </summary>
        static int FlyMove(edict_t ent, float time, trace_t steptrace)
        {
            v3f original_velocity = ent.v.velocity;
            v3f primal_velocity = ent.v.velocity;

            int numbumps = 4;
            int blocked = 0;
            Vector3[] planes = new Vector3[MAX_CLIP_PLANES];
            int numplanes = 0;
            float time_left = time;

            for (int bumpcount = 0; bumpcount < numbumps; bumpcount++)
            {
                if (ent.v.velocity.IsEmpty)
                    break;

                v3f end;
                Mathlib.VectorMA(ref ent.v.origin, time_left, ref ent.v.velocity, out end);

                trace_t trace = Move(ref ent.v.origin, ref ent.v.mins, ref ent.v.maxs, ref end, 0, ent);

                if (trace.allsolid)
                {	// entity is trapped in another solid
                    ent.v.velocity = default(v3f);
                    return 3;
                }

                if (trace.fraction > 0)
                {	// actually covered some distance
                    Mathlib.Copy(ref trace.endpos, out ent.v.origin);
                    original_velocity = ent.v.velocity;
                    numplanes = 0;
                }

                if (trace.fraction == 1)
                    break;		// moved the entire distance

                if (trace.ent == null)
                    Sys.Error("SV_FlyMove: !trace.ent");

                if (trace.plane.normal.Z > 0.7)
                {
                    blocked |= 1;		// floor
                    if (trace.ent.v.solid == Solids.SOLID_BSP)
                    {
                        ent.v.flags = (int)ent.v.flags | EdictFlags.FL_ONGROUND;
                        ent.v.groundentity = EdictToProg(trace.ent);
                    }
                }

                if (trace.plane.normal.Z == 0)
                {
                    blocked |= 2;		// step
                    if (steptrace != null)
                        steptrace.CopyFrom(trace);	// save for player extrafriction
                }

                //
                // run the impact function
                //
                Impact(ent, trace.ent);
                if (ent.free)
                    break;		// removed by the impact function

                time_left -= time_left * trace.fraction;

                // cliped to another plane
                if (numplanes >= MAX_CLIP_PLANES)
                {
                    // this shouldn't really happen
                    ent.v.velocity = default(v3f);
                    return 3;
                }

                planes[numplanes] = trace.plane.normal;
                numplanes++;

                //
                // modify original_velocity so it parallels all of the clip planes
                //
                v3f new_velocity = default(v3f);
                int i, j;
                for (i = 0; i < numplanes; i++)
                {
                    ClipVelocity(ref original_velocity, ref planes[i], out new_velocity, 1);
                    for (j = 0; j < numplanes; j++)
                        if (j != i)
                        {
                            float dot = new_velocity.x * planes[j].X + new_velocity.y * planes[j].Y + new_velocity.z * planes[j].Z;
                            if (dot < 0)
                                break;	// not ok
                        }
                    if (j == numplanes)
                        break;
                }

                if (i != numplanes)
                {
                    // go along this plane
                    ent.v.velocity = new_velocity;
                }
                else
                {
                    // go along the crease
                    if (numplanes != 2)
                    {
                        ent.v.velocity = default(v3f);
                        return 7;
                    }
                    Vector3 dir = Vector3.Cross(planes[0], planes[1]);
                    float d = dir.X * ent.v.velocity.x + dir.Y * ent.v.velocity.y + dir.Z * ent.v.velocity.z;
                    Mathlib.Copy(ref dir, out ent.v.velocity);
                    Mathlib.VectorScale(ref ent.v.velocity, d, out ent.v.velocity);
                }

                //
                // if original velocity is against the original velocity, stop dead
                // to avoid tiny occilations in sloping corners
                //
                if (Mathlib.DotProduct(ref ent.v.velocity, ref primal_velocity) <= 0)
                {
                    ent.v.velocity = default(v3f);
                    return blocked;
                }
            }

            return blocked;
        }
Пример #55
0
        static trace_t Move(ref v3f start, ref v3f mins, ref v3f maxs, ref v3f end, int type, edict_t passedict)
        {
            Vector3 vstart, vmins, vmaxs, vend;

            Mathlib.Copy(ref start, out vstart);
            Mathlib.Copy(ref mins, out vmins);
            Mathlib.Copy(ref maxs, out vmaxs);
            Mathlib.Copy(ref end, out vend);
            return(Move(ref vstart, ref vmins, ref vmaxs, ref vend, type, passedict));
        }
Пример #56
0
        /// <summary>
        /// SV_Move
        /// mins and maxs are relative
        /// if the entire move stays in a solid volume, trace.allsolid will be set
        /// if the starting point is in a solid, it will be allowed to move out to an open area
        /// nomonsters is used for line of sight or edge testing, where mosnters
        /// shouldn't be considered solid objects
        /// passedict is explicitly excluded from clipping checks (normally NULL)
        /// </summary>
        public static trace_t Move(ref Vector3 start, ref Vector3 mins, ref Vector3 maxs, ref Vector3 end, int type, edict_t passedict)
        {
            moveclip_t clip = new moveclip_t();

            // clip to world
            clip.trace = ClipMoveToEntity(sv.edicts[0], ref start, ref mins, ref maxs, ref end);

            clip.start = start;
            clip.end = end;
            clip.mins = mins;
            clip.maxs = maxs;
            clip.type = type;
            clip.passedict = passedict;

            if (type ==  MOVE_MISSILE)
            {
                clip.mins2 = Vector3.One * -15;
                clip.maxs2 = Vector3.One * 15;
            }
            else
            {
                clip.mins2 = mins;
                clip.maxs2 = maxs;
            }

            // create the bounding box of the entire move
            MoveBounds(ref start, ref clip.mins2, ref clip.maxs2, ref end, out clip.boxmins, out clip.boxmaxs);

            // clip to entities
            ClipToLinks(_AreaNodes[0], clip);

            return clip.trace;
        }
Пример #57
0
 /// <summary>
 /// EDICT_TO_PROG(e)
 /// </summary>
 public static int EdictToProg(edict_t e)
 {
     return Array.IndexOf(_Server.edicts, e); // todo: optimize this
 }
Пример #58
0
        /// <summary>
        /// SV_UnlinkEdict
        /// call before removing an entity, and before trying to move one,
        /// so it doesn't clip against itself
        /// flags ent->v.modified
        /// </summary>
        public static void UnlinkEdict(edict_t ent)
        {
            if (ent.area.Prev == null)
                return;		// not linked in anywhere

            ent.area.Remove();  //RemoveLink(&ent->area);
            //ent->area.prev = ent->area.next = NULL;
        }
Пример #59
0
        /// <summary>
        /// NUM_FOR_EDICT
        /// </summary>
        public static int NumForEdict(edict_t e)
        {
            int i = Array.IndexOf(sv.edicts, e); // todo: optimize this

            if (i < 0)
                Sys.Error("NUM_FOR_EDICT: bad pointer");
            return i;
        }
Пример #60
0
        /// <summary>
        /// SV_ClipMoveToEntity
        /// Handles selection or creation of a clipping hull, and offseting (and
        /// eventually rotation) of the end points
        /// </summary>
        static trace_t ClipMoveToEntity(edict_t ent, ref Vector3 start, ref Vector3 mins, ref Vector3 maxs, ref Vector3 end)
        {
            trace_t trace = new trace_t();
            // fill in a default trace
            trace.fraction = 1;
            trace.allsolid = true;
            trace.endpos = end;

            // get the clipping hull
            Vector3 offset;
            hull_t hull = HullForEntity(ent, ref mins, ref maxs, out offset);

            Vector3 start_l = start - offset;
            Vector3 end_l = end - offset;

            // trace a line through the apropriate clipping hull
            RecursiveHullCheck(hull, hull.firstclipnode, 0, 1, ref start_l, ref end_l, trace);

            // fix trace up by the offset
            if (trace.fraction != 1)
                trace.endpos += offset;

            // did we clip the move?
            if (trace.fraction < 1 || trace.startsolid)
                trace.ent = ent;

            return trace;
        }