bubble_bob & bubble.spr
Moderator: InsideQC Admins
10 posts
• Page 1 of 1
bubble_bob & bubble.spr
Hi:
Made a new mod to misc.qc with the bubble code that will let the bubbles be blocked by the map world....comes in handy on maps like E1M4 if you are way down deep in those underwater tunnels. (side note, how come mappers dont make any kool underwater maps like that anymore?)
Anyway, not perfected yet, but I played with the code some more, got the bubble to hug the bsp nicely with setting vec_hull_min to Vec_origin, and Vec_hull_max to ' 1 1 1 '. Default was: 8 8 8 , -8 - -8 for the bubble spr. Of course has to now be SOLID_TRIGGER field, and the NOCLIP movetype was changed to MOVETYPE_FLY. When they encounter the bsp their velocity_z slows down in the touch function...and the think seems to still be able to restore the normal velicity Z when they move around a bit, and eventually free themselves. Ok, heres the bug.
The original code had the bubble NOT make a splash noise once it broke the surface. Since I changed the code, I now hear the spalsh noise when the bubbles breaks the surface. I tried restoring the original paramaters to the bubble.spr, even the old set size once it comes within 4 units of the surface detected via traceline, and still I hear the splash noise. I even tried removing self.flags & FL_WATERJUMP from itself once it becomes non-solid again at 4 units below the surface, but no help. Heres the code:
Made a new mod to misc.qc with the bubble code that will let the bubbles be blocked by the map world....comes in handy on maps like E1M4 if you are way down deep in those underwater tunnels. (side note, how come mappers dont make any kool underwater maps like that anymore?)
Anyway, not perfected yet, but I played with the code some more, got the bubble to hug the bsp nicely with setting vec_hull_min to Vec_origin, and Vec_hull_max to ' 1 1 1 '. Default was: 8 8 8 , -8 - -8 for the bubble spr. Of course has to now be SOLID_TRIGGER field, and the NOCLIP movetype was changed to MOVETYPE_FLY. When they encounter the bsp their velocity_z slows down in the touch function...and the think seems to still be able to restore the normal velicity Z when they move around a bit, and eventually free themselves. Ok, heres the bug.
The original code had the bubble NOT make a splash noise once it broke the surface. Since I changed the code, I now hear the spalsh noise when the bubbles breaks the surface. I tried restoring the original paramaters to the bubble.spr, even the old set size once it comes within 4 units of the surface detected via traceline, and still I hear the splash noise. I even tried removing self.flags & FL_WATERJUMP from itself once it becomes non-solid again at 4 units below the surface, but no help. Heres the code:
- Code: Select all
void () make_bubbles =
{
local entity bubble;
bubble = spawn ();
setmodel (bubble, "progs/s_bubble.spr");
setorigin (bubble, self.origin);
bubble.movetype = MOVETYPE_FLY;
bubble.solid = SOLID_TRIGGER;
bubble.velocity = '0 0 7';
bubble.nextthink = (time + 0.5);
bubble.think = bubble_bob;
bubble.touch = bubble_remove;
bubble.classname = "bubble";
bubble.frame = FALSE;
bubble.cnt = FALSE;
setsize (bubble, '0 0 0', '1 1 1');
self.nextthink = ((time + random ()) + 0.5);
self.think = make_bubbles;
sound (bubble, CHAN_AUTO, "uwater.wav", 0.5, 2); // Underwater sound
};
void () bubble_split =
{
local entity bubble;
bubble = spawn ();
setmodel (bubble, "progs/s_bubble.spr");
setorigin (bubble, self.origin);
bubble.movetype = MOVETYPE_FLY;
bubble.solid = SOLID_TRIGGER;
bubble.velocity = self.velocity;
bubble.nextthink = (time + 0.5);
bubble.think = bubble_bob;
bubble.touch = bubble_remove;
bubble.classname = "bubble";
bubble.frame = TRUE;
bubble.cnt = MOVETYPE_BOUNCE;
setsize (bubble, '0 0 0', '1 1 1');
sound (bubble, CHAN_AUTO, "uwater.wav", 0.5, 2); // Underwater sound
self.frame = TRUE;
self.cnt = MOVETYPE_BOUNCE;
if ((self.waterlevel != 3))
remove (self);
};
void () bubble_remove =
{
self.reserve = self.reserve + 1; // Counting number of touches
if (!self.solid)
self.flags = self.flags - (self.flags & FL_WATERJUMP);
if ((other.classname == self.classname))
{
self.origin_y = other.origin_x ;
self.origin_x = other.origin_y ;
}
if (other.solid && self.classname != other.classname)
{
if (self.reserve > 10)
return;
self.velocity_z = self.velocity_z * 0.9;
self.velocity_x = self.velocity_x * nrandom ();
self.velocity_y = self.velocity_y * nrandom ();
}
};
void () bubble_bob =
{
local float rnd1;
local float rnd2;
local float rnd3;
local vector vtmp1;
local vector modi;
self.cnt = (self.cnt + TRUE);
if ((self.cnt == MOVETYPE_STEP))
{
bubble_split ();
}
makevectors (self.angles);
traceline (self.origin, self.origin + '0 0 4', TRUE, self);
if (trace_inopen)
{
self.movetype = MOVETYPE_NOCLIP;
self.solid = 0;
self.touch = SUB_Null;
setsize (self, '-8 -8 -8', '8 8 8');
}
if (!self.solid)
self.flags = self.flags - (self.flags & FL_WATERJUMP);
if (!(self.waterlevel))
{
sound (self, CHAN_RUNE, "misc/water1.wav", 1, ATTN_IDLE);
SUB_Remove();
}
rnd1 = (self.velocity_x + (-10 + (random () * 20)));
rnd2 = (self.velocity_y + (-10 + (random () * 20)));
rnd3 = ((self.velocity_z + MOVETYPE_BOUNCE) + (random () * MOVETYPE_BOUNCE));
if ((rnd1 > MOVETYPE_BOUNCE))
{
rnd1 = MOVETYPE_FLY;
}
if ((rnd1 < -10))
{
rnd1 = CONTENT_LAVA;
}
if ((rnd2 > MOVETYPE_BOUNCE))
{
rnd2 = MOVETYPE_FLY;
}
if ((rnd2 < -10))
{
rnd2 = CONTENT_LAVA;
}
if ((rnd3 < MOVETYPE_BOUNCE))
{
rnd3 = 5;
}
if ((rnd3 > 30))
{
rnd3 = 25;
}
self.velocity_x = rnd1;
self.velocity_y = rnd2;
self.velocity_z = rnd3;
self.nextthink = (time + 0.5);
self.think = bubble_bob;
};
-

Cobalt - Posts: 445
- Joined: Wed Jun 10, 2009 2:58 am
- Location: New England, USA
Re: bubble_bob & bubble.spr
Cobalt wrote:Hi:
Made a new mod to misc.qc with the bubble code that will let the bubbles be blocked by the map world....comes in handy on maps like E1M4 if you are way down deep in those underwater tunnels. (side note, how come mappers dont make any kool underwater maps like that anymore?)
I suggest "Antediluvian" by metlslime and Than's "Plumbers carry shotguns" or whatever the heck it's called.
-

goldenboy - Posts: 924
- Joined: Fri Sep 05, 2008 11:04 pm
- Location: Kiel
Ok, put this aside for a while, but now trying to get it fixed. Ill be darned if I can spot the thing thats triggering the splash sound in the engine when the bubble moves from in water, to the surface. You would think it be only a one way street, right?
I tried assigning:
self.flags = (self.flags -(self.flags & FL_WATERJUMP));
but makes no difference. If I were to guess, I would take a chance that self.waterlevel of (1) is the trigger...so maybe a way to hack it so it stays > 1 would work, but not sure if the QC is that high of a priority to over ride the engine in this area - probably not.
So LH said to try : contentstransition
It is suppose to supply a new pointcontent transition so you dojt hear the water splash, but I have a hard time figuring out how to use it. Any sugguestions?
I tried assigning:
self.flags = (self.flags -(self.flags & FL_WATERJUMP));
but makes no difference. If I were to guess, I would take a chance that self.waterlevel of (1) is the trigger...so maybe a way to hack it so it stays > 1 would work, but not sure if the QC is that high of a priority to over ride the engine in this area - probably not.
So LH said to try : contentstransition
It is suppose to supply a new pointcontent transition so you dojt hear the water splash, but I have a hard time figuring out how to use it. Any sugguestions?
-

Cobalt - Posts: 445
- Joined: Wed Jun 10, 2009 2:58 am
- Location: New England, USA
- Code: Select all
//DP_SV_ENTITYCONTENTSTRANSITION
//idea: Dresk
//darkplaces implementation: Dresk
//field definitions:
.void(float nOriginalContents, float nNewContents) contentstransition;
//description:
//This field function, when provided, is triggered on an entity when the contents (ie. liquid / water / etc) is changed.
//The first parameter provides the entities original contents, prior to the transition. The second parameter provides the new contents.
//NOTE: If this field function is provided on an entity, the standard watersplash sound IS SUPPRESSED to allow for authors to create their own transition sounds.
I believe it would be something along the lines of:
entity.contentstransition = aContentsTransitionFunction;
void aContentsTransitionFunction(float nOriginalContent, float nNewContents)
{
// nothing == no sound
}
- apolluwn
- Posts: 35
- Joined: Tue Apr 26, 2011 11:57 pm
I noticed that when an entity transits from contents during movement the C code fires the water splash sound by default (observed this in a map where you could launch grenades to bounce against a sky brush: the grenade disappeared for a moment, bounced back to the map hull and then when getting out of the sky brush the splash sound could be heard). Not sure if modern engines (Darkplaces, FTE) offer some way to circumvent this strange behavior.
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC
(LordHavoc)
-

frag.machine - Posts: 2090
- Joined: Sat Nov 25, 2006 1:49 pm
frag.machine wrote:I noticed that when an entity transits from contents during movement the C code fires the water splash sound by default (observed this in a map where you could launch grenades to bounce against a sky brush: the grenade disappeared for a moment, bounced back to the map hull and then when getting out of the sky brush the splash sound could be heard). Not sure if modern engines (Darkplaces, FTE) offer some way to circumvent this strange behavior.
Actually, that's exactly what this extension should do in DP... It doesn't seem to take CONTENT(S)_SKY into consideration, but honestly shouldn't the entity remove itself when it hits the sky and not bounce back anyways?
- Code: Select all
int SV_CheckContentsTransition(prvm_edict_t *ent, const int nContents)
{
int bValidFunctionCall;
// Default Valid Function Call to False
bValidFunctionCall = false;
if(PRVM_serveredictfloat(ent, watertype) != nContents)
{ // Changed Contents
// Acquire Contents Transition Function from QC
if(PRVM_serveredictfunction(ent, contentstransition))
{ // Valid Function; Execute
// Assign Valid Function
bValidFunctionCall = true;
// Prepare Parameters (Original Contents, New Contents)
// Original Contents
PRVM_G_FLOAT(OFS_PARM0) = PRVM_serveredictfloat(ent, watertype);
// New Contents
PRVM_G_FLOAT(OFS_PARM1) = nContents;
// Assign Self
PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
// Execute VM Function
PRVM_ExecuteProgram(PRVM_serveredictfunction(ent, contentstransition), "contentstransition: NULL function");
}
}
// Return if Function Call was Valid
return bValidFunctionCall;
}
- Code: Select all
void SV_CheckWaterTransition (prvm_edict_t *ent)
{
int cont;
cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(PRVM_serveredictvector(ent, origin)));
if (!PRVM_serveredictfloat(ent, watertype))
{
// just spawned here
PRVM_serveredictfloat(ent, watertype) = cont;
PRVM_serveredictfloat(ent, waterlevel) = 1;
return;
}
// DRESK - Support for Entity Contents Transition Event
// NOTE: Call here BEFORE updating the watertype below,
// and suppress watersplash sound if a valid function
// call was made to allow for custom "splash" sounds.
if( !SV_CheckContentsTransition(ent, cont) )
{ // Contents Transition Function Invalid; Potentially Play Water Sound
// check if the entity crossed into or out of water
if (sv_sound_watersplash.string && ((PRVM_serveredictfloat(ent, watertype) == CONTENTS_WATER || PRVM_serveredictfloat(ent, watertype) == CONTENTS_SLIME) != (cont == CONTENTS_WATER || cont == CONTENTS_SLIME)))
SV_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, false);
}
if (cont <= CONTENTS_WATER)
{
PRVM_serveredictfloat(ent, watertype) = cont;
PRVM_serveredictfloat(ent, waterlevel) = 1;
}
else
{
PRVM_serveredictfloat(ent, watertype) = CONTENTS_EMPTY;
PRVM_serveredictfloat(ent, waterlevel) = 0;
}
}
Original GLQuake does this... So it always plays the water sound... DP appears to have fixed this...
- Code: Select all
if (cont <= CONTENTS_WATER)
{
if (ent->v.watertype == CONTENTS_EMPTY)
{ // just crossed into water
SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
}
ent->v.watertype = cont;
ent->v.waterlevel = 1;
}
else
{
if (ent->v.watertype != CONTENTS_EMPTY)
{ // just crossed into water
SV_StartSound (ent, 0, "misc/h2ohit1.wav", 255, 1);
}
ent->v.watertype = CONTENTS_EMPTY;
ent->v.waterlevel = cont;
}
It appears to not be fixed in FTE...
- Code: Select all
if (cont <= Q1CONTENTS_WATER)
{
if (ent->v->watertype == Q1CONTENTS_EMPTY && *sv_sound_watersplash.string)
{ // just crossed into water
SVQ1_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, 0);
}
ent->v->watertype = cont;
ent->v->waterlevel = 1;
}
else
{
if (ent->v->watertype != Q1CONTENTS_EMPTY && *sv_sound_watersplash.string)
{ // just crossed into open
SVQ1_StartSound (ent, 0, sv_sound_watersplash.string, 255, 1, 0);
}
ent->v->watertype = Q1CONTENTS_EMPTY;
ent->v->waterlevel = cont;
}
- apolluwn
- Posts: 35
- Joined: Tue Apr 26, 2011 11:57 pm
Still messing with my code, added some particles and a water noise for when the bubble reaches the top. Found out that if you setmodel to null, that seems to stop the splash noise. I do that when its near the surface, just before remove (self), because sometimes if the velocity_z is not just right, it makes it break the surface, and you get the noise. With a moel that small, waterlevel change of 3,2 then one must happen pretty fast...and would require a faster think than I have I guess.
Also noticed you get the splash if you set the size of the bubble to something else while its touching another (solid_trigger) bubble or if you set the origin of one bubble so that its in the bounding box of the other bubble.
LH says not a good idea for the model to be a solid_trigger, probably true, however, no way to make it hug an underwater bsp when its a bbox.....the bounding box always seems to be above the model no matter what you do with setsize.
Also noticed you get the splash if you set the size of the bubble to something else while its touching another (solid_trigger) bubble or if you set the origin of one bubble so that its in the bounding box of the other bubble.
LH says not a good idea for the model to be a solid_trigger, probably true, however, no way to make it hug an underwater bsp when its a bbox.....the bounding box always seems to be above the model no matter what you do with setsize.
frag.machine wrote:I noticed that when an entity transits from contents during movement the C code fires the water splash sound by default (observed this in a map where you could launch grenades to bounce against a sky brush: the grenade disappeared for a moment, bounced back to the map hull and then when getting out of the sky brush the splash sound could be heard). Not sure if modern engines (Darkplaces, FTE) offer some way to circumvent this strange behavior.
-

Cobalt - Posts: 445
- Joined: Wed Jun 10, 2009 2:58 am
- Location: New England, USA
Re:
hmm, cant get that to work....it dont make sense in the traditional QC sense either. I can get your code to compile, but cant figure how to carry the values. If I change the float n values to real values like -3 or -2 which are the numerics for pointcontents water and solid, it gives compile errors. Its odd to me because .specific entity behaviour such as .th_die for example, trigger when health < 1 I think....then the .th_die code is executed. But here we are triggering the .transition based on what? If its always going to be executed on a transition, how do you declare what values are to be checked against?
apolluwn wrote:
- Code: Select all
//DP_SV_ENTITYCONTENTSTRANSITION
//idea: Dresk
//darkplaces implementation: Dresk
//field definitions:
.void(float nOriginalContents, float nNewContents) contentstransition;
//description:
//This field function, when provided, is triggered on an entity when the contents (ie. liquid / water / etc) is changed.
//The first parameter provides the entities original contents, prior to the transition. The second parameter provides the new contents.
//NOTE: If this field function is provided on an entity, the standard watersplash sound IS SUPPRESSED to allow for authors to create their own transition sounds.
I believe it would be something along the lines of:
entity.contentstransition = aContentsTransitionFunction;
void aContentsTransitionFunction(float nOriginalContent, float nNewContents)
{
// nothing == no sound
}
-

Cobalt - Posts: 445
- Joined: Wed Jun 10, 2009 2:58 am
- Location: New England, USA
Isn't the topic the subject?
Cobalt wrote:hmm, cant get that to work....it dont make sense in the traditional QC sense either. I can get your code to compile, but cant figure how to carry the values. If I change the float n values to real values like -3 or -2 which are the numerics for pointcontents water and solid, it gives compile errors. Its odd to me because .specific entity behaviour such as .th_die for example, trigger when health < 1 I think....then the .th_die code is executed. But here we are triggering the .transition based on what? If its always going to be executed on a transition, how do you declare what values are to be checked against?
I am not sure I understand exactly what you mean by it doesn't make sense in the traditional QC sense...
The values would be checked in the contents transition function... which are filled in from the engine to the vm...
e.g.
- Code: Select all
void aContentsTransitionFunction(float nOriginalContent, float nNewContents)
{
if(nNewContents == CONTENT_WATER)
sound (self, CHAN_AUTO, "sounds/water_transition.wav", 1, ATTN_NORM);
}
This is handled when SV_CheckWater, SV_CheckWaterTransition, or, most specifically, SV_CheckContentsTransition is called...
It gets the contents from
- Code: Select all
cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(PRVM_serveredictvector(ent, origin)));
This returns the content type as an integer...
e.g. in QC
- Code: Select all
float CONTENT_WATER = -3;
and in the C-Code
- Code: Select all
#define CONTENTS_WATER -3
These are the same (obviously, heh) otherwise you would get results that you didn't intend...
It then calls SV_CheckContentsTransition(ent, cont) and if this returns false it plays the water sound. (i.e. the contentstransition field doesn't exist for the entity)
^---(I think SV_CheckContentsTransition is run before this, but is subsequently called from SV_CheckWaterTransition? I'd have to look more, but this is mostly irrelevant really because you should get the desired results regardless...)
This function returns true IF .contentstransition is set to an existing function AND the content type has changed since the last frame. (actually .contentstransition just needs to be set for it to suppress the sound but it will throw an error if the function doesn't exist..)
So... just as long as a function i.e. (aContentsTransitionFunction) exists then it will suppress the water sound by skipping that part of the code. (This is why I posted an empty function... I thought all you were trying to do was suppress the sound and the function need only exist for that to work.)
Here SV_CheckContentsTransition is setting the old contents and new contents to be sent to the vm...
- Code: Select all
PRVM_G_FLOAT(OFS_PARM0) = PRVM_serveredictfloat(ent, watertype);
PRVM_G_FLOAT(OFS_PARM1) = nContents;
PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
PRVM_ExecuteProgram(PRVM_serveredictfunction(ent, contentstransition), "contentstransition: NULL function");
i.e.
PRVM_serveredictfloat(ent, watertype);
^--- sets the content type BEFORE the transition
PRVM_G_FLOAT(OFS_PARM1) = nContents;
^--- sets the content type to the CURRENT content type
which are
float nOriginalContent;
float nNewContent;
respectively, in the aContentsTransitionFunction...
Execute is merely telling the vm to execute the function in the .contentstransition field and if it doesn't exist.... "contentstransition: NULL function"
^----This is the error I was speaking about if you do not have an actual function for .contentstransition
So... Everything you need is there you are just trying to use this extension the wrong way.
Good luck
- apolluwn
- Posts: 35
- Joined: Tue Apr 26, 2011 11:57 pm
Re: Isn't the topic the subject?
Ok, It didnt make sense to me in a QC sense because I have no experience engine coding. I dont recall any other QC instruction that operates quite like this one. Your code pretty much left as it is, works, and the bubble makes no splash no matter what setsize , thanks.
My interpretation of the instruction was that you could declare what happens when say the states are specificly any combination of the pointcontent values. So i thought we had to know the pointcontent value the entity is in prior to the change, and will be in order to call that new set of instructions.
My interpretation of the instruction was that you could declare what happens when say the states are specificly any combination of the pointcontent values. So i thought we had to know the pointcontent value the entity is in prior to the change, and will be in order to call that new set of instructions.
apolluwn wrote:Cobalt wrote:hmm, cant get that to work....it dont make sense in the traditional QC sense either. I can get your code to compile, but cant figure how to carry the values. If I change the float n values to real values like -3 or -2 which are the numerics for pointcontents water and solid, it gives compile errors. Its odd to me because .specific entity behaviour such as .th_die for example, trigger when health < 1 I think....then the .th_die code is executed. But here we are triggering the .transition based on what? If its always going to be executed on a transition, how do you declare what values are to be checked against?
I am not sure I understand exactly what you mean by it doesn't make sense in the traditional QC sense...
The values would be checked in the contents transition function... which are filled in from the engine to the vm...
e.g.
- Code: Select all
void aContentsTransitionFunction(float nOriginalContent, float nNewContents)
{
if(nNewContents == CONTENT_WATER)
sound (self, CHAN_AUTO, "sounds/water_transition.wav", 1, ATTN_NORM);
}
This is handled when SV_CheckWater, SV_CheckWaterTransition, or, most specifically, SV_CheckContentsTransition is called...
It gets the contents from
- Code: Select all
cont = Mod_Q1BSP_NativeContentsFromSuperContents(NULL, SV_PointSuperContents(PRVM_serveredictvector(ent, origin)));
This returns the content type as an integer...
e.g. in QC
- Code: Select all
float CONTENT_WATER = -3;
and in the C-Code
- Code: Select all
#define CONTENTS_WATER -3
These are the same (obviously, heh) otherwise you would get results that you didn't intend...
It then calls SV_CheckContentsTransition(ent, cont) and if this returns false it plays the water sound. (i.e. the contentstransition field doesn't exist for the entity)
^---(I think SV_CheckContentsTransition is run before this, but is subsequently called from SV_CheckWaterTransition? I'd have to look more, but this is mostly irrelevant really because you should get the desired results regardless...)
This function returns true IF .contentstransition is set to an existing function AND the content type has changed since the last frame. (actually .contentstransition just needs to be set for it to suppress the sound but it will throw an error if the function doesn't exist..)
So... just as long as a function i.e. (aContentsTransitionFunction) exists then it will suppress the water sound by skipping that part of the code. (This is why I posted an empty function... I thought all you were trying to do was suppress the sound and the function need only exist for that to work.)
Here SV_CheckContentsTransition is setting the old contents and new contents to be sent to the vm...
- Code: Select all
PRVM_G_FLOAT(OFS_PARM0) = PRVM_serveredictfloat(ent, watertype);
PRVM_G_FLOAT(OFS_PARM1) = nContents;
PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(ent);
PRVM_ExecuteProgram(PRVM_serveredictfunction(ent, contentstransition), "contentstransition: NULL function");
i.e.
PRVM_serveredictfloat(ent, watertype);
^--- sets the content type BEFORE the transition
PRVM_G_FLOAT(OFS_PARM1) = nContents;
^--- sets the content type to the CURRENT content type
which are
float nOriginalContent;
float nNewContent;
respectively, in the aContentsTransitionFunction...
Execute is merely telling the vm to execute the function in the .contentstransition field and if it doesn't exist.... "contentstransition: NULL function"
^----This is the error I was speaking about if you do not have an actual function for .contentstransition
So... Everything you need is there you are just trying to use this extension the wrong way.![]()
Good luck
-

Cobalt - Posts: 445
- Joined: Wed Jun 10, 2009 2:58 am
- Location: New England, USA
10 posts
• Page 1 of 1
Who is online
Users browsing this forum: No registered users and 1 guest