diff --git a/platform/base_models.pk3dir/models/b_portal.bsp b/platform/base_models.pk3dir/models/b_portal.bsp new file mode 100644 index 00000000..81cda990 Binary files /dev/null and b/platform/base_models.pk3dir/models/b_portal.bsp differ diff --git a/platform/base_models.pk3dir/models/b_portal.map b/platform/base_models.pk3dir/models/b_portal.map new file mode 100644 index 00000000..8594ecb9 --- /dev/null +++ b/platform/base_models.pk3dir/models/b_portal.map @@ -0,0 +1,13 @@ +{ +"spawnflags" "0" +"classname" "worldspawn" +"wad" "C:\Games\Quake\gmapn\fte.wad" +{ +( -8 64 -80 ) ( -8 64 -64 ) ( -8 -0 -80 ) nodraw -64 64 180 1 -1 +( -64 64 -80 ) ( -0 64 -80 ) ( -64 64 -64 ) nodraw 0 64 -180 1 -1 +( -64 64 -64 ) ( -64 -0 -64 ) ( -0 64 -64 ) nodraw 64 0 90 1 1 +( -0 -0 -64 ) ( -0 64 -64 ) ( -0 -0 -80 ) warpzone 64 64 0 1 1 +( -0 -64 -64 ) ( -0 -64 -80 ) ( -64 -64 -64 ) nodraw 0 64 -180 1 -1 +( -0 -0 64 ) ( -64 -0 64 ) ( -0 64 64 ) nodraw 64 0 90 1 1 +} +} diff --git a/platform/base_scripts.pk3dir/scripts/drawdebug.shader b/platform/base_scripts.pk3dir/scripts/drawdebug.shader new file mode 100644 index 00000000..b22030e2 --- /dev/null +++ b/platform/base_scripts.pk3dir/scripts/drawdebug.shader @@ -0,0 +1,10 @@ +debugcone +{ + cull none + + { + rgbGen vertex + alphaGen vertex + blendFunc blend + } +} \ No newline at end of file diff --git a/platform/base_scripts.pk3dir/scripts/drawportal.shader b/platform/base_scripts.pk3dir/scripts/drawportal.shader new file mode 100644 index 00000000..3452b494 --- /dev/null +++ b/platform/base_scripts.pk3dir/scripts/drawportal.shader @@ -0,0 +1,15 @@ +//used on the front face of the portal entities. +warpzone +{ + sort portal + surfaceparm nomarks + surfaceparm nodlight +} + +//used on side+back faces, just to hide them. +nodraw +{ + surfaceparm nomarks + surfaceparm nodlight + surfaceparm nodraw +} \ No newline at end of file diff --git a/src/client/defs.h b/src/client/defs.h index 26202817..8c30d5a1 100644 Binary files a/src/client/defs.h and b/src/client/defs.h differ diff --git a/src/client/entities.qc b/src/client/entities.qc index 1d7f877e..b8e0d8dd 100644 --- a/src/client/entities.qc +++ b/src/client/entities.qc @@ -48,6 +48,9 @@ Entity_EntityUpdate(float type, float new) case ENT_VEHICLE: basevehicle_readentity(new); break; + case ENT_PORTAL: + NSPortal_ReadEntity(new); + break; case ENT_VEH_BRUSH: func_vehicle_readentity(new); break; @@ -142,6 +145,9 @@ Entity_EntityUpdate(float type, float new) case ENT_PUSH: trigger_push_ReadEntity(new); break; + case ENT_ENTITYPROJECTILE: + NSProjectile_ReadEntity(new); + break; default: //error(sprintf("Unknown entity type update received. (%d)\n", t)); } diff --git a/src/server/defs.h b/src/server/defs.h index 963140d6..82e7b1cf 100644 --- a/src/server/defs.h +++ b/src/server/defs.h @@ -148,3 +148,6 @@ void main(void) #define SAVE_VECTOR(x,y,z) fputs(x, sprintf("%S \"%v\" ", y, z)) #define SAVE_STRING(x,y,z) fputs(x, sprintf("%S \"%s\" ", y, z)) #define SAVE_HEX(x,y,z) fputs(x, sprintf("%S \"%x\" ", y, z)) + +NSEntity EntityDef_SpawnClassname(string className); +NSEntity EntityDef_CreateClassname(string className); diff --git a/src/server/entityDef.qc b/src/server/entityDef.qc index de2134d7..2ce05781 100644 --- a/src/server/entityDef.qc +++ b/src/server/entityDef.qc @@ -103,7 +103,7 @@ EntityDef_ReadFile(string filePath) /* we've reached the end of a definition */ if (braceDepth == 0) { /* we have something somewhat valid I guess */ - if (currentDef.entClass != "" && currentDef.spawnClass != "") { + if (currentDef.entClass != "" /*&& currentDef.spawnClass != ""*/) { g_entDefTable[g_entDefCount].entClass = currentDef.entClass; g_entDefTable[g_entDefCount].spawnClass = currentDef.spawnClass; g_entDefTable[g_entDefCount].spawnData = currentDef.spawnData; @@ -184,7 +184,7 @@ EntityDef_PrepareEntity(entity target, int id) /* check if the spawnclass is an entityDef */ for (int i = 0i; i < g_entDefCount; i++) { if (g_entDefTable[id].spawnClass == g_entDefTable[i].entClass) { - EntityDef_PrepareEntity(self, i); + EntityDef_PrepareEntity(target, i); isEntDefBased = true; break; } @@ -233,7 +233,7 @@ EntityDef_PrepareEntity(entity target, int id) targetEnt.Respawn(); /* now we rename the classname for better visibility */ - self.classname = g_entDefTable[id].entClass; + targetEnt.classname = g_entDefTable[id].entClass; __fullspawndata = ""; return targetEnt; } @@ -248,4 +248,56 @@ EntityDef_SpawnClassname(string className) } return __NULL__; +} + +NSEntity +EntityDef_CreateClassname(string className) +{ + entity oldSelf = self; + NSEntity new = spawn(NSEntity); + self = new; + EntityDef_SpawnClassname(className); + self = oldSelf; + return new; +} + +string +EntityDef_GetKeyValue(string className, string keyName) +{ + float spawnWords = 0; + string inheritKeys = __NULL__; + + /* loop through all defs */ + for (int i = 0i; i < g_entDefCount; i++) { + /* found the right def */ + //print(sprintf("%S %S\n", className, g_entDefTable[i].entClass)); + if (className == g_entDefTable[i].entClass) { + inheritKeys = g_entDefTable[i].inheritKeys; + spawnWords = tokenize_console(g_entDefTable[i].spawnData); + + /* iterate over our own spawnkeys first */ + for (int c = 0i; c < spawnWords; c+= 2i) { + if (argv(c) == keyName) { + return argv(c+1); + } + } + + /* not found, look into the inherit keyword's spawndata */ + if (inheritKeys == __NULL__) + return ""; + + for (int b = 0i; b < g_entDefCount; i++) { + if (inheritKeys == g_entDefTable[b].entClass) { + spawnWords = tokenize_console(g_entDefTable[b].spawnData); + for (int c = 0i; c < spawnWords; c+= 2i) { + if (argv(c) == keyName) { + return argv(c+1); + } + } + } + } + } + } + + return ""; } \ No newline at end of file diff --git a/src/server/entry.qc b/src/server/entry.qc index b633add8..aecd2b23 100644 --- a/src/server/entry.qc +++ b/src/server/entry.qc @@ -775,10 +775,10 @@ to remove in case we won't initialize it. void CheckSpawn(void() spawnfunc) { - if (EntityDef_SpawnClassname(self.classname)) - return; if (MapTweak_EntitySpawn(self)) return; + if (EntityDef_SpawnClassname(self.classname)) + return; if (spawnfunc) { spawnfunc(); diff --git a/src/server/skill.qc b/src/server/skill.qc index f892768c..34c1903d 100644 --- a/src/server/skill.qc +++ b/src/server/skill.qc @@ -47,3 +47,14 @@ Skill_GetValue(string variable, float defaultvalue) float val = fabs(cvar(sprintf("sk_%s%d", variable, skill))); return (val == 0) ? defaultvalue : val; } + +/* input string is potentially a skill variable */ +float +Skill_GetDefValue(string variable) +{ + if (substring(variable, 0, 6) == "skill:") { + return Skill_GetValue(substring(variable, 6, -1), 0); + } + + return stof(variable); +} \ No newline at end of file diff --git a/src/shared/NSClientPlayer.h b/src/shared/NSClientPlayer.h index 336d74fe..65b9f999 100644 --- a/src/shared/NSClientPlayer.h +++ b/src/shared/NSClientPlayer.h @@ -14,6 +14,8 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +noref .vector v_angle; + /** This entity class represents every player client. When clients connect via the connect command, they will findthemselves @@ -116,7 +118,7 @@ private: PREDICTED_FLOAT(viewzoom) PREDICTED_VECTOR_N(view_ofs) PREDICTED_VECTOR_N(basevelocity) - PREDICTED_VECTOR(v_angle) + PREDICTED_VECTOR_N(v_angle) PREDICTED_FLOAT_N(pmove_flags) PREDICTED_FLOAT(w_attack_next) diff --git a/src/shared/NSItem.h b/src/shared/NSItem.h index 54659145..e93b0d3b 100644 --- a/src/shared/NSItem.h +++ b/src/shared/NSItem.h @@ -27,9 +27,14 @@ public: virtual void Respawn(void); virtual void SpawnKey(string, string); - virtual void SetItem(int i); - virtual void SetFloating(int); - virtual void PickupRespawn(void); + nonvirtual void SetItem(int i); + nonvirtual int GetItem(void); + nonvirtual void SetFloating(int); + nonvirtual bool GetFloating(void); + nonvirtual void SetSpinning(bool); + nonvirtual bool GetSpinning(void); + + nonvirtual void PickupRespawn(void); private: int m_iClip; diff --git a/src/shared/NSItem.qc b/src/shared/NSItem.qc index d8d04b2d..31cbd229 100644 --- a/src/shared/NSItem.qc +++ b/src/shared/NSItem.qc @@ -17,7 +17,7 @@ #ifdef SERVER void NSItem::NSItem(void) { - m_iClip = 0i; + m_iClip = -1; m_iWasDropped = 0i; m_iInvItem = 0i; m_sndAcquire = __NULL__; @@ -122,11 +122,36 @@ void NSItem::SetItem(int i) SetSize([-16,-16,0], [16,16,16]); } +int +NSItem::GetItem(void) +{ + return m_iInvItem; +} + void NSItem::SetFloating(int i) { m_bFloating = i ? true : false; } +bool +NSItem::GetFloating(void) +{ + return m_bFloating; +} + +void +NSItem::SetSpinning(bool value) +{ + m_bSpins = value; +} + +bool +NSItem::GetSpinning(void) +{ + return m_bSpins; +} + + void NSItem::PickupRespawn(void) { diff --git a/src/shared/NSNavAI.h b/src/shared/NSNavAI.h index 15cf5a65..1d9aeed6 100644 --- a/src/shared/NSNavAI.h +++ b/src/shared/NSNavAI.h @@ -14,6 +14,10 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#ifndef MAX_AMMO_TYPES +#define MAX_AMMO_TYPES 16 +#endif + /** This entity class represents a moving/pathfinding object. It knows how to deal with waypoint based nodes and possibly other types of pathfinding in the future. @@ -29,6 +33,9 @@ private: nodeslist_t *m_pRoute; vector m_vecLastNode; vector m_vecTurnAngle; + + /* These are defined in side defs\*.def, ammo_types and ammo_names */ + int m_iAmmoTypes[MAX_AMMO_TYPES]; #endif public: diff --git a/src/shared/NSNavAI.qc b/src/shared/NSNavAI.qc index 5f57ba5a..2a079995 100644 --- a/src/shared/NSNavAI.qc +++ b/src/shared/NSNavAI.qc @@ -23,6 +23,9 @@ NSNavAI::NSNavAI(void) m_pRoute = __NULL__; m_vecLastNode = [0,0,0]; m_vecTurnAngle = [0,0,0]; + + for (int i = 0; i < MAX_AMMO_TYPES; i++) + m_iAmmoTypes[i] = 0; #endif } diff --git a/src/shared/NSPortal.h b/src/shared/NSPortal.h new file mode 100644 index 00000000..801acb66 --- /dev/null +++ b/src/shared/NSPortal.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2016-2022 Vera Visions LLC. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +typedef enumflags +{ + PORTALFL_CHANGED_ORIGIN_X, + PORTALFL_CHANGED_ORIGIN_Y, + PORTALFL_CHANGED_ORIGIN_Z, + PORTALFL_CHANGED_ANGLES_X, + PORTALFL_CHANGED_ANGLES_Y, + PORTALFL_CHANGED_ANGLES_Z, + PORTALFL_CHANGED_TARG_ENTITY, + PORTALFL_CHANGED_TARG_ORIGIN_X, + PORTALFL_CHANGED_TARG_ORIGIN_Y, + PORTALFL_CHANGED_TARG_ORIGIN_Z, + PORTALFL_CHANGED_TARG_ANGLES_X, + PORTALFL_CHANGED_TARG_ANGLES_Y, + PORTALFL_CHANGED_TARG_ANGLES_Z +} nsportal_changed_t; + +class +NSPortal:NSEntity +{ + void NSPortal(void); + + nonvirtual void _PortalUpdated(void); + nonvirtual vector _OriginTransform(vector); + nonvirtual vector _DirectionTransform(vector); + + /** overrides */ + virtual vector camera_transform(vector, vector); + +#ifdef SERVER + virtual void EvaluateEntity(void); + virtual float SendEntity(entity, float); + + /** Sets the NSPortal ID. Only used to link portals together via PortalAutoLink. */ + nonvirtual void SetPortalID(int); + /** Will link this portal to another NSPortal. */ + nonvirtual bool PortalLinkTo(NSPortal); + /** Will link this portal to the youngest other NSPortal. */ + nonvirtual void PortalAutoLink(void); +#endif +#ifdef CLIENT + virtual void ReceiveEntity(float, float); +#endif + +private: + NSPortal m_ePortalTarget; + NSPortal m_ePortalTarget_net; + PREDICTED_VECTOR(m_vecTargetPos) + PREDICTED_VECTOR(m_vecTargetAngle) + + vector m_vecPortalPos; + vector m_vecPortalN; + vector m_vecPortalS; + vector m_vecPortalT; + + /** Will transport an entity from its position to the exit position. */ + nonvirtual void TransportEntity(NSEntity); + +#ifdef CLIENT + vector m_vecTargetPos; + vector m_vecTargetN; + vector m_vecTargetS; + vector m_vecTargetT; +#endif + +#ifdef SERVER + int m_iPortalValue; +#endif + + int m_brushNum; +}; + +#ifdef CLIENT +void NSPortal_ReadEntity(bool); +#endif + +#define SOLID_PORTAL 21 + +.float portalnum; +.float impulse; //used as the radius for the solid_portal csg subtraction \ No newline at end of file diff --git a/src/shared/NSPortal.qc b/src/shared/NSPortal.qc new file mode 100644 index 00000000..677c884c --- /dev/null +++ b/src/shared/NSPortal.qc @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2023 Vera Visions LLC. + * Copyright (c) 2014 David Walton + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#define PORTAL_MINS [8, -64, -64] +#define PORTAL_MAXS [0, 64, 64] + +void +NSPortal::NSPortal(void) +{ + m_ePortalTarget = __NULL__; + m_ePortalTarget_net = __NULL__; + m_vecTargetPos = g_vec_null; + m_vecTargetAngle = g_vec_null; + m_vecPortalPos = g_vec_null; + m_vecPortalN = g_vec_null; + m_vecPortalS = g_vec_null; + m_vecPortalT = g_vec_null; + m_brushNum = -1; + +#ifdef CLIENT + m_vecTargetPos = g_vec_null; + m_vecTargetN = g_vec_null; + m_vecTargetS = g_vec_null; + m_vecTargetT = g_vec_null; +#endif + +#ifdef SERVER + + pvsflags = PVSF_NOREMOVE; + frame1time = 0.0f; + m_iPortalValue = -1; + + /* unique portal IDs */ + { + float numPortals = 0; + for (NSPortal e = __NULL__; (e = (NSPortal)find(e, ::classname, "NSPortal"));) { + e.portalnum = numPortals++; + } + } +#endif +} + +#ifdef SERVER +void +NSPortal::PortalAutoLink(void) +{ + /* this just links against the youngest portal of the same ID at this time */ + NSPortal youngestPortal, oldestPortal; + oldestPortal = youngestPortal = __NULL__; + float youngestTime = 99999.0f; + float oldestTime = 0.0f; + + /* delete any portal with the same id */ + for (NSPortal e = __NULL__; (e = (NSPortal)find(e, ::classname, "NSPortal"));) { + if (e == this) + continue; + + /* find first AND last portal */ + if (e.m_iPortalValue == m_iPortalValue) { + if (oldestTime < e.frame1time) { + oldestTime = e.frame1time; + oldestPortal = e; + } + if (youngestTime > e.frame1time) { + youngestTime = e.frame1time; + youngestPortal = e; + } + } + } + + /* remove the first portal from the world */ + if (youngestPortal != oldestPortal) { + oldestPortal.Destroy(); + } + + PortalLinkTo(youngestPortal); +} + +bool +NSPortal::PortalLinkTo(NSPortal target) +{ + m_ePortalTarget = target; + skin = m_ePortalTarget ? 0 : 1; + SendFlags |= 1; + _PortalUpdated(); + + if (m_ePortalTarget) { + m_ePortalTarget.m_ePortalTarget = this; + m_ePortalTarget.skin = skin; + m_ePortalTarget.SendFlags |= 1; + m_ePortalTarget._PortalUpdated(); + } + + return target ? true : false; +} + +void +NSPortal::SetPortalID(int value) +{ + m_iPortalValue = value; +} +#endif + +void +NSPortal::_PortalUpdated(void) +{ + /* the displayed surface must be in a known position. + we're not going to compensate for the model here, because I'm too lazy. */ + vector newAngles = angles; + //newAngles[0] * -1; + makevectors(newAngles); + + m_vecPortalN = v_forward; + m_vecPortalS = -v_right; + m_vecPortalT = v_up; + m_vecPortalPos = origin; + + /* expand the size of the object along the plane, and set up a portal region. */ + movetype = MOVETYPE_NONE; + solid = skin ? SOLID_BSP : SOLID_PORTAL; + precache_model("models/b_portal.bsp"); + setmodel(this, "models/b_portal.bsp"); + + /* determine size of major axis */ + float portalSize = max(size[0], size[1], size[2]); + impulse = portalSize; /* let the engine know how wide the portal should be */ + + /* make sure the abs size contains the entire portal. */ + portalSize = sqrt(portalSize * portalSize * 2); + mins -= portalSize * [1, 1, 1]; + maxs += portalSize * [1, 1, 1]; + setsize(this, mins, maxs); +} + +vector +NSPortal::_DirectionTransform(vector v) +{ + /* FIXME: this should include .angles stuff */ + vector tmp, r; + tmp[0] = v * m_vecPortalN; + tmp[1] = v * m_vecPortalS; + tmp[2] = v * m_vecPortalT; + + r = [0, 0, 0]; + + if (!m_ePortalTarget) { +#ifdef CSQC + r += tmp[2] * m_vecTargetT; + r -= tmp[1] * m_vecTargetS; + r -= tmp[0] * m_vecTargetN; +#else + r += tmp[2] * this.m_vecPortalT; + r -= tmp[1] * this.m_vecPortalS; + r -= tmp[0] * this.m_vecPortalN; +#endif + } else { + r += tmp[2] * m_ePortalTarget.m_vecPortalT; + r -= tmp[1] * m_ePortalTarget.m_vecPortalS; + r -= tmp[0] * m_ePortalTarget.m_vecPortalN; + } + + return r; +} + +vector +NSPortal::_OriginTransform(vector p) +{ + if (!m_ePortalTarget) { +#ifdef CSQC + return m_vecTargetPos - _DirectionTransform(m_vecPortalPos - p); +#else + return this.m_vecPortalPos - _DirectionTransform(m_vecPortalPos - p); +#endif + } + + return m_ePortalTarget.m_vecPortalPos - _DirectionTransform(m_vecPortalPos - p); +} + +// need to generate forward/right/up vectors +// return value is the new view origin. +// trace_end_pos needs to contain the pvs origin. +vector +NSPortal::camera_transform(vector originalOrg, vector originalAngles) +{ + vector newCameraPos; + + newCameraPos = _OriginTransform(originalOrg); + v_forward = _DirectionTransform(v_forward); + v_right = _DirectionTransform(v_right); + v_up = _DirectionTransform(v_up); + + //trace from the center of the target to the view, to set trace_endpos for the pvs origin + if (m_ePortalTarget) + traceline(m_ePortalTarget.m_vecPortalPos, newCameraPos, MOVE_NOMONSTERS, this); + else + trace_endpos = this.m_vecPortalPos; + + return newCameraPos; +} + +/* because when using custom player physics, the engine will not do us any favors + we have to do the portal transformation manually. DO NOT USE THIS METHOD YOURSELF + unless you really know what you're doing. This is a private method! */ +void +NSPortal::TransportEntity(NSEntity target) +{ + static void getMatrixDiff(vector aX, vector aY, vector aZ, vector bX, vector bY, vector bZ) { + v_forward.x = aX.x * bX.x + aY.x * bX.y + aZ.x * bX.z; + v_forward.y = aX.y * bX.x + aY.y * bX.y + aZ.y * bX.z; + v_forward.z = aX.z * bX.x + aY.z * bX.y + aZ.z * bX.z; + + v_right.x = aX.x * bY.x + aY.x * bY.y + aZ.x * bY.z; + v_right.y = aX.y * bY.x + aY.y * bY.y + aZ.y * bY.z; + v_right.z = aX.z * bY.x + aY.z * bY.y + aZ.z * bY.z; + + v_up.x = aX.x * bZ.x + aY.x * bZ.y + aZ.x * bZ.z; + v_up.y = aX.y * bZ.x + aY.y * bZ.y + aZ.y * bZ.z; + v_up.z = aX.z * bZ.x + aY.z * bZ.y + aZ.z * bZ.z; + } + + vector offsetPos; + vector portalAngleX, portalAngleY, portalAngleZ; + vector exitAngleX, exitAngleY, exitAngleZ; + vector playerDelta; + vector targetAngleX, targetAngleY, targetAngleZ; + vector playerVelX, playerVelY, playerVelZ; + vector angleDiff; + float totalSpeed; + + /* angle matrix of the portal entry */ + makevectors(GetAngles()); + portalAngleX = v_forward * -1; + portalAngleY = v_right * -1; + portalAngleZ = v_up; + + /* matrix of target entity angles */ + if (target.flags & FL_CLIENT) { + makevectors(input_angles); + } else { + makevectors(target.GetAngles()); + } + targetAngleX = v_forward; + targetAngleY = v_right; + targetAngleZ = v_up; + + /* angle matrix of portal exit */ + makevectors(m_ePortalTarget.GetAngles()); + exitAngleX = v_forward; + exitAngleY = v_right; + exitAngleZ = v_up; + + /* ! CALCULATE THE NEW POSITION - START ! */ + /* get the offset */ + offsetPos = target.GetOrigin() - GetOrigin(); + + /* calculate the delta of player pos to portal using said angle matrix */ + playerDelta[0] = dotproduct(offsetPos, portalAngleX); + playerDelta[1] = dotproduct(offsetPos, portalAngleY); + playerDelta[2] = dotproduct(offsetPos, portalAngleZ); + + /* now, translate the offset to the exit angle */ + offsetPos = m_ePortalTarget.GetOrigin(); + offsetPos += exitAngleX * playerDelta[0]; + offsetPos += exitAngleY * playerDelta[1]; + offsetPos += exitAngleZ * playerDelta[2]; + target.SetOrigin(offsetPos); /* apply the new position */ + + /* ! CALCULATE THE NEW ANGLE - START ! */ + /* get the difference between the two rotational matrices */ + getMatrixDiff(portalAngleX, portalAngleY, portalAngleZ * -1, targetAngleX, targetAngleY, targetAngleZ); + angleDiff = vectoangles(v_forward, -v_up); /* we now have an euler angle */ + + /* apply the new angles */ + if (target.flags & FL_CLIENT) { + input_angles = m_ePortalTarget.GetAngles() - angleDiff; + target.v_angle = input_angles; + //Client_FixAngle(this, target.v_angle); + } else { + target.angles = m_ePortalTarget.GetAngles() - angleDiff; + } + + /* ! CALCULATE DESTINATION VELOCITY - START ! */ + /* let's get the current speed first */ + totalSpeed = vlen(target.GetVelocity()); + /* convert target velocity into a direction matrix */ + makevectors(vectoangles(target.GetVelocity())); + playerVelX = v_forward; + playerVelY = v_right; + playerVelZ = v_up; + getMatrixDiff(portalAngleX, portalAngleY, portalAngleZ, playerVelX, playerVelY, playerVelZ); + angleDiff = vectoangles(v_forward, v_up); /* we now have an euler angle again */ + /* get the new movement direction relative to the exit portal */ + makevectors(m_ePortalTarget.GetAngles() + angleDiff); + /* apply the final velocity */ + target.SetVelocity(v_forward * totalSpeed); +} + +#ifdef SERVER +void +NSPortal::EvaluateEntity(void) +{ + EVALUATE_VECTOR(origin, 0, PORTALFL_CHANGED_ORIGIN_X) + EVALUATE_VECTOR(origin, 1, PORTALFL_CHANGED_ORIGIN_Y) + EVALUATE_VECTOR(origin, 2, PORTALFL_CHANGED_ORIGIN_Z) + EVALUATE_VECTOR(angles, 0, PORTALFL_CHANGED_ANGLES_X) + EVALUATE_VECTOR(angles, 1, PORTALFL_CHANGED_ANGLES_Y) + EVALUATE_VECTOR(angles, 2, PORTALFL_CHANGED_ANGLES_Z) + EVALUATE_FIELD(m_ePortalTarget, PORTALFL_CHANGED_TARG_ENTITY) + EVALUATE_VECTOR(m_vecTargetPos, 0, PORTALFL_CHANGED_TARG_ORIGIN_X) + EVALUATE_VECTOR(m_vecTargetPos, 1, PORTALFL_CHANGED_TARG_ORIGIN_Y) + EVALUATE_VECTOR(m_vecTargetPos, 2, PORTALFL_CHANGED_TARG_ORIGIN_Z) + EVALUATE_VECTOR(m_vecTargetAngle, 0, PORTALFL_CHANGED_TARG_ANGLES_X) + EVALUATE_VECTOR(m_vecTargetAngle, 1, PORTALFL_CHANGED_TARG_ANGLES_Y) + EVALUATE_VECTOR(m_vecTargetAngle, 2, PORTALFL_CHANGED_TARG_ANGLES_Z) +} + +float +NSPortal::SendEntity(entity ePEnt, float flChanged) +{ + WriteByte(MSG_ENTITY, ENT_PORTAL); + WriteFloat(MSG_ENTITY, flChanged); + + SENDENTITY_COORD(origin[0], PORTALFL_CHANGED_ORIGIN_X) + SENDENTITY_COORD(origin[1], PORTALFL_CHANGED_ORIGIN_Y) + SENDENTITY_COORD(origin[2], PORTALFL_CHANGED_ORIGIN_Z) + SENDENTITY_FLOAT(angles[0], PORTALFL_CHANGED_ANGLES_X) + SENDENTITY_FLOAT(angles[1], PORTALFL_CHANGED_ANGLES_Y) + SENDENTITY_FLOAT(angles[2], PORTALFL_CHANGED_ANGLES_Z) + SENDENTITY_ENTITY(m_ePortalTarget, PORTALFL_CHANGED_TARG_ENTITY) + + if (1) { + SENDENTITY_COORD(m_vecTargetPos[0], PORTALFL_CHANGED_TARG_ORIGIN_X) + SENDENTITY_COORD(m_vecTargetPos[1], PORTALFL_CHANGED_TARG_ORIGIN_Y) + SENDENTITY_COORD(m_vecTargetPos[2], PORTALFL_CHANGED_TARG_ORIGIN_Z) + SENDENTITY_FLOAT(m_vecTargetAngle[0], PORTALFL_CHANGED_TARG_ANGLES_X) + SENDENTITY_FLOAT(m_vecTargetAngle[1], PORTALFL_CHANGED_TARG_ANGLES_Y) + SENDENTITY_FLOAT(m_vecTargetAngle[2], PORTALFL_CHANGED_TARG_ANGLES_Z) + } + + return (true); +} +#endif + +#ifdef CLIENT +void +NSPortal::ReceiveEntity(float flNew, float flChanged) +{ + if (m_ePortalTarget) { + m_ePortalTarget.m_ePortalTarget = world; + m_ePortalTarget = world; + } + + READENTITY_COORD(origin[0], PORTALFL_CHANGED_ORIGIN_X) + READENTITY_COORD(origin[1], PORTALFL_CHANGED_ORIGIN_Y) + READENTITY_COORD(origin[2], PORTALFL_CHANGED_ORIGIN_Z) + READENTITY_FLOAT(angles[0], PORTALFL_CHANGED_ANGLES_X) + READENTITY_FLOAT(angles[1], PORTALFL_CHANGED_ANGLES_Y) + READENTITY_FLOAT(angles[2], PORTALFL_CHANGED_ANGLES_Z) + READENTITY_ENTITY(m_ePortalTarget, PORTALFL_CHANGED_TARG_ENTITY) + + READENTITY_COORD(m_vecTargetPos[0], PORTALFL_CHANGED_TARG_ORIGIN_X) + READENTITY_COORD(m_vecTargetPos[1], PORTALFL_CHANGED_TARG_ORIGIN_Y) + READENTITY_COORD(m_vecTargetPos[2], PORTALFL_CHANGED_TARG_ORIGIN_Z) + READENTITY_FLOAT(m_vecTargetAngle[0], PORTALFL_CHANGED_TARG_ANGLES_X) + READENTITY_FLOAT(m_vecTargetAngle[1], PORTALFL_CHANGED_TARG_ANGLES_Y) + READENTITY_FLOAT(m_vecTargetAngle[2], PORTALFL_CHANGED_TARG_ANGLES_Z) + + if (m_ePortalTarget) { + if (m_ePortalTarget.m_ePortalTarget) + m_ePortalTarget.m_ePortalTarget.m_ePortalTarget = world; + + m_ePortalTarget. m_ePortalTarget = this; + } else { + skin = 1; + makevectors(angles); + m_vecTargetPos = origin; + m_vecTargetN = v_forward; + m_vecTargetS = v_right; + m_vecTargetT = v_up; + } + setorigin(this, origin); + _PortalUpdated(); + drawmask = MASK_ENGINE; + predraw = 0; +} +#endif + +#ifdef CLIENT +void +NSPortal_ReadEntity(bool new) +{ + NSPortal me = (NSPortal)self; + if (new) { + spawnfunc_NSPortal(); + } + me.ReceiveEntity(new, readfloat()); +} +#endif \ No newline at end of file diff --git a/src/shared/NSProjectile.h b/src/shared/NSProjectile.h index 3c939a67..5197c086 100644 --- a/src/shared/NSProjectile.h +++ b/src/shared/NSProjectile.h @@ -14,23 +14,98 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +typedef enumflags +{ + PROJ_CHANGED_ORIGIN_X, + PROJ_CHANGED_ORIGIN_Y, + PROJ_CHANGED_ORIGIN_Z, + PROJ_CHANGED_ANGLES_X, + PROJ_CHANGED_ANGLES_Y, + PROJ_CHANGED_ANGLES_Z, + PROJ_CHANGED_MODELINDEX, + PROJ_CHANGED_SIZE, + PROJ_CHANGED_FRAME, + PROJ_CHANGED_SKIN, + PROJ_CHANGED_EFFECTS, + PROJ_CHANGED_BODY, + PROJ_CHANGED_SCALE, + PROJ_CHANGED_VELOCITY, + PROJ_CHANGED_ANGULARVELOCITY, + PROJ_CHANGED_RENDERCOLOR, + PROJ_CHANGED_RENDERAMT, + PROJ_CHANGED_RENDERMODE, +} nsprojectile_changed_t; + +.void(entity, entity) m_pImpact; +.float traileffectnum; + /** This entity class represents an interactive projectile. Objects such as rockets, grenades, bolts etc. should ideally be this. */ class NSProjectile:NSSurfacePropEntity { private: + PREDICTED_FLOAT_N(traileffectnum) + +#ifdef SERVER /* sprite animation gubbins */ int m_iProjectileAnimEnd; int m_iProjectileAnimStart; float m_flProjectileFramerate; - virtual void m_pImpact(entity, entity) = 0; + + /* temp */ + float m_flDmgMultiplier; + + /* defAPI */ + string m_defDamage; + string m_defSplashDamage; + vector m_vecLaunchVelocity; + float m_flThrust; /* TODO */ + float m_flThrustStart; /* TODO */ + float m_flThrustEnd; /* TODO */ + float m_flFrictionLinear; /* TODO */ + float m_flBounce; + float m_flMass; /* TODO */ + float m_flGravity; /* TODO */ + float m_flFuse; + bool m_bDetonateOnFuse; + bool m_bDetonateOnDeath; + bool m_bDetonateOnWorld; + bool m_bDetonateOnActor; + bool m_bImpactEffect; /* TODO */ + bool m_bImpactGib; /* TODO */ + string m_matDetonate; /* TODO */ + float m_flDecalSize; /* TODO */ + string m_partSmokeFly; + string m_partModelDetonate; + string m_partSmokeDetonate; + string m_partSmokeBounce; + string m_partSmokeFuse; + int m_iDebrisCount; /* TODO */ + vector m_vecLightColor; /* TODO */ + float m_flLightRadius; /* TODO */ + float m_flLightOffset; /* TODO */ + vector m_vecExplodeLightColor; /* TODO */ + float m_fExplodelLightRadius; /* TODO */ + float m_fExplodelLightFadetime; /* TODO */ + string m_sndFly; + string m_sndExplode; + string m_sndBounce; + + /* ETQW-additions */ + bool m_bIsBullet; nonvirtual void _AnimateThink(void); nonvirtual void _AnimateThinkDead(void); +#endif public: void NSProjectile(void); +#ifdef CLIENT + virtual void ReceiveEntity(float, float); +#endif + +#ifdef SERVER /** Sets the function that'll be called upon impact of the projectile onto a surface. */ nonvirtual void SetImpact(void(entity, entity)); /** When called, will animated between two frame positions at a specified framerate on loop. */ @@ -41,4 +116,24 @@ public: /** Called upon the projectile touching another object. */ virtual void Touch(entity); virtual void Spawned(void); + virtual void Death(void); + virtual void Pain(void); + + virtual void SpawnKey(string, string); + virtual void EvaluateEntity(void); + virtual float SendEntity(entity, float); + + nonvirtual void _FuseEnded(void); + nonvirtual void _Explode(void); + + nonvirtual void _LaunchHitscan(vector, vector, float); + + /* launch the projectile into the world */ + nonvirtual void Launch(vector, vector, float, float, float); +#endif }; + + +#ifdef CLIENT +void NSProjectile_ReadEntity(bool); +#endif \ No newline at end of file diff --git a/src/shared/NSProjectile.qc b/src/shared/NSProjectile.qc index 26a2eb44..c9a3c74b 100644 --- a/src/shared/NSProjectile.qc +++ b/src/shared/NSProjectile.qc @@ -17,9 +17,187 @@ void NSProjectile::NSProjectile(void) { +#ifdef SERVER m_iProjectileAnimEnd = 0i; m_iProjectileAnimStart = 0i; m_flProjectileFramerate = 0.1f; /* default to 10 hz */ + + /* defAPI */ + m_defDamage = __NULL__; + m_defSplashDamage = __NULL__; + m_vecLaunchVelocity = g_vec_null; + m_flThrust = 0.0f; + m_flThrustStart = 0.0f; + m_flThrustEnd = 0.0f; + m_flFrictionLinear = 0.0f; + m_flBounce = 0.0f; + m_flMass = 0.0f; + m_flGravity = 0.0f; + m_flFuse = 0.0f; + m_bDetonateOnFuse = 0.0f; + m_bDetonateOnDeath = 0.0f; + m_bDetonateOnWorld = 0.0f; + m_bDetonateOnActor = 0.0f; + m_bImpactEffect = false; + m_bImpactGib = false; + m_matDetonate = __NULL__; + m_flDecalSize = 0.0f; + m_partSmokeFly = __NULL__; + m_partModelDetonate = __NULL__; + m_partSmokeDetonate = __NULL__; + m_partSmokeBounce = __NULL__; + m_partSmokeFuse = __NULL__; + m_iDebrisCount = 0i; + m_vecLightColor = g_vec_null; + m_flLightRadius = 0.0f; + m_flLightOffset = 0.0f; + m_vecExplodeLightColor = g_vec_null; + m_fExplodelLightRadius = 0.0f; + m_fExplodelLightFadetime = 0.0f; + m_sndFly = __NULL__; + m_sndExplode = __NULL__; + m_sndBounce = __NULL__; + + /* ETQW-additions */ + m_bIsBullet = false; +#endif +} + +#ifdef SERVER +void +NSProjectile::SpawnKey(string strKey, string strValue) +{ + switch (strKey) { + case "def_damage": /* direct damage, upon touch */ + m_defDamage = ReadString(strValue); + break; + case "def_splash_damage": /* direct damage, upon touch */ + m_defSplashDamage = ReadString(strValue); + break; + case "velocity": + m_vecLaunchVelocity = ReadVector(strValue); + break; + case "health": /* amount of dmg the projectile can take */ + health = ReadFloat(strValue); + break; + case "model": + model = ReadString(strValue); + m_oldModel = model; + break; + case "velocity": + velocity = ReadVector(strValue); + break; + case "angular_velocity": /* sets avelocity */ + avelocity = ReadVector(strValue); + break; + case "thrust": /* rate of acceeleration */ + m_flThrust = ReadFloat(strValue); + break; + case "thrust_start": /* when to begin accel */ + m_flThrustStart = ReadFloat(strValue); + break; + case "thrust_end": /* when to stop accel */ + m_flThrustEnd = ReadFloat(strValue); + break; + case "linear_friction": /* air friction */ + m_flFrictionLinear = ReadFloat(strValue); + break; + case "bounce": /* bounce multiplier */ + m_flBounce = ReadFloat(strValue); + break; + case "mass": + m_flMass = ReadFloat(strValue); + break; + case "gravity": /* 0 means no gravity */ + m_flGravity = ReadFloat(strValue); + break; + case "fuse": /* fuse time in seconds */ + m_flFuse = ReadFloat(strValue); + break; + case "detonate_on_fuse": /* should it detonate when fuse runs out? */ + m_bDetonateOnFuse = ReadBool(strValue); + break; + case "detonate_on_death": /* should it detonate when it gets damaged enough? */ + m_bDetonateOnDeath = ReadBool(strValue); + break; + case "detonate_on_world": /* should it detonate when touching world? */ + m_bDetonateOnWorld = ReadBool(strValue); + break; + case "detonate_on_actor": /* should it detonate when touching a monster/player */ + m_bDetonateOnActor = ReadBool(strValue); + break; + case "impact_damage_effect": /* blood splats? */ + m_bImpactEffect = ReadBool(strValue); + break; + case "impact_gib": /* gibs */ + m_bImpactGib = ReadBool(strValue); + break; + case "mtr_detonate": + m_matDetonate = ReadString(strValue); + break; + case "decal_size": + m_flDecalSize = ReadFloat(strValue); + break; + case "smoke_fly": + m_partSmokeFly = ReadString(strValue); + break; + case "model_detonate": + m_partModelDetonate = ReadString(strValue); + break; + case "smoke_detonate": + m_partSmokeDetonate = ReadString(strValue); + break; + case "smoke_bounce": + m_partSmokeBounce = ReadString(strValue); + break; + case "smoke_fuse": + m_partSmokeFuse = ReadString(strValue); + break; + case "debris_count": + m_iDebrisCount = ReadInt(strValue); + break; + /*case "def_debris": + break; + case "def_shrapnel": + break; + case "mtr_light_shader": + break;*/ + case "light_color": + m_vecLightColor = ReadVector(strValue); + break; + case "light_radius": + m_flLightRadius = ReadFloat(strValue); + break; + case "light_offset": + m_flLightOffset = ReadFloat(strValue); + break; + /*case "mtr_explode_light_shader": + break;*/ + case "explode_light_color": + m_vecExplodeLightColor = ReadVector(strValue); + break; + case "explode_light_radus": + m_fExplodelLightRadius = ReadFloat(strValue); + break; + case "explode_light_fadetime": + m_fExplodelLightFadetime = ReadFloat(strValue); + break; + case "snd_fly": + m_sndFly = ReadString(strValue); + break; + case "snd_explode": + m_sndExplode = ReadString(strValue); + break; + case "snd_bounce": + m_sndBounce = ReadString(strValue); + break; + /* ETQW-additions */ + case "is_bullet": + m_bIsBullet = ReadBool(strValue); + break; + default: + super::SpawnKey(strKey, strValue); + } } void @@ -39,9 +217,53 @@ NSProjectile::Spawned(void) void NSProjectile::Touch(entity eToucher) { - if (m_pImpact) - m_pImpact(eToucher, this); + if (m_defDamage) + if (eToucher.takedamage != DAMAGE_NO) { + float damageVal; + float baseDamage = Skill_GetDefValue(EntityDef_GetKeyValue(m_defDamage, "damage")); + float randomDamage = Skill_GetDefValue(EntityDef_GetKeyValue(m_defDamage, "damage_random")); + damageVal = (baseDamage + randomDamage); + if (m_flDmgMultiplier >= 0.0) + damageVal *= m_flDmgMultiplier; + + Damage_Apply(eToucher, owner, damageVal, WEAPON_NONE, DMG_BLUNT); + } + + if (m_bDetonateOnWorld) { + if (eToucher == world || eToucher.takedamage == DAMAGE_NO) { + _Explode(); + return; + } + } + if (m_bDetonateOnActor) { + if (eToucher.takedamage != DAMAGE_NO) { + _Explode(); + return; + } + } + + if (m_partSmokeBounce) + pointparticles(particleeffectnum(m_partSmokeBounce), origin, velocity, 1); + + StartSoundDef(m_sndBounce, CHAN_BODY, true); +} + +void +NSProjectile::Pain(void) +{ + /* do nothing. */ +} + +void +NSProjectile::Death(void) +{ + if (m_bDetonateOnDeath) { + _Explode(); + return; + } + + /* whatever else comes otherwise? */ Destroy(); } @@ -98,4 +320,187 @@ NSProjectile::AnimateOnce(int startframe, int endframe, float framerate) frame = startframe; think = _AnimateThinkDead; nextthink = time + m_flProjectileFramerate; -} \ No newline at end of file +} + +void +NSProjectile::_FuseEnded(void) +{ + if (m_bDetonateOnFuse) { + _Explode(); + return; + } + + if (m_partSmokeFuse) + pointparticles(particleeffectnum(m_partSmokeFuse), origin, velocity, 1); + + Destroy(); +} +void +NSProjectile::_Explode(void) +{ + makevectors(vectoangles(velocity)); + vector explodePos = origin - (v_forward * 32); + + if (m_partModelDetonate) + pointparticles(particleeffectnum(m_partModelDetonate), explodePos, velocity, 1); + + //print(sprintf("%S\n", m_defSplashDamage)); + if (m_defSplashDamage) { + float flDamage = Skill_GetDefValue(EntityDef_GetKeyValue(m_defSplashDamage, "damage")); + float flRadius = Skill_GetDefValue(EntityDef_GetKeyValue(m_defSplashDamage, "radius")); + + if (m_flDmgMultiplier >= 0.0) + flDamage *= m_flDmgMultiplier; + + //print(sprintf("Damage: %d; Radius: %d\n", flDamage, flRadius)); + Damage_Radius(origin, owner, flDamage, flRadius, TRUE, WEAPON_NONE); + } + + StartSoundDef(m_sndExplode, CHAN_VOICE, true); + Destroy(); +} + +void +NSProjectile::_LaunchHitscan(vector startPos, vector launchDir, float dmgMultiplier) +{ + +} + +void +NSProjectile::Launch(vector startPos, vector launchDir, float fuseOffset, float powerMultiplier, float dmgMultiplier) +{ + vector moveVel = g_vec_null; + + if (m_bIsBullet) { + _LaunchHitscan(startPos, launchDir, dmgMultiplier); + return; + } + + SetOrigin(startPos); + SetAngles(launchDir); + SetModel(m_oldModel); + + if (dmgMultiplier <= 0.0) + dmgMultiplier = 1.0f; + + if (powerMultiplier <= 0.0) + powerMultiplier = 1.0f; + + m_flDmgMultiplier = dmgMultiplier; + + /* convert absolute vel from def into relative */ + makevectors(launchDir); + moveVel = (m_vecLaunchVelocity[0] * powerMultiplier) * v_forward; + moveVel += (m_vecLaunchVelocity[1] * powerMultiplier) * v_right; + moveVel += (m_vecLaunchVelocity[2] * powerMultiplier) * v_up; + + /* fire slower underwater */ + if (pointcontents(startPos) == CONTENT_WATER) { + SetVelocity(moveVel / 10); + } else { + SetVelocity(moveVel); + } + + SetAngularVelocity(avelocity); + SetHealth(health); + SetSolid(SOLID_BBOX); + + if (m_flBounce > 0) { + SetMovetype(MOVETYPE_BOUNCE); + } else { + SetMovetype(MOVETYPE_FLYMISSILE); + } + + if (m_partSmokeFly) { + traileffectnum = particleeffectnum(m_partSmokeFly); + } + + if (GetHealth() > 0) { + SetTakedamage(DAMAGE_YES); + } else { + SetTakedamage(DAMAGE_NO); + } + + if (m_flFuse > 0) { + ScheduleThink(_FuseEnded, m_flFuse + fuseOffset); + } + + StartSoundDef(m_sndFly, CHAN_BODY, true); + SendFlags = (-1); + SendEntity = 0; /* HACK: remove this once Spike fixes CSQC-set traileffectnum etc. */ +} + +void +NSProjectile::EvaluateEntity(void) +{ + EVALUATE_VECTOR(origin, 0, PROJ_CHANGED_ORIGIN_X) + EVALUATE_VECTOR(origin, 1, PROJ_CHANGED_ORIGIN_Y) + EVALUATE_VECTOR(origin, 2, PROJ_CHANGED_ORIGIN_Z) + EVALUATE_VECTOR(angles, 0, PROJ_CHANGED_ANGLES_X) + EVALUATE_VECTOR(angles, 1, PROJ_CHANGED_ANGLES_Y) + EVALUATE_VECTOR(angles, 2, PROJ_CHANGED_ANGLES_Z) + EVALUATE_FIELD(modelindex, PROJ_CHANGED_MODELINDEX) + EVALUATE_FIELD(traileffectnum, PROJ_CHANGED_MODELINDEX) +} + +/* Make sure StartFrame calls this */ +float +NSProjectile::SendEntity(entity ePEnt, float flChanged) +{ + if (!modelindex) + return (0); + + if (clienttype(ePEnt) != CLIENTTYPE_REAL) + return (0); + + WriteByte(MSG_ENTITY, ENT_ENTITYPROJECTILE); + + /* broadcast how much data is expected to be read */ + WriteFloat(MSG_ENTITY, flChanged); + + SENDENTITY_COORD(origin[0], PROJ_CHANGED_ORIGIN_X) + SENDENTITY_COORD(origin[1], PROJ_CHANGED_ORIGIN_Y) + SENDENTITY_COORD(origin[2], PROJ_CHANGED_ORIGIN_Z) + SENDENTITY_ANGLE(angles[0], PROJ_CHANGED_ANGLES_X) + SENDENTITY_ANGLE(angles[1], PROJ_CHANGED_ANGLES_Y) + SENDENTITY_ANGLE(angles[2], PROJ_CHANGED_ANGLES_Z) + SENDENTITY_SHORT(modelindex, PROJ_CHANGED_MODELINDEX) + SENDENTITY_FLOAT(traileffectnum, PROJ_CHANGED_MODELINDEX) + + return (1); +} +#endif + +.float emiteffectnum; +#ifdef CLIENT +void +NSProjectile::ReceiveEntity(float flNew, float flChanged) +{ + READENTITY_COORD(origin[0], PROJ_CHANGED_ORIGIN_X) + READENTITY_COORD(origin[1], PROJ_CHANGED_ORIGIN_Y) + READENTITY_COORD(origin[2], PROJ_CHANGED_ORIGIN_Z) + READENTITY_ANGLE(angles[0], PROJ_CHANGED_ANGLES_X) + READENTITY_ANGLE(angles[1], PROJ_CHANGED_ANGLES_Y) + READENTITY_ANGLE(angles[2], PROJ_CHANGED_ANGLES_Z) + READENTITY_SHORT(modelindex, PROJ_CHANGED_MODELINDEX) + READENTITY_FLOAT(traileffectnum, PROJ_CHANGED_MODELINDEX) + setmodelindex(this, modelindex); + setorigin(this, origin); +} +#endif + +#ifdef CLIENT +void +NSProjectile_ReadEntity(bool new) +{ + float fl; + + NSProjectile rend = (NSProjectile)self; + if (new) { + spawnfunc_NSProjectile(); + } + + fl = readfloat(); + rend.ReceiveEntity(new, fl); +} +#endif \ No newline at end of file diff --git a/src/shared/defs.h b/src/shared/defs.h index 5d9e5d5d..d3397454 100644 --- a/src/shared/defs.h +++ b/src/shared/defs.h @@ -66,6 +66,7 @@ string __fullspawndata; #include "NSProjectile.h" #include "NSItem.h" #include "NSSpraylogo.h" +#include "NSPortal.h" #include "../xr/defs.h" #include "NSClient.h" diff --git a/src/shared/entities.h b/src/shared/entities.h index c7b3ede2..87a05895 100644 --- a/src/shared/entities.h +++ b/src/shared/entities.h @@ -20,6 +20,7 @@ typedef enum ENT_NONE = 0, /**< invalid, but reserved. */ ENT_ENTITY, /**< of type NSEntity */ ENT_ENTITYRENDERABLE, /**< of type NSRenderableEntity */ + ENT_ENTITYPROJECTILE, /**< of type NSProjectile */ ENT_SURFPROP, /**< of type NSSurfacePropEntity */ ENT_PHYSICS, /**< of type NSPhysicsEntity */ ENT_MONSTER, /**< of type NSMonster */ @@ -27,6 +28,7 @@ typedef enum ENT_PLAYER, /**< of type NSClientPlayer */ ENT_WEAPON, /**< of type NSWeapon */ ENT_SPECTATOR, /**< of type NSClientSpectator */ + ENT_PORTAL, /**< of type NSPortal */ ENT_AMBIENTSOUND, /**< of type ambient_generic */ ENT_BEAM, /**< of type env_beam */ ENT_DLIGHT, /**< of type light_dynamic */ diff --git a/src/shared/include.src b/src/shared/include.src index 8bf30820..fb8d892f 100644 --- a/src/shared/include.src +++ b/src/shared/include.src @@ -17,6 +17,7 @@ NSMonster.qc NSTalkMonster.qc NSProjectile.qc NSItem.qc +NSPortal.qc NSClient.qc NSClientSpectator.qc diff --git a/src/shared/pmove_custom.qc b/src/shared/pmove_custom.qc index f503fb5c..cc5ccb84 100644 --- a/src/shared/pmove_custom.qc +++ b/src/shared/pmove_custom.qc @@ -581,6 +581,11 @@ PMoveCustom_Move(void) self.origin = trace_endpos; continue; } + } else { + if (trace_ent.solid == SOLID_PORTAL) { + NSPortal portalEntry = (NSPortal)trace_ent; + portalEntry.TransportEntity((NSEntity)self); + } } } @@ -597,6 +602,10 @@ PMoveCustom_Move(void) if (trace_fraction == 1.0) { return; + } else if (trace_ent.solid == SOLID_PORTAL) { + NSPortal portalEntry = (NSPortal)trace_ent; + portalEntry.TransportEntity((NSEntity)self); + return; } /*if (trace_startsolid) {