]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/client/bgmscript.qc
fix note-off during decay or attack stage when above sustain level
[divverent/nexuiz.git] / data / qcsrc / client / bgmscript.qc
1 float bgmscriptbuf;
2 float bgmscriptbufsize;
3 float bgmscriptbufloaded;
4
5 .float bgmscriptline;
6 .float bgmscriptline0;
7 .float bgmscriptvelocity;
8 .float bgmscripttime;
9 .float bgmscriptstate;
10 .float bgmscriptstatetime;
11 .float bgmscriptdelta;
12
13 float GetAttackDecaySustainAmplitude(float a, float d, float s, float t)
14 {
15         // phase:
16         //   attack: from 0 to 1, in time a for a full length
17         //   decay: from 1 to s, in time d
18         //   sustain: s
19         
20         if(t < 0)
21                 return 0;
22         
23         if(a)
24                 if(t <= a)
25                         return t / a;
26
27         if(d)
28                 if(t <= a + d)
29                         return ((t - a) / d) * (s - 1) + 1;
30
31         return s;
32 }
33
34 float GetReleaseAmplitude(float d, float s, float r, float t)
35 {
36         float decayval, releaseval;
37
38         if(!r)
39                 return 0;
40         
41         if(t > r)
42                 return 0;
43         
44         releaseval = s * (1 - t / r);
45
46         if(t < -d)
47                 return 1;
48
49         if(t < 0 && t >= -d)
50         {
51                 // pre-time decay
52                 // value is s at time  0
53                 //          1 at time -d
54                 decayval = ((t + d) / d) * (s - 1) + 1;
55                 return max(decayval, releaseval);
56         }
57
58         return releaseval;
59 }
60
61 float GetAttackTime(float a, float amp)
62 {
63         return amp * a;
64 }
65
66 float GetReleaseTime(float d, float s, float r, float amp)
67 {
68         float decaytime, releasetime;
69
70         if(!s)
71                 return 0;
72         
73         // if amp > s, we may be in the attack or in the prolonged decay curve
74         releasetime = (1 - amp / s) * r;
75
76         if(amp > s)
77         {
78                 if(s == 1) // gracefully handle division by zero here
79                         return 0;
80
81                 // pre-time decay
82                 // value is s at time  0
83                 //          1 at time -d
84                 decaytime = (amp - 1) / (s - 1) * d - d;
85                 return max(decaytime, releasetime);
86         }
87
88         return releasetime;
89 }
90
91 void BGMScript_Init()
92 {
93         string s;
94         float fh;
95         bgmscriptbuf = bgmscriptbufsize = 0;
96         bgmscriptbufloaded = 1;
97         s = strcat("maps/", mi_shortname, ".bgs");
98         fh = fopen(s, FILE_READ);
99         if(fh < 0)
100                 return;
101         bgmscriptbuf = buf_create();
102         while((s = fgets(fh)))
103         {
104                 bufstr_set(bgmscriptbuf, bgmscriptbufsize, s);
105                 ++bgmscriptbufsize;
106         }
107         fclose(fh);
108 }
109
110 void BGMScript_InitEntity(entity e)
111 {
112         float l;
113         string m;
114         if(e.bgmscript != "")
115         {
116                 if(!bgmscriptbufloaded)
117                         BGMScript_Init();
118
119                 float i;
120
121                 m = strcat(e.bgmscript, " ");
122                 l = strlen(m);
123
124                 e.bgmscriptline0 = -1;
125                 for(i = 0; i < bgmscriptbufsize; ++i)
126                 {
127                         if(substring(bufstr_get(bgmscriptbuf, i), 0, l) == m)
128                                 break;
129                 }
130                 e.bgmscriptline = e.bgmscriptline0 = i;
131                 if(i >= bgmscriptbufsize)
132                 {
133                         print("func_pointparticles: bgmscript does not define ", e.bgmscript, "\n");
134                         e.bgmscript = "";
135                 }
136         }
137 }
138
139 float BGMScript(entity e)
140 {
141         float t;
142         float amp;
143
144         if(e.bgmscript == "")
145                 return 1;
146         
147         if(cvar("bgmvolume") <= 0)
148                 return -1;
149
150         e.just_toggled = FALSE;
151
152         t = gettime(GETTIME_CDTRACK);
153         if(t < e.bgmscripttime)
154         {
155                 e.bgmscriptline = e.bgmscriptline0;
156                 e.bgmscripttime = 0;
157                 e.bgmscriptstatetime = t - drawframetime - e.bgmscriptdelta; // FIXME this causes a tiny hitch
158         }
159
160         // find the CURRENT line
161         for(;;)
162         {
163                 tokenize_sane(bufstr_get(bgmscriptbuf, e.bgmscriptline));
164                 if(stof(argv(1)) >= t)
165                         break;
166                 if(argv(0) != e.bgmscript)
167                 {
168                         // end of bgmscript, will revert to beginning later
169                         break;
170                 }
171                 else if(t >= stof(argv(1)))
172                 {
173                         e.bgmscriptline += 1;
174                         e.bgmscripttime = stof(argv(1));
175
176                         if(e.bgmscriptstate)
177                                 amp = GetAttackDecaySustainAmplitude(e.bgmscriptattack, e.bgmscriptdecay, e.bgmscriptsustain, e.bgmscripttime - e.bgmscriptstatetime) * e.bgmscriptvelocity;
178                         else
179                                 amp = GetReleaseAmplitude(e.bgmscriptdecay, e.bgmscriptsustain, e.bgmscriptrelease, e.bgmscripttime - e.bgmscriptstatetime);
180
181                         // time code reached!
182                         e.bgmscriptvelocity = stof(argv(2));
183                         if(e.bgmscriptvelocity > 0)
184                                 e.just_toggled = e.bgmscriptstate = TRUE;
185                         else
186                                 e.just_toggled = e.bgmscriptstate = FALSE;
187
188                         if(e.bgmscriptstate)
189                                 e.bgmscriptstatetime = e.bgmscripttime - GetAttackTime(e.bgmscriptattack, amp / e.bgmscriptvelocity);
190                         else
191                                 e.bgmscriptstatetime = e.bgmscripttime - GetReleaseTime(e.bgmscriptdecay, e.bgmscriptsustain, e.bgmscriptrelease, amp);
192                 }
193         }
194
195         if(e.bgmscriptstate)
196         {
197                 // attack, decay or sustain
198                 e.bgmscriptdelta = t - e.bgmscriptstatetime;
199                 return GetAttackDecaySustainAmplitude(e.bgmscriptattack, e.bgmscriptdecay, e.bgmscriptsustain, e.bgmscriptdelta) * e.bgmscriptvelocity;
200         }
201         else
202         {
203                 // release
204                 e.bgmscriptdelta = t - e.bgmscriptstatetime;
205                 return GetReleaseAmplitude(e.bgmscriptdecay, e.bgmscriptsustain, e.bgmscriptrelease, self.bgmscriptdelta);
206         }
207 }
208