Help: Making a player 'deaf'

Discuss programming in the QuakeC language.
OneManClan
Posts: 247
Joined: Sat Feb 28, 2009 2:38 pm
Contact:

Help: Making a player 'deaf'

Post by OneManClan »

Hi everyone,

I write this in a state of mental exhaustion. It's been days and days and in spite of having .dimension_see .dimension_seen and dimension_sent patiently explained to me (thanks Spike), I still cant figure out how to make it so 'concussed' players are (temporarily) deaf.

I've read Spikes .dimension demonstration codeover and over, and although using the .dimension fields worked to make a player hear something no-one else can hear, I still haven't been able to use this concept to make a player NOT hear what everyone else can. I tried setting the deaf_player's self.dimension_see to an unused channel, and yes, that stops them hearing any sound entities make (good), but it also stops them seeing any entities (bad).

One idea suggested (*if* I understood correctly) was to change the default channel (255) for all players and entities, and somehow use that in conjunction w the sound function, to make it so that the sounds aren't sent to the concussed players channel... but I clearly am still too newbie to grasp the concept.

Spoike says we need to be "changing the dimension the ent can be seen in only for the duration of the sound call", but wouldn't this mean that no-one can hear the sound?

How can we create the situation where (eg) both players can see a grenade, but only 1 player can hear it?

any response appreciated, and please feel free to dumb down all explanations to my (perhaps embarrassingly) newbie level.

thanks,


OneManClan

[EDIT]
I stumbled across this in my notes:
Spike wrote:"dimensions are masks, not individual values"
"254|1==255"
This implies entities can be visible in multiple dimensions... and the 'deafness' effect can be achieved via bitwise operators.. (?)
OneManClan
Posts: 247
Joined: Sat Feb 28, 2009 2:38 pm
Contact:

Re: Help: Making a player 'deaf'

Post by OneManClan »

UPDATE:

Q1: How can an entity be 'seen' in multiple .dimensions?

Ok, I get Spikes suggestion that we assign two dimensions:
.dimension_seen = 1; // seen by all
.dimension_seen = 2; // seen by all who are not deaf.

I also get the concept that we switch the .dimension_seen of an entity to '2' just before it uses the 'sound' function, and back to '1' immediately after. Cool.

However, this means that entities need to be 'seen' by players with different .dimension_see 's. ... but how? I've been RTFMing like mad, and *still* have no idea how to use 'masks' / bitwise operators to make NonDeaf players 'see' the same entity as a DeafPlayer, ie the grenade example where the deaf player and non-deaf player can both see the grenade although they have different .dimension_see channels.

:?


Q 2: How do we change the default entity.dimension_see/seen ?

In any case, hoping that that bit will become clear later ( 'bit' gedddit? ;)) I had a go at changing the default .dimension_see for all entities from 255 to 1 (as suggested by Spike). I did this by making a wrapper for the spawn function, as follows:

Code: Select all

entity() spawn =
{
local entity te;

te = spawn_b(); // this is the renamed built-in (#14)

te.dimension_see = 1;
te.dimension_seen = 1;

return te;

};
Also, at the bottom of ClientConnect, I put:

Code: Select all

self.dimension_see = 1;
self.dimension_seen = 1;

This compiled, the server ran, I connected, but as soon as I fired a weapon, threw a grenade, or (strangely enough) touched a door whilst it was opening/closing (?) the server 'crashes' (kinda gracefully though):

Image


The 'we're in dimension <DimensionNum>' dprints happen every time 'sound' is called.
The "spawn 1, spawn 2... spawn 6" dprints are just me making sure the crash didn't occur *within* the spawn wrapper function (as above), it didn't.

I recall Spike saying that only players/clients have a .dimension_see, but I tried commenting out 'te.dimension_see = 1;' and only leaving 'te.dimension_seen = 1;' but the same crash happens with either of those two lines.

I'm really stuck here, so any response will be appreciated,

thanks,

OneManClan
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Post by Spike »

bitwise truths:
255 = 1+2+4+8+16+32+64+128.
254 = 2+4+8+16+32+64+128.

255&1 = 1
254&1 = 0

1|3 = 3
1|2 = 3
1|1 = 1

When using bitfields, it helps to give names to the individual bits (as in all programming, magic values are bad).

x&y = 1 where both x and y = 1, 0 where either is 0. if 1, the dimensions match and the ent can be seen.
Note that this is 'bitwise and' and not 'logical and', and thus your result value is only 0 or 1, but the bits that are set are as set. See the .items field for a practical example in quake. Where a bit is set, you have a hud item visible in your sbar, where its 0, you don't. Dimension_see/send is just a pair of masks. When they cancel each other out (see&seen == 0), the ent is not visible.
OneManClan
Posts: 247
Joined: Sat Feb 28, 2009 2:38 pm
Contact:

Post by OneManClan »

[DISCLAIMER: THIS POST CONTAINS BASIC, SIMPLIFIED EXPLANATIONS OF BITWISE OPERATIONS FOR NEWBIES SUCH AS MYSELF, AND WILL POSSIBLY BE VERY BORING TO GURUS!]

[EDIT: Disclaimer 2 (Aug 2011): Over the last year, I've edited the following explanation multiple times, as I (slowly) became less newbie. The explanation is still valid, and I hope useful, but it may still contain inaccuracies. Make sure you read the following posts for yet more corrections.]

Thanks to Spike for the "bitwise truths" example/demo/calculations. I looked at them over and over, and am pleased to say that (finally) there's been a BREAKTHROUGH in my grasp of the topic. Hopefully. :o Ok, I had two major misunderstandings:

Misunderstanding 1.
I was thinking of the 'dimensions' as 'channels', and assumed things on the same channel were visible to each other, and things on different channels were not. WRONG. The dimension 'channels' only have to produce the result '0' as a result of Spikes formula:

Code: Select all

if (see&seen == 0), the ent is not visible

Misunderstanding 2.
I also thought, from looking at typical qc code such as:

Code: Select all

if (thing.flags & #FL_MONSTER))
that (in this example) 'thing.flags & #FL_MONSTER' asks the question: "Are the bits set to '1' in #FL_MONSTER, also set to '1' in thing.flags?", and expecting a yes (1) or a no (0). But in fact (for the benefit of anyone reading this more newbie than I) the '&' outputs a new number, compiled by all the bits that the two numbers have in common. If the new number is > 0 [EDIT: WRONG, that should be "if the new number is !=0", negative numbers produce a 'TRUE' result in 'if' statements], the if statement (such as above) is TRUE. The typical bitwise explanation diagrams always show the two values/ bytes directly above the other and this makes it much easier to grasp. We look at columns, and make a new number, by putting a '1' in every box where both numbers have a '1'. This is why the values in defs.qc for flags are (usually) 1,2,4,8,16 etc, because those numbers produce usable results for boolean checks using '&'. Yes this is old news to the Gurus here, but to a newbie like me suddenly a whole bunch of things have fallen into place. :)

Anyway, although the bodyque error (as per my previous post) is still being analysed, I believe I (finally!) DO grasp the .dimension see concept :) So I thought I'd do walkthrough of my understanding, both for the benefit of any other newbies interested in using .dimension, and for my own benefit to make things clearer, to provide a 'reference guide' in case I forget all this in a few months, and (of course) to give the Gurus a chance to step in and correct anything I've said which is incorrect.

So without further ado:

WALKTHROUGH: ACHIEVING 'DEAFNESS' VIA .DIMENSION_SEE AND BITWISE OPERATIONS:

1. We give every entity in the game a default .dimension_see and .dimension_seen value of '3'. [EDIT: this step isn't necessary, as explained in the next post. Also the figures below were arrived at simply by looking at the two bytes on top of each other, and figuring out where the '1's needed to be so that they lined up to produce a '0' as the result of a '&' operation]

2. We spawn 2 players, Tom and Dick both with the (default) .dimension_see of 3 (00000011)

3. We make Dick deaf by changing his .dimension_see to '2' (00000010).

4. Tom throws a grenade with the (default) .dimension_seen of 3 (00000011).


Now, remembering Spike's formula:

Code: Select all

if (see&seen == 0), the ent is not visible
we process the numbers:

if (Dick.dimension_see & grenade.dimension_seen) != 0, Dick (who is now deaf, remember) will see the grenade (which is what we want).

if (2&3 != 0) //Dick sees the grenade

00000010 (2)
&
00000011 (3)
=
00000010 (2)

So, 2&3 == 2 != 0 // therefore Dick will see the grenade

Now, to make sure Dick doesn't hear the grenade, just before the sound function, we change its' grenade.dimension_seen to '1' (00000001). Dick is still deaf and has .dimension_see = 2.

The formula again:

Code: Select all

if (see&seen == 0), the ent is not visible
if (Dick.dimension_see & grenade.dimension_seen == 0) //Dick will NOT hear the grenade

2&1

00000010 (2)
&
00000001 (1)
=
00000000 (0)

None of the bits match, so the final number gets NO bits, which means 2&1 == 0 ... so Dick doesn't hear anything (good!).

But what happened to Tom? He still has the .dimension_see of 3 - did he get to hear the grenade (w a .dimension_seen of 1?)? Let's find out:
if (see&seen == 0), the ent is not visible.
tom.dimension_see & grenade.dimension_seen

3&1

00000011 (3)
&
00000001 (1)
=
00000001 (1)

3&1 == 1 != 0 so the ent is NOT invisible to Tom, and he did in fact hear the grenade!


Hope this helps!



OneManClan
Last edited by OneManClan on Fri Aug 12, 2011 1:30 am, edited 2 times in total.
OneManClan
Posts: 247
Joined: Sat Feb 28, 2009 2:38 pm
Contact:

Post by OneManClan »

OneManClan wrote:1. We give every entity in the game a default .dimension_see and .dimension_seen value of '3'.
This step actually proved very difficult, but thankfully, unnecessary. I forgot that .dimension is a float (16 bits!)

[EDIT: QuakeC floats are in fact 32 bit. The following concept is still valid, and the code works, but bear in mind there are actually an extra 16 zeros to the left of the 16 bits referred to here]

- therefore the default 255 leaves 8 bits of '0' to play around with [EDIT: the bitwise masking procedure can still be done w 8 bits; as you can see below, the leftmost 8 bits aren't needed to produce a '0' via a '&' operation]. So we go:

.dimension 255 (00000000 11111111) // default, see/seen by all

.dimension 127 (00000000 01111111) // seen by all not deaf

.dimension 128 (00000000 10000000) // deaf players .dimension_see


I tested it and it all works!! Concussed players now suffer from 'deafness'. I might add some 'hyperventilation/breathing' sounds to make it more intense. Anyway, big thanks to Spike for providing this amazing functionality, and for his help in making the 'ConcDeaf' effect happen.



OneManClan
Last edited by OneManClan on Sun Mar 20, 2011 4:45 am, edited 1 time in total.
OneManClan
Posts: 247
Joined: Sat Feb 28, 2009 2:38 pm
Contact:

Post by OneManClan »

Hey people,

The 'deafness' code has been working great for about six months now, but ... it doesn't provide TOTAL deafness. The sound of the projectiles hitting walls/surfaces can still be heard - apparantly this is controlled on the engine side, not the quake C. Is this true?

Does anyone have any ideas as to how to stop these sounds as well? I *think* I recall someone mentioning the use of writebytes. Possible?

thanks,


OneManClan
ps. I know I can lower a players volume (via stuffcmd), but I have a (new) sound I DO want players to hear (their own heavy, panicky breathing), and *nothing* else.
mh
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Post by mh »

The following sounds are controlled entirely by the client:
  • "wizard/hit.wav"
  • "hknight/hit.wav"
  • "weapons/tink1.wav"
  • "weapons/ric1.wav"
  • "weapons/ric2.wav"
  • "weapons/ric3.wav"
  • "weapons/r_exp3.wav"
They're started as a result of various TE_ messages (e.g. TE_EXPLOSION starts the "weapons/r_exp3.wav" sound, as well as the particle effect and dynamic light), so you have no control whatsoever over them from QC. None. There is no way.

All options involve modifying engine code. Probably the best way is to define new TE_ effects but you really need to be careful with compatibility here. Logically you would split each of these existing effects into 3 components - a new TE_ for the particle effect, a new TE_ for the dynamic light and a new TE_ for the sound, and then send only the effects that you want. You'd then need to go through each place in your QC where a TE_ is sent and make the appropriate modifications there too.

Optionally you could modify the QC to spawn a particle effect (via "particle") and a sound (via "sound") instead of sending a TE_ message. There is howver no way that I know of that you can spawn a dynamic light through QC. Well, there is, but it involves setting up fake entities, attaching effects to them, and so on. This I believe is what Nehahra did in some cases; a protocol change ("svc_dynamiclight"?) or a new TE_ for dynamic lights would be preferable.

Ugly? Yes.
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Post by Spike »

Obviously CSQC can do anything...

In lieu of csqc, I can give you a robust solution for FTE clients, I can give a usually-robust solution for DP7 clients. However, for any other client, its worse news than MH says - particle() is a custom extension in QuakeWorld.

One hack would be to get the user to replace their wav sounds with an empty/null sound, and use a sound() call from the server with a different replacement name (using the deafness code you already have), but you would need your users to help you with that by downloading a proper package specific to your mod (may require separate gamedir).
Alex
Posts: 24
Joined: Sun Feb 13, 2011 6:24 pm

Post by Alex »

This is a really simple and dirty way of doing it, don't know if you'll like it or not. There's some old code out there that codes for flashbangs. It blinds the player by altering the gamma value via console commands and uses a function to gradually decrease the gamma back to normal.

I'd try using the same code but substitute volume for gamma. The only problem is, not EVERYBODY has their volume set to 1, so the resulting issue could be that your "unconcussed" volume is greater or less than your original volume. But I'm sure someone here knows how to figure that out.

If you can't find the code I can try to dig it up if I've got it.

Alex
mh
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Post by mh »

Alex wrote:This is a really simple and dirty way of doing it, don't know if you'll like it or not. There's some old code out there that codes for flashbangs. It blinds the player by altering the gamma value via console commands and uses a function to gradually decrease the gamma back to normal.

I'd try using the same code but substitute volume for gamma. The only problem is, not EVERYBODY has their volume set to 1, so the resulting issue could be that your "unconcussed" volume is greater or less than your original volume. But I'm sure someone here knows how to figure that out.

If you can't find the code I can try to dig it up if I've got it.

Alex
The main problem I can see with that is that the OP wants one sound to be playing at the normal volume but every other sound to be suppressed. Changing volume globally won't do this.
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
mankrip
Posts: 924
Joined: Fri Jul 04, 2008 3:02 am

Post by mankrip »

mh wrote:The following sounds are controlled entirely by the client:
  • "wizard/hit.wav"
  • "hknight/hit.wav"
  • "weapons/tink1.wav"
  • "weapons/ric1.wav"
  • "weapons/ric2.wav"
  • "weapons/ric3.wav"
  • "weapons/r_exp3.wav"
They're started as a result of various TE_ messages (e.g. TE_EXPLOSION starts the "weapons/r_exp3.wav" sound, as well as the particle effect and dynamic light), so you have no control whatsoever over them from QC. None. There is no way.
There is. Rename those sounds, put files containing 0.1 seconds of silence where the originals were, and add a few lines into the QC code to play the renamed sounds along with the TE_ effects.
Ph'nglui mglw'nafh mankrip Hell's end wgah'nagl fhtagn.
==-=-=-=-=-=-=-=-=-=-==
Dev blog / Twitter / YouTube
mh
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Post by mh »

mk wrote:
mh wrote:The following sounds are controlled entirely by the client:
  • "wizard/hit.wav"
  • "hknight/hit.wav"
  • "weapons/tink1.wav"
  • "weapons/ric1.wav"
  • "weapons/ric2.wav"
  • "weapons/ric3.wav"
  • "weapons/r_exp3.wav"
They're started as a result of various TE_ messages (e.g. TE_EXPLOSION starts the "weapons/r_exp3.wav" sound, as well as the particle effect and dynamic light), so you have no control whatsoever over them from QC. None. There is no way.
There is. Rename those sounds, put files containing 0.1 seconds of silence where the originals were, and add a few lines into the QC code to play the renamed sounds along with the TE_ effects.
Well, OK - no way that doesn't involve disgusting abuse of the original game data then. :P :lol:
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
goldenboy
Posts: 924
Joined: Fri Sep 05, 2008 11:04 pm
Location: Kiel
Contact:

Post by goldenboy »

I think it could also be done in QC. Add a field .deaf and a check for it to every sound call in the game. If true, play null.wav instead (or lower volume).

Brain dead? Yes. RMQ does something like that to weapon sounds when the player is in Rage mode (under 25, I think, hitpoints).

Having sound effects in the engine would be nicer though (maybe via LADSPA plugins or something similar). Freeverb comes to mind.
OneManClan
Posts: 247
Joined: Sat Feb 28, 2009 2:38 pm
Contact:

Post by OneManClan »

Spike wrote:One hack would be to get the user to replace their wav sounds with an empty/null sound, and use a sound() call from the server with a different replacement name (using the deafness code you already have), but you would need your users to help you with that by downloading a proper package specific to your mod (may require separate gamedir).
I'm assuming mk describes the same concept here:
mk wrote:
  • "wizard/hit.wav"
  • "hknight/hit.wav"
  • "weapons/tink1.wav"
  • "weapons/ric1.wav"
  • "weapons/ric2.wav"
  • "weapons/ric3.wav"
  • "weapons/r_exp3.wav"
Rename those sounds, put files containing 0.1 seconds of silence where the originals were, and add a few lines into the QC code to play the renamed sounds along with the TE_ effects.
"put files containing 0.1 seconds of silence where the originals were"

Q1: How? The originals are stored in pak0.pak. Even if I modified it, wouldn't every player then need to download this new pak?

Q2: Wouldn't this pak effectively replace their existing .pak, and thus 'break compatibility' w every other version of Quake?

Unless..... :

Q3: What if I gave the modified pak0.pak another name, pakfoo.pak, and somehow got QuakeC to tell the server to tell the client to use pakfoo.pak instead of the regular pak0.pak? s this what you're referring to Spike? Since pakfoo.pak would be less than 17mb, would it be possible for players to have it automatically downloaded (into a new folder) rather than having to install it themselves?



OneManClan
OneManClan
Posts: 247
Joined: Sat Feb 28, 2009 2:38 pm
Contact:

Post by OneManClan »

Hey guys,

I just checked, and my previous post didn't get a response.

Please confirm: are the answers to Q1, Q2, and Q3 'yes'?

thanks,

OneManClan
Post Reply