Author Topic: A couple of visual effects  (Read 881 times)

abgar

  • Full Member
  • ***
  • Posts: 114
    • View Profile
A couple of visual effects
« on: May 12, 2016, 03:00:09 AM »
Hi guys,
Hoping someone can help with a couple of visual effects....
1 - Create a 'ball' of smoke that shoots out from a player for a certain distance (will need to be able to check if any players come into contact with this) - if possible, would be great to be able to change the colour of the smoke, too, and also to change the velocity that the smoke balls shoots out from the client...

2 - Im changing the model of a player to a cardboard box while they're crouching.  The only problem is the box model effectively disappears under ground when crouched.  Is there any way to offset the model position?  Assuming i might need to create a pro-dynamic and parent it to the client, whilst making the client completely invis - but i don't know how to create the prop-dynamic and parent it.   

Anyone feel like a challenge with these two? 

Thanks heaps in advance :)

Revan

  • Hero Member
  • *****
  • Posts: 513
    • View Profile
Re: A couple of visual effects
« Reply #1 on: May 13, 2016, 03:57:28 AM »
1. I think the best way to achieve what you want is to create a env_smokestack particle (take a look at CreateParticle) set the position and apply the correct velocity to shoot it into the direction you want. If you want to detect collisions you'd have to do the calculations on your own (GetVectorDistance and friends are the way to go because you can't detect collisions with a env_smokestack entity) - it's not 100% accurate but will do the job just fine.

2. There are multiple ways to do this but parenting a prop_dynamic is definitely the best (using TE_SetupGlowSprite and the like that work via the server exclusevily doesn't look as nice as a parented prop_dynamic which is networked from server to client and thus results in a nice smoothness of the effect). You should create the prop_dynamic on spawn to avoid creating and deleting it every time a player crouches/uncrouches. To show/hide it I think you can use the TurnOn/TurnOff Inputs respectively.
Some code to give you an idea:
Code: [Select]
int g_cardboardEntity[MAXPLAYERSCUSTOM];
onPlayerSpawn(int client)
{
  int ent = CreateEntityByName("prop_dynamic_override");
  DispatchKeyValue(ent, "model", "cardboard.mdl"); //set model (and don't forget to call PrecacheModel with it in OnMapStart)
  SetEntProp(ent, Prop_Data, "m_CollisionGroup", 2);
  SetEntPropEnt(ent, Prop_Send, "m_hOwnerEntity", client);

  DispatchSpawn(ent);

  TeleportEntity(ent, or, ang, NULL_VECTOR);

  SetVariantString("!activator");
  AcceptEntityInput(ent, "SetParent", client, ent, 0);

  SetVariantString("forward"); //not sure if this attachment exists in CS:GO
  AcceptEntityInput(ent, "SetParentAttachmentMaintainOffset", ent, ent, 0);

  g_cardboardEntity[client] = ent;
}

onPlayerCrouch(int client, bool isCrouching)
{
  int ent = g_cardboardEntity[client];
  if(IsValidEntity(ent)) {
    if(isCrouching)
      AcceptEntityInput(ent, "TurnOn");
    else
      AcceptEntityInput(ent, "TurnOff");
  }
}

onPlayerChangesRaceOrDies(int client)
{
  int ent = g_cardboardEntity[client];
  if(IsValidEntity(ent)) {
    AcceptEntityInput(ent, "Kill");
  }
}
#war3source on gamesurge

abgar

  • Full Member
  • ***
  • Posts: 114
    • View Profile
Re: A couple of visual effects
« Reply #2 on: May 17, 2016, 10:41:26 PM »
Awesome, thanks Revan.

I'll take a look at the Prop-Dynamic tonight and test it out.

With the env_smokestack though, how do i set velocity on that to make it move?  It looks like there's no pre-defined key value, so would this need to be parented to something else as well?

abgar

  • Full Member
  • ***
  • Posts: 114
    • View Profile
Re: A couple of visual effects
« Reply #3 on: May 18, 2016, 12:54:43 AM »
Cardboard Box now working exactly as required :)

Revan, thanks so much mate :)

abgar

  • Full Member
  • ***
  • Posts: 114
    • View Profile
Re: A couple of visual effects
« Reply #4 on: May 18, 2016, 10:08:09 PM »
So, here's my smoke code to create the smoke ball. 
Smoke creates correctly, in the right spot etc, but how can i move this entity?  I've tried using PushEntToVector, however that didn't work for the smokeball - didn't move anything at all....
Can someone give me some guidance please?  Im not great with creating entities, or getting them to move LOL.

Thanks :)

Code: [Select]
CreateEnvSmokeStack(client)
{
new SmokeEnt = CreateEntityByName("env_smokestack");
if(IsValidEdict(SmokeEnt) && IsClientInGame(client))
{
new Float:StartPos[3]; GetClientAbsOrigin(client, StartPos); StartPos[2]+=40.0;

new String:originData[64];
Format(originData, sizeof(originData), "%f %f %f", StartPos[0], StartPos[1], StartPos[2]);

new String:SName[128];
Format(SName, sizeof(SName), "Smoke%i", client);
DispatchKeyValue(SmokeEnt,"targetname", SName);
DispatchKeyValue(SmokeEnt,"Origin", originData);
DispatchKeyValue(SmokeEnt,"BaseSpread", "30");
DispatchKeyValue(SmokeEnt,"SpreadSpeed", "30");
DispatchKeyValue(SmokeEnt,"Speed", "50");
DispatchKeyValue(SmokeEnt,"StartSize", "50");
DispatchKeyValue(SmokeEnt,"EndSize", "50");
DispatchKeyValue(SmokeEnt,"Rate", "30");
DispatchKeyValue(SmokeEnt,"JetLength", "10");
DispatchKeyValue(SmokeEnt,"Twist", "10");
DispatchKeyValue(SmokeEnt,"RenderColor", "255 255 51");
DispatchKeyValue(SmokeEnt,"RenderAmt", "100");
DispatchKeyValue(SmokeEnt,"SmokeMaterial", "particle/particle_smokegrenade1.vmt");

DispatchSpawn(SmokeEnt);
SetVariantString("!activator");
AcceptEntityInput(SmokeEnt, "SetParent", client, SmokeEnt, 0);
AcceptEntityInput(SmokeEnt, "TurnOn");

CreateTimer(15.0,StopSmoke,SmokeEnt);
}
}

public Action:StopSmoke(Handle:timer, any:SmokeEnt)
{
if(IsValidEdict(SmokeEnt))
{
AcceptEntityInput(SmokeEnt, "Kill");
}
}

Revan

  • Hero Member
  • *****
  • Posts: 513
    • View Profile
Re: A couple of visual effects
« Reply #5 on: May 19, 2016, 12:49:01 PM »
I think you're correct, it doesn't look like env_smokestack got a m_vecVelocity netprop that could be used to propel it to a direction on its own.
A simple way to move the entity is to TeleportEntity the env_smokestack a few units every few seconds so it looks like it's moving(this should be pretty easy to do and okay looking)

(...) It looks like there's no pre-defined key value, so would this need to be parented to something else as well?
This is actually a really great idea! You could create a prop (invisible if you want) and PushEntToVector it into the desired direction. The prop (and thus the env_smokestack) will actually bounce of walls because it is a physical entity which would be a quite cool feature - not sure if you want this tho.
To do this just start with the code from the cardboard effect (but don't parent the prop to the player) and attach the smokestack to the prop instead (War3Source_Effects has some cool stuff, like ModifyEntityAttach to make this easier), then you can use PushEntToVector which should do the trick! :)
#war3source on gamesurge

abgar

  • Full Member
  • ***
  • Posts: 114
    • View Profile
Re: A couple of visual effects
« Reply #6 on: May 20, 2016, 12:37:25 AM »
Thanks Revan,

For some reason, the prop_dynamic wouldn't allow me to teleport it (or maybe i set it up wrong.)
However, i did get it to work (sort of) using a prop_physics (code below).
Only problem i have now is that when the entity is fire, it projects forward, but gravity takes hold of it, and it acts more like a grenade throw.  Is there a way to keep it at the same height whilst it's moving?  AND, is there a way to get it to move through walls, by any chance?  I had a look at the Collission groups, but can't see anything that would allow it go through wallls...

Also, can you give me a working example of how to use PushEntToVector, please?  I've tried it a few different ways, but nothing actually happens with it at all (nothing get's moved) so im assuming it's my implementation that's wrong?

Here's my code so far - so close to be exactly what i need :)
Code: [Select]
CreateEnvSmokeStack(client)
{
new SmokeEnt = CreateEntityByName("env_smokestack");
new PropPhysEnt = CreateEntityByName("prop_physics");
if(IsValidEdict(SmokeEnt) && IsValidEdict(PropPhysEnt) && IsClientInGame(client))
{
new Float:ResultingPos[3], Float:AngleVector[3];
new Float:clientAbsPos[3]; GetClientAbsOrigin(client, clientAbsPos); clientAbsPos[2]+=40.0;
new Float:clientAbsAng[3]; GetClientEyeAngles(client, clientAbsAng);
new Float:clientEyePos[3]; GetClientEyePosition(client, clientEyePos);
new String:originData[64];

// GET THE ANGLE, VECTOR, SPEED OF THE PHYSICS ENTITY
GetAngleVectors(clientAbsAng, AngleVector, NULL_VECTOR, NULL_VECTOR);
NormalizeVector(AngleVector, AngleVector);
AddVectors(clientEyePos, AngleVector, ResultingPos);
NormalizeVector(AngleVector, AngleVector);
ScaleVector(AngleVector, 1000.0);

// SETUP PROP_PHSYICS ENT
DispatchKeyValue(PropPhysEnt, "model", SmokeEntModel);
SetEntProp(PropPhysEnt, Prop_Send, "m_fEffects", GetEntProp(PropPhysEnt, Prop_Send, "m_fEffects") | 0x020);  // 0x020 == NO_DRAW - don't draw the actual entity, make it invis
SetEntProp(PropPhysEnt, Prop_Send, "m_CollisionGroup", 1);
DispatchSpawn(PropPhysEnt);

// SETUP THE SMOKEENT
Format(originData, sizeof(originData), "%f %f %f", clientAbsPos[0], clientAbsPos[1], clientAbsPos[2]);
new String:SName[128];
Format(SName, sizeof(SName), "Smoke%i", client);
DispatchKeyValue(SmokeEnt,"targetname", SName);
DispatchKeyValue(SmokeEnt,"Origin", originData);
DispatchKeyValue(SmokeEnt,"BaseSpread", "50");
DispatchKeyValue(SmokeEnt,"SpreadSpeed", "50");
DispatchKeyValue(SmokeEnt,"Speed", "50");
DispatchKeyValue(SmokeEnt,"StartSize", "50");
DispatchKeyValue(SmokeEnt,"EndSize", "50");
DispatchKeyValue(SmokeEnt,"Rate", "30");
DispatchKeyValue(SmokeEnt,"JetLength", "10");
DispatchKeyValue(SmokeEnt,"Twist", "10");
DispatchKeyValue(SmokeEnt,"RenderColor", "255 255 51");
DispatchKeyValue(SmokeEnt,"RenderAmt", "255");
DispatchKeyValue(SmokeEnt,"SmokeMaterial", "particle/particle_smokegrenade1.vmt");

// TELEPORT THE HENADE PROJECTILE
TeleportEntity(PropPhysEnt, ResultingPos, clientAbsAng, AngleVector);

// PARENT THE SMOKEENT TO THE HENADE PROJECTILE, SO IT MOVES
DispatchSpawn(SmokeEnt);
SetVariantString("!activator");
AcceptEntityInput(SmokeEnt, "SetParent", PropPhysEnt, SmokeEnt, 0);
AcceptEntityInput(SmokeEnt, "TurnOn");

// KILL BOTH ENTITIES
CreateTimer(5.0,StopSmoke,SmokeEnt);
CreateTimer(5.0,StopSmoke,PropPhysEnt);
}
}

public Action:StopSmoke(Handle:timer, any:Entity)
{
if(IsValidEdict(Entity))
{
AcceptEntityInput(Entity, "Kill");
}
}

Revan

  • Hero Member
  • *****
  • Posts: 513
    • View Profile
Re: A couple of visual effects
« Reply #7 on: May 31, 2016, 10:16:09 AM »
Sorry for the late response, better late than never :)

I tried to disable the gravity and to change the collision groups but no success so far. Disabling gravity might be possible with the VPhysics extension.

I tried to solve the problem differently - It looks pretty cool but you need to tweak the values (speed and duration) to suit your needs:
And you need to use EntReferences in the Timers (because the entity index could be, theoretically, reused and you would kill the wrong entity).
Code: [Select]
ConVar SpeedVar;
public OnPluginStart()
{
    RegConsoleCmd("fx_smoke",fx_smoke);
    SpeedVar = CreateConVar("fxtest_speed", "12.0");
}

public Action:fx_smoke(client, args)
{
    if(client == 0) {
        PrintToServer("fx_smoke: you need to be in-game");
    } else {
        CreateEnvSmokeStack2(client);
    }

    return Plugin_Handled;
}

CreateEnvSmokeStack2(client)
{
    new SmokeEnt = CreateEntityByName("env_smokestack");
    if(IsValidEdict(SmokeEnt) && IsClientInGame(client))
    {
        new String:originData[64];
        new Float:clientAbsPos[3];    GetClientAbsOrigin(client, clientAbsPos);    clientAbsPos[2]+=40.0;
       
        // SETUP THE SMOKEENT
        Format(originData, sizeof(originData), "%f %f %f", clientAbsPos[0], clientAbsPos[1], clientAbsPos[2]);
        new String:SName[128];
        Format(SName, sizeof(SName), "Smoke%i", client);
        DispatchKeyValue(SmokeEnt,"targetname", SName);
        DispatchKeyValue(SmokeEnt,"Origin", originData);
        DispatchKeyValue(SmokeEnt,"BaseSpread", "50");
        DispatchKeyValue(SmokeEnt,"SpreadSpeed", "50");
        DispatchKeyValue(SmokeEnt,"Speed", "50");
        DispatchKeyValue(SmokeEnt,"StartSize", "50");
        DispatchKeyValue(SmokeEnt,"EndSize", "50");
        DispatchKeyValue(SmokeEnt,"Rate", "30");
        DispatchKeyValue(SmokeEnt,"JetLength", "10");
        DispatchKeyValue(SmokeEnt,"Twist", "10");
        DispatchKeyValue(SmokeEnt,"RenderColor", "255 255 51");
        DispatchKeyValue(SmokeEnt,"RenderAmt", "255");
        DispatchKeyValue(SmokeEnt,"SmokeMaterial", "particle/particle_smokegrenade1.vmt");
       
        // PARENT THE SMOKEENT TO THE HENADE PROJECTILE, SO IT MOVES
        DispatchSpawn(SmokeEnt);
        AcceptEntityInput(SmokeEnt, "TurnOn");

        // get aim pos
        float endpos[3];
        War3_GetAimEndPoint(client, endpos);
       
        DataPack dp;
        CreateDataTimer(0.1, UpdatePos, dp);
        dp.WriteCell(SmokeEnt); //TODO: use ent reference
        dp.WriteFloat(endpos[0]);
        dp.WriteFloat(endpos[1]);
        dp.WriteFloat(endpos[2]);
       
        CreateTimer(8.0,killent,SmokeEnt);
    }
}

public Action UpdatePos(Handle timer, Handle dph)
{
    DataPack dp = view_as<DataPack>(dph);
    dp.Reset();
    int ent = dp.ReadCell();
    if(ent != 0 && IsValidEntity(ent))
    {
        PrintToServer("updatePos %d", ent);
        float x = dp.ReadFloat();
        float y = dp.ReadFloat();
        float z = dp.ReadFloat();
       
        // src and destination vector
        float src[3];
        float dest[3];
        dest[0] = x; dest[1] = y; dest[2] = z;
       
        // Get current entity origin
        GetEntPropVector(ent, Prop_Send, "m_vecOrigin", src);
       
        // do the math
        float direction[3];
        SubtractVectors(dest, src, direction);
        NormalizeVector(direction, direction);
        ScaleVector(direction, SpeedVar.FloatValue);
       
        // update position
        AddVectors(src, direction, src);
        TeleportEntity(ent, src, NULL_VECTOR, NULL_VECTOR);

        // todo: check if player is near enough and damage him
        // <magic></magic>
       
        // re-fire
        DataPack dp2;
        CreateDataTimer(0.1, UpdatePos, dp2);
        dp2.WriteCell(ent); //TODO: use ent reference
        dp2.WriteFloat(x);
        dp2.WriteFloat(y);
        dp2.WriteFloat(z);
    }
    return Plugin_Continue;
}

public Action:killent(Handle:timer, any:Entity)
{
    if(IsValidEdict(Entity))
    {
        AcceptEntityInput(Entity, "Kill");
    }
}
« Last Edit: May 31, 2016, 10:22:29 AM by Revan »
#war3source on gamesurge

abgar

  • Full Member
  • ***
  • Posts: 114
    • View Profile
Re: A couple of visual effects
« Reply #8 on: May 31, 2016, 07:20:20 PM »
That's awesome Revan.  Thanks for that.  I like how the speed is adjustable with yours fairly easily.  The only thing i'd be changing would be the use of War3_GetAimEndPoint, because that has an inbuilt TraceRay function that would stop it from going through walls etc (which is something i really wanted in this one).
It really helps me to understand how you've done it too, though, as that gives a lot more freedom for me to do things similar in the future :)

Here's what i ended up going with, using a series of creation / deletion - just for your thoughts, and anyone else to see :)

The only real benefit in this way that i can see is being able to easily specify the total range this will travel and over what time, whereas using a speed Cvar to scale would need some testing to get exactly what you wanted. 
Code: [Select]
new Counter;
new Float:EndPos[10][3];
new bool:bHitByOverload[MAXPLAYERSCUSTOM]={false, ...};

public OnUltimateCommand(client,race,bool:pressed)
{
if(ValidPlayer(client,true) && race==thisRaceID && pressed)
{
new OverloadLevel=War3_GetSkillLevel(client,thisRaceID,ULT_OVERLOAD);
if(OverloadLevel>0)
{
if(SkillAvailable(client,thisRaceID,ULT_OVERLOAD,true,true,true))
{
War3_CooldownMGR(client,OverloadCD,thisRaceID,ULT_OVERLOAD,true,true);

new Float:angle[3]; GetClientAbsAngles(client,angle);
new Float:startpos[3]; GetClientEyePosition(client,startpos);
new Float:endpos[3];
new Float:direction[3];
new Float:vertexgap=100.0;
GetAngleVectors(angle, direction, NULL_VECTOR, NULL_VECTOR);
ScaleVector(direction, vertexgap);
AddVectors(startpos, direction, endpos);

for (new x=0;x<10;x++)
{
EndPos[x][0]=endpos[0];
EndPos[x][1]=endpos[1];
EndPos[x][2]=endpos[2];
AddVectors(endpos, direction, endpos);
}

Counter=1;
CreateEnvSmokeStack(client,EndPos[1]);

new Float:SmokeTimeDelay=0.2;
for (new y=1;y<10;y++)
{
CreateTimer(SmokeTimeDelay,SmokeTimer,client);
SmokeTimeDelay+=0.2;
}
}
}
else
W3MsgUltNotLeveled(client);
}
}

public Action:SmokeTimer(Handle:timer,any:client)
{
CreateEnvSmokeStack(client,EndPos[Counter]);
Counter++;
}

CreateEnvSmokeStack(client,Float:SmokePos[3])
{
new OverloadLevel=War3_GetSkillLevel(client,thisRaceID,ULT_OVERLOAD);
new SmokeEnt = CreateEntityByName("env_smokestack");
if(IsValidEdict(SmokeEnt) && IsClientInGame(client))
{
new String:originData[64];
Format(originData, sizeof(originData), "%f %f %f", SmokePos[0], SmokePos[1], SmokePos[2]);
new String:SName[128];
Format(SName, sizeof(SName), "Smoke%i", Counter);
DispatchKeyValue(SmokeEnt,"targetname", SName);
DispatchKeyValue(SmokeEnt,"Origin", originData);
DispatchKeyValue(SmokeEnt,"BaseSpread", "50");
DispatchKeyValue(SmokeEnt,"SpreadSpeed", "50");
DispatchKeyValue(SmokeEnt,"Speed", "50");
DispatchKeyValue(SmokeEnt,"StartSize", "50");
DispatchKeyValue(SmokeEnt,"EndSize", "50");
DispatchKeyValue(SmokeEnt,"Rate", "30");
DispatchKeyValue(SmokeEnt,"JetLength", "10");
DispatchKeyValue(SmokeEnt,"Twist", "10");
DispatchKeyValue(SmokeEnt,"RenderColor", "255 255 51");
DispatchKeyValue(SmokeEnt,"RenderAmt", "255");
DispatchKeyValue(SmokeEnt,"SmokeMaterial", "particle/particle_smokegrenade1.vmt");
DispatchSpawn(SmokeEnt);
AcceptEntityInput(SmokeEnt, "TurnOn");

CreateTimer(0.3,StopSmoke,SmokeEnt);

for (new enemy=1;enemy<=MaxClients;enemy++)
{
if(ValidPlayer(enemy,true) && GetClientTeam(enemy)!=GetClientTeam(client) && !bHitByOverload[enemy] && UltFilter(enemy))
{
new Float:enemyPos[3]; GetClientAbsOrigin(enemy,enemyPos);
if(GetVectorDistance(SmokePos,enemyPos)<=SmokeRange)
{
War3_DealDamage(enemy,OverLoadDamage[OverloadLevel],client,DMG_CRUSH,"overload",_,W3DMGTYPE_MAGIC);
War3_SetBuff(enemy,fSlow,thisRaceID,SlowAmount[OverloadLevel]);
CreateTimer(SlowDuration[OverloadLevel],StopSlow,enemy);
W3SetPlayerColor(enemy,thisRaceID,255,255,51,255);
bHitByOverload[enemy]=true;
}
}
}
}
}

public Action:StopSmoke(Handle:timer, any:Entity)
{
if(IsValidEdict(Entity))
{
AcceptEntityInput(Entity, "Kill");
}
}

public Action:StopSlow(Handle:timer,any:client)
{
if(ValidPlayer(client) && bHitByOverload[client])
{
bHitByOverload[client]=false;
W3ResetBuffRace(client,fSlow,thisRaceID);
W3ResetPlayerColor(client,thisRaceID);
}
}