• [Source SDK] How to create a rope?
    5 replies, posted
I know this is a dumb question but considering the utter lack of documentation, guides and examples for Source SDK I hope I'll be forgiven for asking this, especially here. I'm trying to create a rope between 2 positions. If I'm not mistaken the constraints system in Source SDK requires entities which is fine, the constraint should be placed between the player (Singleplayer) and whatever object they shoot at (could the world or any other entity). I'm looking to do this in C++, not Hammer. Any help or links to examples or docs or whatever would be super helpful.
Example from the HL2 Crane: [url]https://github.com/ValveSoftware/source-sdk-2013/blob/master/mp/src/game/server/hl2/vehicle_crane.cpp#L206[/url] CRopeKeyframe::Create [url]https://github.com/ValveSoftware/source-sdk-2013/blob/master/mp/src/game/server/rope.h#L31[/url]
Oh wow I cant believe I missed that. I even tried using some of the code from the crane. Just out of curiousity, what happens if one of the entities is NULL/world? I'd like to position the end point at a specific spot if thats possible? I tried using [URL="https://github.com/ValveSoftware/source-sdk-2013/blob/master/mp/src/public/vphysics_interface.h#L578"]IPhysicsEnvironment::CreateLengthConstraint[/URL] and [URL="https://github.com/ValveSoftware/source-sdk-2013/blob/master/mp/src/public/vphysics/constraints.h#L275"]constraint_lengthparams_t[/URL] which let me specify the entities and positions except it doesnt work (at least how I'm using it).
[CODE] trace_t trace; UTIL_TraceLine(...); IPhysicsObject *pPhysicsObject = NULL; // GetWorldEntity()->VPhysicsGetObject() seems to return NULL - use g_PhysWorldObject instead if (pHitEntity->IsWorld()) { pPhysicsObject = g_PhysWorldObject; } else { pPhysicsObject = pHitEntity->VPhysicsGetObject(); } constraint_groupparams_t group; group.Defaults(); IPhysicsConstraintGroup *pConstraintGroup = physenv->CreateConstraintGroup(group); constraint_lengthparams_t length; length.Defaults(); length.InitWorldspace(pOtherEntity->VPhysicsGetObject(), pPhysicsObject, pOtherEntity->GetAbsOrigin(), trace.endpos); length.constraint.Defaults(); IPhysicsConstraint *pConstraint = physenv->CreateLengthConstraint(pOtherEntity->VPhysicsGetObject(), pPhysicsObject, pConstraintGroup, length); pConstraintGroup->Activate(); [/CODE] Attaching the player to the world does not seem to be possible
Damn. This is related to the other thread you helped me with where I'd like the grapple hook to let you swing instead of only push or pull.
I hate to bumb this but I'm still having problems getting the constraint to work. I'm using a modified (and working) version of the [URL="https://github.com/MaestroFenix/NewGrappleHook"]NewGrappleHook[/URL]. Instead of push/pull functionality, I'd like it to allow me to swing where I shoot a position, it "hooks" into it and I can swing around on it. Here is the code I'm using. It is server-side only and uses the default weapon script from the New Grapple Hook. The constraint is created in [b]CGrappleHook::HookCreate[/b]. [b]weapon_grapple.h[/b] [code] //========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: Implements the grapple hook weapon. // // Primary attack: fires a beam that hooks on a surface. // Secondary attack: switches between pull and rapple modes // // //=============================================================================// #ifndef WEAPON_GRAPPLE_H #define WEAPON_GRAPPLE_H #ifdef _WIN32 #pragma once #endif #include "basehlcombatweapon_shared.h" #include "basecombatcharacter.h" #include "beam_shared.h" #include "sprite.h" #include "vphysics/constraints.h" //----------------------------------------------------------------------------- // CWeaponGrapple //----------------------------------------------------------------------------- class CWeaponGrapple : public CBaseHLCombatWeapon { DECLARE_CLASS(CWeaponGrapple, CBaseHLCombatWeapon); public: enum class State { WIND_IN, SWING }; public: CWeaponGrapple(); ~CWeaponGrapple(); virtual void Precache() override; virtual void PrimaryAttack() override; virtual void SecondaryAttack() override; virtual void Drop(const Vector &vecVelocity) override; virtual bool Holster(CBaseCombatWeapon *pSwitchingTo = nullptr) override; virtual void ItemPostFrame() override; virtual bool IsWeaponZoomed() override { return false; } State getState() const { return m_eState; } private: CHandle<CSprite> m_hLightGlow = nullptr; CHandle<CBaseEntity> m_hHook = nullptr; State m_eState = State::SWING; void drawBeam(const Vector& rkoStart, const Vector& rkoEnd, const float kfWidth); void fireHook(const trace_t& rkoTrace); public: CHandle<CBeam> m_hBeam = nullptr; void destroyHook(); }; //----------------------------------------------------------------------------- // Grapple Hook //----------------------------------------------------------------------------- class CGrappleHook final : public CBaseCombatCharacter { DECLARE_CLASS(CGrappleHook, CBaseCombatCharacter); public: Class_T Classify() final { return CLASS_NONE; } public: static CGrappleHook *HookCreate(const trace_t& rkoTrace, CWeaponGrapple* poOwner); public: IPhysicsConstraintGroup* m_pConstraintGroup = nullptr; IPhysicsConstraint* m_pConstraint = nullptr; ~CGrappleHook(); void Spawn() final; void Precache() final; void HookThink(); void HookTouch(CBaseEntity *pOther); protected: DECLARE_DATADESC(); CHandle<CWeaponGrapple> m_hGrappleWeapon = nullptr; }; #endif // WEAPON_GRAPPLE_H [/code] [b]weapon_grapple.cpp[/b] [code] //========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #include "cbase.h" #include "gamestats.h" #include "in_buttons.h" #include "world.h" #include "weapon_grapple.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #define HOOK_MODEL "models/props_junk/rock001a.mdl" const int kiAirVelocity = 3500; const int kiWaterVelocity = 1500; //----------------------------------------------------------------------------- // Grapple Hook //----------------------------------------------------------------------------- LINK_ENTITY_TO_CLASS(grapple_hook, CGrappleHook); BEGIN_DATADESC(CGrappleHook) // Function Pointers DEFINE_FUNCTION(HookThink), DEFINE_FUNCTION(HookTouch), END_DATADESC() CGrappleHook *CGrappleHook::HookCreate(const trace_t& rkoTrace, CWeaponGrapple* poOwner) { // Create a new entity with CGrappleHook private data CGrappleHook *pHook = (CGrappleHook *)CreateEntityByName("grapple_hook"); pHook->SetAbsOrigin(rkoTrace.endpos); pHook->Spawn(); pHook->m_hGrappleWeapon = poOwner; // Create constraint group constraint_groupparams_t oGroupParams; oGroupParams.Defaults(); pHook->m_pConstraintGroup = physenv->CreateConstraintGroup(oGroupParams); // Create constraint IPhysicsObject* pPhysPly = poOwner->GetOwner()->VPhysicsGetObject(); IPhysicsObject* pPhysObj = g_PhysWorldObject; Vector oAttachPos = rkoTrace.endpos; if (rkoTrace.m_pEnt != nullptr && UTIL_IsValidEntity(rkoTrace.m_pEnt) && rkoTrace.m_pEnt->VPhysicsGetObject() != nullptr) { pPhysObj = rkoTrace.m_pEnt->VPhysicsGetObject(); pPhysObj->WorldToLocal(&oAttachPos, rkoTrace.endpos); } constraint_lengthparams_t oLengthParams; oLengthParams.Defaults(); oLengthParams.InitWorldspace(pPhysPly, pPhysObj, Vector(), oAttachPos); oLengthParams.constraint.Defaults(); pHook->m_pConstraint = physenv->CreateLengthConstraint(pPhysPly, pPhysObj, pHook->m_pConstraintGroup, oLengthParams); pHook->m_pConstraint->Deactivate(); pHook->m_pConstraintGroup->Activate(); return pHook; } CGrappleHook::~CGrappleHook() { if (m_hGrappleWeapon != nullptr) { CBasePlayer* poOwner = ToBasePlayer(m_hGrappleWeapon->GetOwner()); if (poOwner != nullptr && UTIL_IsValidEntity(poOwner)) { poOwner->SetPhysicsFlag(PFLAG_VPHYSICS_MOTIONCONTROLLER, false); } } if (m_pConstraint != nullptr) { physenv->DestroyConstraint(m_pConstraint); } if (m_pConstraintGroup != nullptr) { physenv->DestroyConstraintGroup(m_pConstraintGroup); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CGrappleHook::Spawn() { Precache(); SetModel(HOOK_MODEL); SetMoveType(MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM); UTIL_SetSize(this, -Vector(0.3f, 0.3f, 0.3f), Vector(0.3f, 0.3f, 0.3f)); SetSolid(SOLID_BBOX); SetGravity(0.05f); AddEffects(EF_NODRAW); // The rock is invisible, the crossbow bolt is the visual representation // Make sure we're updated if we're underwater UpdateWaterState(); SetTouch(&CGrappleHook::HookTouch); SetThink(&CGrappleHook::HookThink); SetNextThink(gpGlobals->curtime + 0.1f); } void CGrappleHook::Precache() { PrecacheModel(HOOK_MODEL); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CGrappleHook::HookTouch(CBaseEntity *pOther) { if (pOther->IsSolid() == false || pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS)) return; CWeaponGrapple* poGrapple = m_hGrappleWeapon; trace_t tr; tr = BaseClass::GetTouchTrace(); EmitSound("Weapon_AR2.Reload_Push"); // if what we hit is static architecture, can stay around for a while. Vector vecDir = GetAbsVelocity(); //FIXME: We actually want to stick (with hierarchy) to what we've hit SetMoveType(MOVETYPE_NONE); SetTouch(NULL); VPhysicsDestroyObject(); VPhysicsInitNormal(SOLID_BBOX, FSOLID_NOT_STANDABLE, false); AddSolidFlags(FSOLID_NOT_SOLID); CBasePlayer* poOwner = ToBasePlayer(poGrapple->GetOwner()); Assert(poOwner != nullptr); // Set Jay's gai flag poOwner->SetPhysicsFlag(PFLAG_VPHYSICS_MOTIONCONTROLLER, true); IPhysicsObject *pRootPhysObject = VPhysicsGetObject(); Assert(pRootPhysObject); pRootPhysObject->EnableMotion(false); pRootPhysObject->SetMass(VPHYSICS_MAX_MASS); // Root has huge mass, tip has little SetThink(&CGrappleHook::HookThink); SetNextThink(gpGlobals->curtime + 0.1f); UTIL_ImpactTrace(&tr, DMG_BULLET); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CGrappleHook::HookThink() { CBasePlayer* poPlayer = ToBasePlayer(m_hGrappleWeapon->GetOwner()); SetNextThink(gpGlobals->curtime + 0.05f); //set next globalthink m_hGrappleWeapon->m_hBeam->SetAbsEndPos(GetAbsOrigin()); m_hGrappleWeapon->m_hBeam->RelinkBeam(); poPlayer->SetGravity(0.0f); poPlayer->SetGroundEntity(NULL); if (m_hGrappleWeapon->getState() == CWeaponGrapple::State::WIND_IN) { //All of this push the player far from the hook Vector tempVec1 = poPlayer->GetAbsOrigin() - GetAbsOrigin(); VectorNormalize(tempVec1); poPlayer->SetAbsVelocity(tempVec1 * 400 * -1); } } //----------------------------------------------------------------------------- // CWeaponGrapple //----------------------------------------------------------------------------- LINK_ENTITY_TO_CLASS(weapon_grapple, CWeaponGrapple); PRECACHE_WEAPON_REGISTER(weapon_grapple); //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- CWeaponGrapple::CWeaponGrapple() { m_bReloadsSingly = true; m_bFiresUnderwater = true; m_bAltFiresUnderwater = true; } #define CROSSBOW_GLOW_SPRITE "sprites/light_glow02_noz.vmt" #define CROSSBOW_GLOW_SPRITE2 "sprites/blueflare1.vmt" CWeaponGrapple::~CWeaponGrapple() { destroyHook(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponGrapple::Precache() { UTIL_PrecacheOther("grapple_hook"); PrecacheModel("sprites/physbeam.vmt"); PrecacheModel("sprites/physcannon_bluecore2b.vmt"); BaseClass::Precache(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponGrapple::PrimaryAttack() { if (m_hHook != nullptr) return; m_iPrimaryAttacks++; CBasePlayer *pPlayer = ToBasePlayer(GetOwner()); gamestats->Event_WeaponFired(pPlayer, true, GetClassname()); WeaponSound(SINGLE); pPlayer->DoMuzzleFlash(); trace_t tr; Vector vecShootOrigin, vecShootDir, vecDir, vecEnd; AngleVectors(pPlayer->EyeAngles(), &vecDir); //Gets the direction where the player is aiming vecShootOrigin = pPlayer->Weapon_ShootPosition(); //Gets the position of the player vecEnd = vecShootOrigin + (vecDir * MAX_TRACE_LENGTH); //Gets the position where the hook will hit //Traces a line between the two vectors UTIL_TraceLine(vecShootOrigin, vecEnd, MASK_SHOT, pPlayer, COLLISION_GROUP_NONE, &tr); // Creates an energy impact effect and fires the hook if we don't hit the sky if ((tr.surface.flags & SURF_SKY) != SURF_SKY) { CPVSFilter filter(tr.endpos); te->GaussExplosion(filter, 0.0f, tr.endpos, tr.plane.normal, 0); m_hLightGlow = CSprite::SpriteCreate("sprites/physcannon_bluecore2b.vmt", GetAbsOrigin(), TRUE); //Makes a sprite at the end of the beam m_hLightGlow->SetTransparency(9, 255, 255, 255, 200, kRenderFxNoDissipation); //Sets FX render and color m_hLightGlow->SetAbsOrigin(tr.endpos); //Sets the position m_hLightGlow->SetBrightness(255); //Bright m_hLightGlow->SetScale(0.65); //Scale drawBeam(vecShootOrigin, tr.endpos, 15.5); //Draws the beam fireHook(tr); m_flNextPrimaryAttack = 0.0f; m_flNextSecondaryAttack = gpGlobals->curtime + 0.75f; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponGrapple::SecondaryAttack(void) { if (m_eState == State::WIND_IN) { m_eState = State::SWING; } else { m_eState = State::WIND_IN; } WeaponSound(EMPTY); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponGrapple::Drop(const Vector &vecVelocity) { destroyHook(); BaseClass::Drop(vecVelocity); } //----------------------------------------------------------------------------- // Purpose: // Input : *pSwitchingTo - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CWeaponGrapple::Holster(CBaseCombatWeapon *pSwitchingTo) { destroyHook(); return BaseClass::Holster(pSwitchingTo); } void CWeaponGrapple::ItemPostFrame() { CBasePlayer *pOwner = ToBasePlayer(GetOwner()); if ((pOwner->m_nButtons & IN_ATTACK) == IN_ATTACK) { if (m_flNextPrimaryAttack < gpGlobals->curtime) { PrimaryAttack(); } } // Change this to else-if if we dont want the player to be able to switch states while they are grappling if ((pOwner->m_afButtonPressed & IN_ATTACK2) == IN_ATTACK2) { if (m_flNextPrimaryAttack < gpGlobals->curtime) { SecondaryAttack(); } } if (m_hHook != nullptr) { if ((pOwner->m_nButtons & IN_ATTACK) != IN_ATTACK) { destroyHook(); } } } void CWeaponGrapple::drawBeam(const Vector& rkoStart, const Vector& rkoEnd, const float kfWidth) { m_hBeam = CBeam::BeamCreate("sprites/physbeam.vmt", 15.5); //Draw the main beam shaft m_hBeam->SetStartPos(rkoStart); // It starts at startPos m_hBeam->PointEntInit(rkoEnd, this); // This sets up some things that the beam uses to figure out where it should start and end m_hBeam->SetEndAttachment(LookupAttachment("Muzzle")); // This makes it so that the beam appears to come from the muzzle of the pistol m_hBeam->SetWidth(kfWidth); m_hBeam->SetBrightness(255); // Higher brightness means less transparent m_hBeam->RelinkBeam(); float scrollOffset = gpGlobals->curtime + 5.5; m_hBeam->SetScrollRate(scrollOffset); //Sets scrollrate of the beam sprite UpdateWaterState(); SetNextThink(gpGlobals->curtime + 0.1f); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CWeaponGrapple::fireHook(const trace_t& rkoTrace) { CBasePlayer *pOwner = ToBasePlayer(GetOwner()); if (pOwner == nullptr) return; Vector vecAiming = pOwner->GetAutoaimVector(0); QAngle angAiming; VectorAngles(vecAiming, angAiming); m_hHook = CGrappleHook::HookCreate(rkoTrace, this); if (pOwner->GetWaterLevel() == 3) { m_hHook->SetAbsVelocity(vecAiming * kiWaterVelocity); } else { m_hHook->SetAbsVelocity(vecAiming * kiAirVelocity); } pOwner->ViewPunch(QAngle(-2, 0, 0)); //WeaponSound( SINGLE ); //WeaponSound( SPECIAL2 ); m_flNextPrimaryAttack = gpGlobals->curtime + 0.75; m_flNextSecondaryAttack = gpGlobals->curtime + 0.75; } void CWeaponGrapple::destroyHook() { if (m_hHook != nullptr) { m_hHook->SetTouch(nullptr); m_hHook->SetThink(nullptr); UTIL_Remove(m_hHook); m_hHook = nullptr; } if (m_hBeam != nullptr) { UTIL_Remove(m_hBeam); //Kill beam m_hBeam = nullptr; UTIL_Remove(m_hLightGlow); //Kill sprite m_hLightGlow = nullptr; } } [/code] Any and all help is appreciated especially since Source is a horrible engine.
Sorry, you need to Log In to post a reply to this thread.