1 //-------------------------------------------------------------------------
2 /*
3 Copyright (C) 1997, 2005 - 3D Realms Entertainment
4
5 This file is part of Shadow Warrior version 1.2
6
7 Shadow Warrior is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15
16 See the GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21
22 Original Source: 1997 - Frank Maddin and Jim Norwood
23 Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
24 */
25 //-------------------------------------------------------------------------
26
27 #include "compat.h"
28 #include "pragmas.h"
29
30 #include "game.h"
31 #include "interp.h"
32 #include "interpso.h"
33 #include "names2.h"
34
35 #define SO_MAXINTERPOLATIONS MAXINTERPOLATIONS
36
37 static struct so_interp
38 {
39 struct interp_data
40 {
41 void *curipos;
42 int32_t oldipos;
43 int32_t bakipos;
44 int32_t lastipos;
45 int32_t lastoldipos;
46 int32_t lastangdiff;
47 int32_t spriteofang;
48 } data[SO_MAXINTERPOLATIONS];
49
50 int32_t numinterpolations;
51 int32_t tic, lasttic;
52 SWBOOL hasvator;
53 } so_interpdata[MAX_SECTOR_OBJECTS];
54
so_setpointinterpolation(so_interp * interp,int32_t * posptr)55 static void so_setpointinterpolation(so_interp *interp, int32_t *posptr)
56 {
57 int32_t i;
58 if (interp->numinterpolations >= SO_MAXINTERPOLATIONS)
59 return;
60
61 for (i = 0; i < interp->numinterpolations; i++)
62 if (interp->data[i].curipos == posptr)
63 return;
64
65 so_interp::interp_data *data = &interp->data[interp->numinterpolations++];
66
67 data->curipos = posptr;
68 data->oldipos = *posptr;
69 data->lastipos = *posptr;
70 data->lastoldipos = *posptr;
71 data->spriteofang = -1;
72 }
73
so_setspriteanginterpolation(so_interp * interp,int16_t * posptr,int32_t spritenum)74 static void so_setspriteanginterpolation(so_interp *interp, int16_t *posptr, int32_t spritenum)
75 {
76 int32_t i;
77 if (interp->numinterpolations >= SO_MAXINTERPOLATIONS)
78 return;
79
80 for (i = 0; i < interp->numinterpolations; i++)
81 if (interp->data[i].curipos == posptr)
82 return;
83
84 so_interp::interp_data *data = &interp->data[interp->numinterpolations++];
85
86 data->curipos = posptr;
87 data->oldipos = *posptr;
88 data->lastipos = *posptr;
89 data->lastoldipos = *posptr;
90 data->lastangdiff = 0;
91 data->spriteofang = spritenum;
92 }
93
94 // Covers points and angles altogether
so_stopdatainterpolation(so_interp * interp,void * posptr)95 static void so_stopdatainterpolation(so_interp *interp, void *posptr)
96 {
97 int32_t i;
98
99 for (i = 0; i < interp->numinterpolations; i++)
100 if (interp->data[i].curipos == posptr)
101 break;
102
103 if (i == interp->numinterpolations)
104 return;
105
106 interp->data[i] = interp->data[--(interp->numinterpolations)];
107 }
108
so_addinterpolation(SECTOR_OBJECTp sop)109 void so_addinterpolation(SECTOR_OBJECTp sop)
110 {
111 SECTORp *sectp;
112 int32_t startwall, endwall;
113 int32_t i;
114
115 so_interp *interp = &so_interpdata[sop - SectorObject];
116 interp->numinterpolations = 0;
117 interp->hasvator = FALSE;
118
119 for (sectp = sop->sectp; *sectp; sectp++)
120 {
121 startwall = (*sectp)->wallptr;
122 endwall = startwall + (*sectp)->wallnum - 1;
123
124 for (i = startwall; i <= endwall; i++)
125 {
126 int32_t nextwall = wall[i].nextwall;
127
128 so_setpointinterpolation(interp, &wall[i].x);
129 so_setpointinterpolation(interp, &wall[i].y);
130
131 if (nextwall >= 0)
132 {
133 so_setpointinterpolation(interp, &wall[wall[nextwall].point2].x);
134 so_setpointinterpolation(interp, &wall[wall[nextwall].point2].y);
135 }
136 }
137
138 for (SPRITES_OF_SECT(*sectp - sector, i))
139 if (sprite[i].statnum == STAT_VATOR && SP_TAG1(sprite+i) == SECT_VATOR)
140 break;
141 interp->hasvator |= (i >= 0);
142 }
143
144 if (!interp->hasvator)
145 for (sectp = sop->sectp; *sectp; sectp++)
146 {
147 so_setpointinterpolation(interp, &(*sectp)->ceilingz);
148 so_setpointinterpolation(interp, &(*sectp)->floorz);
149 }
150
151 // interpolate midpoint, for aiming at a remote controlled SO
152 so_setpointinterpolation(interp, &sop->xmid);
153 so_setpointinterpolation(interp, &sop->ymid);
154 so_setpointinterpolation(interp, &sop->zmid);
155
156 interp->tic = 0;
157 interp->lasttic = synctics;
158 }
159
so_setspriteinterpolation(SECTOR_OBJECTp sop,spritetype * sp)160 void so_setspriteinterpolation(SECTOR_OBJECTp sop, spritetype *sp)
161 {
162 so_interp *interp = &so_interpdata[sop - SectorObject];
163
164 so_setpointinterpolation(interp, &sp->x);
165 so_setpointinterpolation(interp, &sp->y);
166 if (!interp->hasvator)
167 so_setpointinterpolation(interp, &sp->z);
168 so_setspriteanginterpolation(interp, &sp->ang, sp - sprite);
169 }
170
so_stopspriteinterpolation(SECTOR_OBJECTp sop,spritetype * sp)171 void so_stopspriteinterpolation(SECTOR_OBJECTp sop, spritetype *sp)
172 {
173 so_interp *interp = &so_interpdata[sop - SectorObject];
174
175 so_stopdatainterpolation(interp, &sp->x);
176 so_stopdatainterpolation(interp, &sp->y);
177 if (!interp->hasvator)
178 so_stopdatainterpolation(interp, &sp->z);
179 so_stopdatainterpolation(interp, &sp->ang);
180 }
181
so_setinterpolationtics(SECTOR_OBJECTp sop,int16_t locktics)182 void so_setinterpolationtics(SECTOR_OBJECTp sop, int16_t locktics)
183 {
184 so_interp *interp = &so_interpdata[sop - SectorObject];
185
186 interp->tic = 0;
187 interp->lasttic = locktics;
188 }
189
so_updateinterpolations(void)190 void so_updateinterpolations(void) // Stick at beginning of domovethings
191 {
192 int32_t i;
193 SECTOR_OBJECTp sop;
194 so_interp *interp;
195 so_interp::interp_data *data;
196 SWBOOL interpolating = gs.InterpolateSO && !CommEnabled; // If changing from menu
197
198 for (sop = SectorObject, interp = so_interpdata;
199 sop < &SectorObject[MAX_SECTOR_OBJECTS]; sop++, interp++)
200 {
201 if (SO_EMPTY(sop))
202 continue;
203 if (interp->tic < interp->lasttic)
204 interp->tic += synctics;
205 for (i = 0, data = interp->data; i < interp->numinterpolations; i++, data++)
206 {
207 if (data->spriteofang >= 0)
208 {
209 USERp u = User[data->spriteofang];
210 if (u)
211 u->oangdiff = 0;
212 if (!interpolating)
213 data->lastangdiff = 0;
214 data->oldipos = *(int16_t *)(data->curipos);
215 }
216 else
217 data->oldipos = *(int32_t *)(data->curipos);
218
219 if (!interpolating)
220 data->lastipos = data->lastoldipos = data->oldipos;
221 }
222 }
223 }
224
225 // must call restore for every do interpolations
226 // make sure you don't exit
so_dointerpolations(int32_t smoothratio)227 void so_dointerpolations(int32_t smoothratio) // Stick at beginning of drawscreen
228 {
229 int32_t i, delta;
230 SECTOR_OBJECTp sop;
231 so_interp *interp;
232 so_interp::interp_data *data;
233
234 // Set the bakipos values separately, in case a point is shared.
235 // Also set lastipos if there's been an actual change in a point.
236 for (sop = SectorObject, interp = so_interpdata;
237 sop < &SectorObject[MAX_SECTOR_OBJECTS]; sop++, interp++)
238 {
239 if (SO_EMPTY(sop))
240 continue;
241
242 for (i = 0; i < interp->numinterpolations; i++)
243 interp->data[i].bakipos = (interp->data[i].spriteofang >= 0) ?
244 *(int16_t *)(interp->data[i].curipos) :
245 *(int32_t *)(interp->data[i].curipos);
246
247 if (interp->tic == 0) // Only if the SO has just moved
248 {
249 for (i = 0, data = interp->data; i < interp->numinterpolations; i++, data++)
250 {
251 data->lastipos = data->bakipos;
252 data->lastoldipos = data->oldipos;
253 if (data->spriteofang >= 0)
254 {
255 USERp u = User[data->spriteofang];
256 data->lastangdiff = u ? u->oangdiff : 0;
257 }
258 }
259 }
260 }
261
262 for (sop = SectorObject, interp = so_interpdata;
263 sop < &SectorObject[MAX_SECTOR_OBJECTS]; sop++, interp++)
264 {
265 if (SO_EMPTY(sop))
266 continue;
267
268 // Check if interpolation has been explicitly disabled
269 if (interp->lasttic == 0)
270 continue;
271
272 // Unfortunately, interpolating over less samples doesn't work well
273 // in multiplayer. We also skip any sector object not
274 // remotely controlled by some player.
275 if (CommEnabled &&
276 ((interp->lasttic != synctics) ||
277 !(sop->controller) ||
278 ((Player[screenpeek].sop_control == sop) &&
279 !Player[screenpeek].sop_remote)))
280 continue;
281
282 int32_t ratio = smoothratio * synctics + 65536 * interp->tic;
283 ratio /= interp->lasttic;
284 ratio = (interp->tic == interp->lasttic) ? 65536 : ratio;
285
286 for (i = 0, data = interp->data; i < interp->numinterpolations; i++, data++)
287 {
288 // Hack for jittery coolies in level 1's train.
289 // Based in idea on code from draw.cpp:analyzesprites.
290 // TODO: It could be better. In particular, it could be better
291 // to conditionally disable the interpolation from analyzesprites
292 // instead, using TSPRITE info if possible.
293 if (((uintptr_t)(data->curipos) >= (uintptr_t)sprite) &&
294 ((uintptr_t)(data->curipos) < (uintptr_t)(sprite + Numsprites)))
295 {
296 int32_t sprnum = ((char *)data->curipos - (char *)sprite) / sizeof(*sprite);
297 USERp u = User[sprnum];
298 if (u && (sprite[sprnum].statnum != STAT_DEFAULT) &&
299 ((TEST(u->Flags, SPR_SKIP4) && (sprite[sprnum].statnum <= STAT_SKIP4_INTERP_END)) ||
300 (TEST(u->Flags, SPR_SKIP2) && (sprite[sprnum].statnum <= STAT_SKIP2_INTERP_END))))
301 continue;
302 }
303
304 if (data->spriteofang >= 0)
305 *(int16_t *)(data->curipos) = NORM_ANGLE(data->lastoldipos + mulscale16(data->lastangdiff, ratio));
306 else
307 {
308 delta = data->lastipos - data->lastoldipos;
309 *(int32_t *)(data->curipos) = data->lastoldipos + mulscale16(delta, ratio);
310 }
311 }
312 }
313 }
314
so_restoreinterpolations(void)315 void so_restoreinterpolations(void) // Stick at end of drawscreen
316 {
317 int32_t i;
318 SECTOR_OBJECTp sop;
319 so_interp *interp;
320 so_interp::interp_data *data;
321
322 for (sop = SectorObject, interp = so_interpdata;
323 sop < &SectorObject[MAX_SECTOR_OBJECTS]; sop++, interp++)
324 {
325 if (SO_EMPTY(sop))
326 continue;
327
328 for (i = 0, data = interp->data; i < interp->numinterpolations; i++, data++)
329 if (data->spriteofang >= 0)
330 *(int16_t *)(data->curipos) = data->bakipos;
331 else
332 *(int32_t *)(data->curipos) = data->bakipos;
333 }
334 }
335
336 int SaveSymDataInfo(MFILE_WRITE fil, void *ptr);
337
so_writeinterpolations(MFILE_WRITE fil)338 SWBOOL so_writeinterpolations(MFILE_WRITE fil)
339 {
340 int32_t i;
341 SECTOR_OBJECTp sop;
342 const so_interp *interp;
343 SWBOOL saveisshot = FALSE;
344
345 for (sop = SectorObject, interp = so_interpdata;
346 sop < &SectorObject[MAX_SECTOR_OBJECTS]; sop++, interp++)
347 {
348 const so_interp::interp_data *data = interp->data;
349 MWRITE(&interp->numinterpolations,sizeof(interp->numinterpolations),1,fil);
350 MWRITE(&interp->hasvator,sizeof(interp->hasvator),1,fil);
351 for (i = 0; i < interp->numinterpolations; i++, data++)
352 {
353 saveisshot |= SaveSymDataInfo(fil, data->curipos);
354 MWRITE(&data->oldipos,sizeof(data->oldipos),1,fil);
355 MWRITE(&data->spriteofang,sizeof(data->spriteofang),1,fil);
356 }
357 }
358 return saveisshot;
359 }
360
361 int LoadSymDataInfo(MFILE_READ fil, void **ptr);
362
so_readinterpolations(MFILE_READ fil)363 SWBOOL so_readinterpolations(MFILE_READ fil)
364 {
365 int32_t i;
366 SECTOR_OBJECTp sop;
367 so_interp *interp;
368 SWBOOL saveisshot = FALSE;
369
370 for (sop = SectorObject, interp = so_interpdata;
371 sop < &SectorObject[MAX_SECTOR_OBJECTS]; sop++, interp++)
372 {
373 so_interp::interp_data *data = interp->data;
374 MREAD(&interp->numinterpolations,sizeof(interp->numinterpolations),1,fil);
375 MREAD(&interp->hasvator,sizeof(interp->hasvator),1,fil);
376 for (i = 0; i < interp->numinterpolations; i++, data++)
377 {
378 saveisshot |= LoadSymDataInfo(fil, (void **)&data->curipos);
379 MREAD(&data->oldipos,sizeof(data->oldipos),1,fil);
380 MREAD(&data->spriteofang,sizeof(data->spriteofang),1,fil);
381 data->lastipos = data->lastoldipos = data->oldipos;
382 data->lastangdiff = 0;
383 }
384 interp->tic = 0;
385 interp->lasttic = synctics;
386 }
387 return saveisshot;
388 }
389