Skip to content

PR to add Treyarc Function of ViewmodelFX and further improve!#360

Merged
wroyca merged 12 commits into
iw4x:mainfrom
neoXclusive:viewmodel-fx-support
May 23, 2026
Merged

PR to add Treyarc Function of ViewmodelFX and further improve!#360
wroyca merged 12 commits into
iw4x:mainfrom
neoXclusive:viewmodel-fx-support

Conversation

@neoXclusive

@neoXclusive neoXclusive commented Jan 30, 2026

Copy link
Copy Markdown
Contributor

ALL CREDITS TO ANTIGA @EINFLICTOR

Viewmodel Fx at its current state only works on local host.

Pr is to get this code out into the world! Like i said for now Fx is only playing on local client, for all clients to see their own viewmodelfx in an online server this code still needs work. This is what im hoping to achieve in the end goal of releasing the code!

Function naming could probably be better on a release version if it ever gets improved.

heres some base gsc so you can see how the functionality works within gsc.

If you are interested in reviewing code and need a fast mod to test on, ill send the tesla gun mod your way. Just dm on cord @myluckyneo

Init()
{
	level.tesla_gun = "t4_dg2_mp";
	replaceFunc( maps\mp\gametypes\_gamelogic::matchStartTimerPC, maps\mp\gametypes\_gamelogic::matchStartTimerSkip );
	level thread onPlayerConnected();
	
	level._teslaFX = [];

	level._teslaFX["tesla_viewmodel_view"]  = "sk/t5/weapons/tesla/fx_zombie_tesla_tube_view";
	level._teslaFX["tesla_viewmodel_view2"] = "sk/t5/weapons/tesla/fx_zombie_tesla_tube_view2";
	level._teslaFX["tesla_viewmodel_view3"] = "sk/t5/weapons/tesla/fx_zombie_tesla_tube_view3";
	level._teslaFX["tesla_viewmodel_rail"] = "sk/t5/weapons/tesla/fx_zombie_tesla_rail_view";
}

onPlayerConnected()
{
	for( ;; )
	{
		level waittill( "connected", player );
		player thread bulbFX();
		player thread afterShotFX();
		player thread railIdleFX();
	    player thread onPlayerSpawned();
    }
}

// Called on player spawn (server)
onPlayerSpawned()
{
    self endon("disconnect");

    self waittill("spawned_player");

    // Give weapon (server-side)
    self takeAllWeapons();
    self giveWeapon(level.tesla_gun);
    wait 1;
    self switchToWeapon(level.tesla_gun);
}

railIdleFX()
{
	level endon("game_ended");
	for (;;)
	{
		wait 10;
		PVMFX_BOTH(level._teslaFX["tesla_viewmodel_rail"], "tag_flash");
	}
}

bulbFX()
{
    level endon("game_ended");

    for (;;)
    {
		
        wait 0.1;
		if( self getCurrentWeapon() != level.tesla_gun )
			continue;

        ammo = self GetWeaponAmmoClip( self getCurrentWeapon() );

        switch(ammo)
        {
            case 3:
                PVMFX_BOTH(level._teslaFX["tesla_viewmodel_view"], "tag_brass");
                break;
            case 2:
                PVMFX_BOTH(level._teslaFX["tesla_viewmodel_view2"], "tag_brass");
                break;
            case 1:
                PVMFX_BOTH(level._teslaFX["tesla_viewmodel_view3"], "tag_brass");
                break;
		
        }
    }
}
afterShotFX()
{
    level endon("game_ended");
    self endon("disconnect");

    for (;;)
    {
        self waittill("weapon_fired", weapon);

        if (weapon == level.tesla_gun)
        {
            wait 0.1;
            PVMFX_BOTH(level._teslaFX["tesla_viewmodel_rail"], "tag_flash");
            IPrintLnBold(">> [TESLA DEBUG] Local viewmodel FX played for ", self.name);
        }
    }
}

@neoXclusive neoXclusive changed the title PR to add Treyarc Function of PVMFX_BOTH further improve Viewmodel FX in iw4x! PR to add Treyarc Function of Viewmodel further improve ViewmodelFX in iw4x! Jan 30, 2026
@neoXclusive neoXclusive changed the title PR to add Treyarc Function of Viewmodel further improve ViewmodelFX in iw4x! PR to add Treyarc Function of ViewmodelFX further improve ViewmodelFX in iw4x! Jan 30, 2026
@neoXclusive neoXclusive changed the title PR to add Treyarc Function of ViewmodelFX further improve ViewmodelFX in iw4x! PR to add Treyarc Function of ViewmodelFX and further improve! Jan 30, 2026
Comment thread src/Components/Modules/ViewModelFx.cpp
Comment thread src/Components/Modules/ViewModelFx.cpp Outdated
Comment on lines +78 to +80
Game::CG_PlayBoltedEffect(0, fx, fpDObjHandle, tagName);

Game::CG_StopBoltedEffect(0, 0, fpDObjHandle, tagName);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: What is the intent here? The effect is played and then immediately stopped in the next instruction, which would likely result in no visible effect. Is this intentional, or is there some non-obvious detail about how GSC interacts with the engine that I'm missing?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iirc CG_PlayBoltedEffect starts the fx in the viewport but without CG_StopBoltedEffect i know it continues to play fx forever (rather than say an fx is only released on reload and stops when its loaded)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is GSC suppose to be calling stop vfx at a certain point, and not the engine? CG_StopBoltedEffect seems like what you would call after the fx needs to be done with, but this function could be used for general purpose and not only wunderwoff

Comment thread src/Game/Functions.hpp
typedef const DObj*(*CG_GetBoneIndex_t)(int localClientNum, unsigned int boneName, char* boneIndex);
extern CG_GetBoneIndex_t CG_GetBoneIndex;

typedef int(__cdecl* DObjGetBoneIndex_t)(int a1, int a2, unsigned char* a3);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: I believe a1 is probably const DObj* dobj or DObj* dobj ?


namespace Components::ViewModelFxSetup
{
static char cg_weaponsArray[32 * Weapon::WEAPON_LIMIT];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never used?

@@ -0,0 +1,10 @@
#pragma once

namespace Components::ViewModelFxSetup

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
namespace Components::ViewModelFxSetup
namespace Components

@wroyca

wroyca commented Jan 30, 2026

Copy link
Copy Markdown
Member

Thank you for the PR!

I've only had a brief look so far and left comments on a few obvious things I noticed here and there. Hopefully I'll be able to spend more time reviewing it in depth as time allows.

One major point I'm still unsure about is the GSC naming. For example, setanim feels very generic. I'll need to gather some additional opinions on that, especially since I'm not directly involved in these parts.

Overall, there isn't much more for me to add at the moment, but thanks again for the contribution 🫡

@neoXclusive

Copy link
Copy Markdown
Contributor Author

Setanim was kept on accident and has been removed this isn't a needed function for VMFX 😎. Thanks for your time, hopefully antiga may be able to pop in and give a bit more insight soon.

@mjkzy mjkzy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool pr neo, i just dropped my 2 thoughts here if u wanted feedback :)

Game::CG_PlayBoltedEffect(0, fx, dobjHandle, tagName);
});

GSC::Script::AddFunction("PVMFX_BOTH", []

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for what it's worth, this naming convention is really weird compared to the usual tradition of IW4x builtins and such. PVMFX_BOTH seems to do what PlayViewmodelFX but just plays on both hands instead, and instantly stops the bolt fx and playing it. i think the PVMFX_BOTH function could just be built into PlayViewmodelFX, and you can add a boolean 3rd (index 2) parameter for doing both hands, or if u need left hand only, just add a "hand index" parameter and then maybe both hands boolean still?

Comment thread src/Game/Functions.cpp
CG_WeaponDObjHandle_t CG_WeaponDObjHandle = reinterpret_cast<CG_WeaponDObjHandle_t>(0x41DB70);
CG_StopBoltedEffect_t CG_StopBoltedEffect = reinterpret_cast<CG_StopBoltedEffect_t>(0x44C230);


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

redudant space here

@wroyca

wroyca commented May 22, 2026

Copy link
Copy Markdown
Member

There are things I'm not happy with here, but let's not be pedantic, we can look at them on a second pass

@wroyca wroyca marked this pull request as ready for review May 22, 2026 13:25
@wroyca wroyca force-pushed the viewmodel-fx-support branch from df5ba18 to 027b24f Compare May 22, 2026 13:25
@wroyca

wroyca commented May 22, 2026

Copy link
Copy Markdown
Member

@neoXclusive can you fix the build? :)

@wroyca wroyca merged commit 71d9570 into iw4x:main May 23, 2026
2 checks passed
@mjkzy

mjkzy commented May 23, 2026

Copy link
Copy Markdown
Contributor

i still feel as if the naming conventions for these gsc functions are really odd and is just duplicate code almost, no second pass?

@wroyca

wroyca commented May 23, 2026

Copy link
Copy Markdown
Member

i still feel as if the naming conventions for these gsc functions are really odd and is just duplicate code almost, no second pass?

Yes, second pass is very much welcome, see #360 (comment) :)

@neoXclusive

Copy link
Copy Markdown
Contributor Author

"> i still feel as if the naming conventions for these gsc functions are really odd and is just duplicate code almost, no second pass?"

I will make a second pass and rename functions. This still needs work so i will put the time in to get these updated asap. Thanks M

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants