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