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.