Purge client/defs.qh
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / bgmscript.qc
1 #include "bgmscript.qh"
2
3 #include <common/util.qh>
4 #include <client/autocvars.qh>
5 #include <client/main.qh>
6
7 #define CONSTANT_SPEED_DECAY
8
9 float bgmscriptbuf;
10 float bgmscriptbufsize;
11 float bgmscriptbufloaded;
12
13 classfield(BGMScript) .float bgmscriptline;
14 classfield(BGMScript) .float bgmscriptline0;
15 classfield(BGMScript) .float bgmscriptvolume;
16 classfield(BGMScript) .float bgmscripttime;
17 classfield(BGMScript) .float bgmscriptstate;
18 classfield(BGMScript) .float bgmscriptstatetime;
19
20 float GetAttackDecaySustainAmplitude(float a, float d, float s, float t)
21 {
22         // phase:
23         //   attack: from 0 to 1, in time a for a full length
24         //   decay: from 1 to s, in time d
25         //   sustain: s
26
27         if(t < 0)
28                 return 0;
29
30         if(a)
31                 if(t <= a)
32                         return t / a;
33
34         if(d)
35                 if(t <= a + d)
36                         return ((t - a) / d) * (s - 1) + 1;
37
38         return s;
39 }
40
41 float GetReleaseAmplitude(float d, float s, float r, float t)
42 {
43         float decayval, releaseval;
44
45         if(!r)
46                 return 0;
47
48         if(t > r)
49                 return 0;
50
51         releaseval = s * (1 - t / r);
52
53         if(t < -d)
54                 return 1;
55
56         if(t < 0 && t >= -d)
57         {
58                 // pre-time decay
59                 // value is s at time  0
60                 //          1 at time -d
61                 decayval = ((t + d) / d) * (s - 1) + 1;
62                 return max(decayval, releaseval);
63         }
64
65         return releaseval;
66 }
67
68 float GetAttackTime(float a, float amp)
69 {
70         return amp * a;
71 }
72
73 float GetReleaseTime(float d, float s, float r, float amp)
74 {
75         float decaytime, releasetime;
76
77         if(!s)
78                 return 0;
79
80         // if amp > s, we may be in the attack or in the prolonged decay curve
81         releasetime = (1 - amp / s) * r;
82
83         if(amp > s)
84         {
85                 if(s == 1) // gracefully handle division by zero here
86                         return 0;
87
88                 // pre-time decay
89                 // value is s at time  0
90                 //          1 at time -d
91                 decaytime = (amp - 1) / (s - 1) * d - d;
92                 return max(decaytime, releasetime);
93         }
94
95         return releasetime;
96 }
97
98 void BGMScript_Init()
99 {
100         string s;
101         float fh;
102         bgmscriptbuf = bgmscriptbufsize = 0;
103         bgmscriptbufloaded = 1;
104         s = strcat("maps/", mi_shortname, ".bgs");
105         fh = fopen(s, FILE_READ);
106         if(fh < 0)
107                 return;
108         bgmscriptbuf = buf_create();
109         while((s = fgets(fh)))
110         {
111                 bufstr_set(bgmscriptbuf, bgmscriptbufsize, s);
112                 ++bgmscriptbufsize;
113         }
114         fclose(fh);
115 }
116
117 void BGMScript_InitEntity(entity e)
118 {
119         float l;
120         string m;
121         if(e.bgmscript != "")
122         {
123                 if(!bgmscriptbufloaded)
124                         BGMScript_Init();
125
126                 float i;
127
128                 m = strcat(e.bgmscript, " ");
129                 l = strlen(m);
130
131                 e.bgmscriptline0 = -1;
132                 for(i = 0; i < bgmscriptbufsize; ++i)
133                 {
134                         if(substring(bufstr_get(bgmscriptbuf, i), 0, l) == m)
135                                 break;
136                 }
137                 e.bgmscriptline = e.bgmscriptline0 = i;
138                 if(i >= bgmscriptbufsize)
139                 {
140                         LOG_INFOF("ERROR: bgmscript does not define %s", e.bgmscript);
141                         strfree(e.bgmscript);
142                 }
143         }
144 }
145
146 float GetCurrentAmplitude(entity e, float trel)
147 {
148         if(e.bgmscriptstate)
149                 return GetAttackDecaySustainAmplitude(e.bgmscriptattack, e.bgmscriptdecay, e.bgmscriptsustain, trel) * e.bgmscriptvolume;
150         else
151         {
152 #ifdef CONSTANT_SPEED_DECAY
153                 return GetReleaseAmplitude(e.bgmscriptdecay, e.bgmscriptsustain * e.bgmscriptvolume, e.bgmscriptrelease, trel);
154 #else
155                 return GetReleaseAmplitude(e.bgmscriptdecay, e.bgmscriptsustain, e.bgmscriptrelease, trel) * e.bgmscriptvolume;
156 #endif
157         }
158 }
159
160 float GetTimeForAmplitude(entity e, float amp)
161 {
162         if(e.bgmscriptstate)
163                 return GetAttackTime(e.bgmscriptattack, amp / e.bgmscriptvolume);
164         else
165         {
166 #ifdef CONSTANT_SPEED_DECAY
167                 return GetReleaseTime(e.bgmscriptdecay, e.bgmscriptsustain * e.bgmscriptvolume, e.bgmscriptrelease, amp);
168 #else
169                 return GetReleaseTime(e.bgmscriptdecay, e.bgmscriptsustain, e.bgmscriptrelease, amp / e.bgmscriptvolume);
170 #endif
171         }
172 }
173
174 float doBGMScript(entity e)
175 {
176         float amp, vel;
177
178         if(e.bgmscript == "")
179                 return 1;
180
181         if(autocvar_bgmvolume <= 0)
182                 return -1;
183
184         e.just_toggled = false;
185
186         if(bgmtime < 0)
187                 return -1;
188
189         if(bgmtime < e.bgmscripttime)
190         {
191                 amp = GetCurrentAmplitude(e, e.bgmscripttime - e.bgmscriptstatetime + drawframetime);
192
193                 e.bgmscriptline = e.bgmscriptline0;
194                 e.bgmscripttime = bgmtime;
195
196                 // treat this as a stop event for all notes, to prevent sticking keys
197                 e.bgmscriptstate = false;
198                 e.bgmscriptvolume = 1;
199                 e.bgmscriptstatetime = bgmtime - GetTimeForAmplitude(e, amp);
200         }
201
202         // find the CURRENT line
203         for (;;)
204         {
205                 tokenize_console(bufstr_get(bgmscriptbuf, e.bgmscriptline));
206                 if(stof(argv(1)) >= bgmtime || argv(0) != e.bgmscript)
207                 {
208                         e.bgmscripttime = bgmtime;
209                         return GetCurrentAmplitude(e, bgmtime - e.bgmscriptstatetime);
210                 }
211                 else if(bgmtime >= stof(argv(1)))
212                 {
213                         e.bgmscriptline += 1;
214                         e.bgmscripttime = stof(argv(1));
215
216                         amp = GetCurrentAmplitude(e, e.bgmscripttime - e.bgmscriptstatetime);
217
218                         // time code reached!
219                         vel = stof(argv(2));
220                         if(vel > 0)
221                         {
222                                 e.just_toggled = e.bgmscriptstate = true;
223                                 e.bgmscriptvolume = vel;
224                         }
225                         else
226                                 e.just_toggled = e.bgmscriptstate = false;
227
228                         e.bgmscriptstatetime = e.bgmscripttime - GetTimeForAmplitude(e, amp);
229                 }
230         }
231 }
232