1 /*
2 
3 Copyright (C) 2015-2018 Night Dive Studios, LLC.
4 
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 
18 */
19 /*
20  * $Source: r:/prj/cit/src/RCS/tfdirect.c $
21  * $Revision: 1.16 $
22  * $Author: dc $
23  * $Date: 1994/09/08 06:30:34 $
24  */
25 
26 #define __TFDIRECT_SRC
27 #include <stdlib.h>
28 #include <string.h>
29 
30 // pretty cool set of header files, eh?
31 #include "tfdirect.h" // ditto, yep yep yep
32 #include "ss_flet.h"  // for everything, basically, constants mostly
33 #include "map.h"      // for MAP_HEIGHTS, must be'fore frintern for fdt_mptr
34 #include "mapflags.h" // for light_flr and light_ceil
35 #include "objects.h"  // for ObjsClearDealt
36 #include "frintern.h" // for _fdt_x, _y and _mptr
37 // gruesome annoyance to destroy projectiles
38 #include "damage.h"
39 #include "objsim.h"
40 #include "objbit.h"
41 #include "objprop.h"
42 
43 extern void tile_hit(int mx, int my);
44 
45 #define USE_OLD_PASSING
46 //#define SAFETY_RETURN
47 //#define DIAGONAL_CORNERS
48 #define STAIRS_NEAR_THE_TOP
49 #define STAIRS_ABOVE_DA_TOP
50 
51 // i love us
52 uchar v_to_cur[] = {
53     (SS_BCD_CURR_E | SS_BCD_CURR_LOW) >> SS_BCD_CURR_SHF,    (SS_BCD_CURR_W | SS_BCD_CURR_LOW) >> SS_BCD_CURR_SHF,
54     (SS_BCD_CURR_N | SS_BCD_CURR_LOW) >> SS_BCD_CURR_SHF,    (SS_BCD_CURR_S | SS_BCD_CURR_LOW) >> SS_BCD_CURR_SHF,
55     (SS_BCD_CURR_E | SS_BCD_CURR_MID) >> SS_BCD_CURR_SHF,    (SS_BCD_CURR_W | SS_BCD_CURR_MID) >> SS_BCD_CURR_SHF,
56     (SS_BCD_CURR_N | SS_BCD_CURR_MID) >> SS_BCD_CURR_SHF,    (SS_BCD_CURR_S | SS_BCD_CURR_MID) >> SS_BCD_CURR_SHF,
57     (SS_BCD_CURR_E | SS_BCD_CURR_HIGH) >> SS_BCD_CURR_SHF,   (SS_BCD_CURR_W | SS_BCD_CURR_HIGH) >> SS_BCD_CURR_SHF,
58     (SS_BCD_CURR_N | SS_BCD_CURR_HIGH) >> SS_BCD_CURR_SHF,   (SS_BCD_CURR_S | SS_BCD_CURR_HIGH) >> SS_BCD_CURR_SHF,
59     (SS_BCD_REPUL_UP | SS_BCD_CURR_LOW) >> SS_BCD_CURR_SHF,  (SS_BCD_REPUL_DOWN | SS_BCD_CURR_LOW) >> SS_BCD_CURR_SHF,
60     (SS_BCD_REPUL_UP | SS_BCD_CURR_MID) >> SS_BCD_CURR_SHF,  (SS_BCD_REPUL_DOWN | SS_BCD_CURR_MID) >> SS_BCD_CURR_SHF,
61     (SS_BCD_REPUL_UP | SS_BCD_CURR_HIGH) >> SS_BCD_CURR_SHF, (SS_BCD_REPUL_DOWN | SS_BCD_CURR_HIGH) >> SS_BCD_CURR_SHF
62 };
63 
64 // Local Prototypes
65 uchar _tf_set_flet(int flags, fix att, fix dist, fix *norm);
66 uchar _tf_internal_chk(void);
67 fix _tf_border_check_2d(void);
68 void _tf_norm_create(void);
69 void _tf_get_crosses(void);
70 fix tf_solve_2d_case(int flags);
71 int _stair_check(fix walls[4][2], int flags);
72 void terrfunc_one_map_square(int fmask);
73 TerrainHit tf_direct(fix fix_x, fix fix_y, fix fix_z, fix rad, int ph, TFType tf_type);
74 
75 // old style physics...
76 extern TerrainData terrain_info;
77 
78 // passing globals
79 ss_facelet_return ss_edms_facelets[SS_MAX_FACELETS];
80 uchar ss_edms_facelet_cnt;
81 int ss_edms_bcd_flags;
82 int ss_edms_bcd_param;
83 
84 // globals...
85 fix (*tf_vert_2d)[2]; // 2d vertices of the face, when reset
86 char tf_norm_hnts[4]; // normal hints for strange param stuff
87 fix *tf_pt;           // 3 elements: first 2 in plane, 3 is distance from plane
88 fix tf_loc_pt[3];     // localized relative to current map tile
89 fix tf_raw_pt[3];     // raw world location of object
90 int tf_ph;            // current physics handle
91 fix tf_rad;           // rad of current physics'ed object
92 
93 // locals
94 static fix tf_norm_2d[4][2];             // computed normals
95 static char cross_face[4][2], cross_cnt; // which face is being crossed, count
96 static char tf_pcnt;                     // points for working face
97 static fix tf_cur_rad;                   // obj radius for this solve, changes if remetriced, say
98 
99 // sneaky sneaky, locals which arent, im such a bad person
100 #define tfunc_mptr  _fdt_mptr
101 #define tfunc_map_x _fdt_x
102 #define tfunc_map_y _fdt_y
103 
104 static fix tfunc_minz, tfunc_maxz;
105 
106 // no unreachables...
107 // dbg system stuff
108 
109 #ifdef TF_TALK_SYSTEM
110 
111 #define FletList (1 << 0)
112 #define FletSet  (1 << 1)
113 #define IntChk   (1 << 2)
114 #define BordChk  (1 << 3)
115 #define AlignFce (1 << 4)
116 #define RemetFce (1 << 5)
117 #define Grab     (1 << 6)
118 #define Calls    (1 << 7)
119 #define Area     (1 << 8)
120 #define Ret      (1 << 9)
121 #define Cylinder (1 << 10)
122 
123 //#define DEFAULT_TALK        Ret|FletList
124 #define DEFAULT_TALK 0
125 #define TF_TALK_STATICS 0xffff
126 
127 int tf_talk = DEFAULT_TALK, tf_tmp;
128 
129 #define tf_talk_setup()    tf_talk = DEFAULT_TALK
130 #define tf_turn_on(flg)    tf_tmp = tf_talk, tf_talk |= (flg)
131 #define tf_talk_check(flg) ((flg & TF_TALK_STATICS) && (tf_talk & flg))
132 #define tf_undo_set(flg)   tf_talk = tf_tmp | (flg)
133 
134 #define do_tf_Spew(flg, dat) mprintf dat
135 #define tf_Spew(flg, dat)   \
136     if (tf_talk_check(flg)) \
137     do_tf_Spew(flg, dat)
138 #else
139 #define tf_talk_setup()
140 #define tf_turn_on(flg)
141 #define tf_talk_check(flg) FALSE
142 #define tf_undo_set(flg)
143 #define do_tf_Spew(flg, dat)
144 #define tf_Spew(flg, dat)
145 #endif
146 #define tf_Stat(dat)
147 #define terrfunc_it_calls_inc()
148 
149 // renderer stuff we call
150 extern void fr_tfunc_grab_fast(int mask);
151 
152 #define _tf_list_flet()
153 
154 // here we need the tmap/size parser stuff to happen....
155 
156 // actually output a facelet
_tf_set_flet(int flags,fix att,fix dist,fix * norm)157 uchar _tf_set_flet(int flags, fix att, fix dist, fix *norm) {
158     fix full_norms[2] = {fix_make(1, 0), -fix_make(1, 0)};
159     int pv;
160     ss_facelet_return *cur_fc = &ss_edms_facelets[ss_edms_facelet_cnt++];
161 
162     tf_Spew(FletSet, ("Set %d.. vals %x %x %x, norm %x %x %x\n", ss_edms_facelet_cnt - 1, flags, att, dist,
163                       norm != NULL ? norm[0] : 0xb, norm != NULL ? norm[1] : 0xa, norm != NULL ? norm[2] : 0xd));
164     if (ss_edms_facelet_cnt >= SS_MAX_FACELETS)
165         return FALSE;
166     cur_fc->flags = flags;
167     cur_fc->att = att;
168     cur_fc->comp = tf_rad - dist;
169     switch (flags & SS_BCD_AXIS_MASK) {
170     case SS_BCD_PRIM_MULTI:
171         *(g3s_vector *)cur_fc->norm = *(g3s_vector *)norm; //    _memcpy32l(cur_fc->norm,norm,3);
172 #ifdef USE_OLD_PASSING
173         goto i_hate_everyone;
174 #endif
175         return TRUE;
176     case SS_BCD_PRIM_XAXIS:
177         pv = 0;
178         break;
179     case SS_BCD_PRIM_YAXIS:
180         pv = 1;
181         break;
182     case SS_BCD_PRIM_ZAXIS:
183         pv = 2;
184         break;
185     }
186 
187     LG_memset(cur_fc->norm, 0, 3 * 4); //  _memset32l(cur_fc->norm,0,3);
188     cur_fc->norm[pv] = full_norms[flags & SS_BCD_PRIM_NEG];
189 #ifdef USE_OLD_PASSING
190 i_hate_everyone : {
191     int which, prim;
192     which = ((flags & SS_BCD_TYPE_MASK) == SS_BCD_TYPE_WALL)
193                 ? 2
194                 : (((flags & SS_BCD_TYPE_MASK) == SS_BCD_TYPE_CEIL) ? 0 : 1);
195     prim = ((flags & SS_BCD_AXIS_MASK) >> 1) - 1;
196     if (prim == -1)
197         prim = FCE_NO_PRIM;
198     facelet_add(which, cur_fc->norm, cur_fc->att, cur_fc->comp, prim);
199     //      if ((flags&SS_BCD_MISC_STAIR)&&(cur_fc->att!=fix_1))
200     //         flags&=~SS_BCD_MISC_STAIR;    // no stair bit when attenuated
201     ss_edms_bcd_flags |= flags;
202 }
203 #endif
204     //   if (ss_edms_bcd_flags&SS_BCD_MISC_CLIMB)
205     //      tf_talk|=Ret|FletList;
206     return TRUE;
207 }
208 
209 // tf_internal_chk...
_tf_internal_chk(void)210 uchar _tf_internal_chk(void) {
211     fix dp[2];
212     dp[0] = fix_mul(tf_norm_2d[cross_face[0][0]][0], (tf_pt[0] - tf_vert_2d[cross_face[0][1]][0])) +
213             fix_mul(tf_norm_2d[cross_face[0][0]][1], (tf_pt[1] - tf_vert_2d[cross_face[0][1]][1]));
214     dp[1] = fix_mul(tf_norm_2d[cross_face[1][0]][0], (tf_pt[0] - tf_vert_2d[cross_face[1][1]][0])) +
215             fix_mul(tf_norm_2d[cross_face[1][0]][1], (tf_pt[1] - tf_vert_2d[cross_face[1][1]][1]));
216     tf_Spew(IntChk, ("tic: %x and %x - cross faces %d and %d\n", dp[0], dp[1], cross_face[0][0], cross_face[1][0]));
217     return ((dp[0] > 0) && (dp[1] > 0));
218 }
219 
220 // solver for border case check...
221 
222 // unit-normals version
223 
224 // non-unit normals version
225 // behind the edge? set and then check to see if valid range
226 #define set_n_chk_1(val)                              \
227     _1d_pt[1] = (val);                                \
228     if ((_1d_pt[1] > 0) || (_1d_pt[1] < -tf_cur_rad)) \
229     continue
230 
231 // f_dist is distance away in 3rd dimension, cnt is number of points
232 // sign is sorta unknown, as f_dist is distance, not direction, hmmm...
_tf_border_check_2d(void)233 fix _tf_border_check_2d(void) {
234     fix _1d_pt[2], _1d_endpt[2];
235     int i;
236     for (i = 0; i < tf_pcnt; i++) {
237         if ((tf_norm_2d[i][0] != 0) &&
238             (tf_norm_2d[i][1] != 0)) { // do the real case, have to learn about cross clock, i think
239             fix nfac, c_pt[2];
240             tf_Spew(BordChk,
241                     ("gc2d: face %d fixing to solve real case w %x %x\n", i, tf_norm_2d[i][0], tf_norm_2d[i][1]));
242             c_pt[0] = tf_pt[0] - tf_vert_2d[i][0];
243             c_pt[1] = tf_pt[1] - tf_vert_2d[i][1];
244             _1d_pt[1] = fix_mul(tf_norm_2d[i][0], c_pt[0]) + fix_mul(tf_norm_2d[i][1], c_pt[1]);
245             if (_1d_pt[1] >= 0)
246                 continue; // behind the edge, here we can only check behind, as we are unnormalized as of yet
247             _1d_pt[0] = fix_mul(tf_norm_2d[i][1], c_pt[0]) - fix_mul(tf_norm_2d[i][0], c_pt[1]);
248             _1d_endpt[0] = fix_mul(tf_norm_2d[i][1], tf_norm_2d[i][1]) + fix_mul(tf_norm_2d[i][0], tf_norm_2d[i][0]);
249             nfac = fix_sqrt(_1d_endpt[0]); // well, its all true, sadly, need to normalize
250             _1d_pt[1] = fix_div(_1d_pt[1], nfac);
251             if (_1d_pt[1] < -tf_cur_rad)
252                 continue; // too far away
253             if ((_1d_pt[0] > 0) && (_1d_pt[0] < _1d_endpt[0]))
254                 return -_1d_pt[0]; // distance from facelet
255             else {                 // finish setting up the 1d case
256                 _1d_pt[0] = fix_div(_1d_pt[0], nfac);
257                 _1d_endpt[0] = fix_div(_1d_endpt[0], nfac);
258             }
259         } else // is clock gnosis correct or not? who the hell knows...
260         {
261             tf_Spew(BordChk, ("gc2: face %d simp case nrms %x %x\n", i, tf_norm_2d[i][0], tf_norm_2d[i][1]));
262             if (tf_norm_2d[i][0] == 0)    // north/south normal
263                 if (tf_norm_2d[i][1] > 0) // this is the east facing edge, north facing interior normal
264                 {
265                     set_n_chk_1(tf_pt[1] - tf_vert_2d[i][1]);
266                     _1d_pt[0] = tf_vert_2d[i][0] - tf_pt[0];
267                     _1d_endpt[0] = tf_norm_2d[i][1];
268                 } else // south facing normal, ie. invert everything, perhaps this could be cooler, eh?
269                 {
270                     set_n_chk_1(tf_vert_2d[i][1] - tf_pt[1]);
271                     _1d_pt[0] = tf_pt[0] - tf_vert_2d[i][0];
272                     _1d_endpt[0] = -tf_norm_2d[i][1];
273                 }
274             else                          // east/west normal
275                 if (tf_norm_2d[i][0] > 0) // this is the south facing edge, east facing interior normal
276             {
277                 set_n_chk_1(tf_pt[0] - tf_vert_2d[i][0]);
278                 _1d_pt[0] = tf_pt[1] - tf_vert_2d[i][1];
279                 _1d_endpt[0] = tf_norm_2d[i][0];
280             } else {
281                 set_n_chk_1(tf_vert_2d[i][0] - tf_pt[0]);
282                 _1d_pt[0] = tf_vert_2d[i][1] - tf_pt[1];
283                 _1d_endpt[0] = -tf_norm_2d[i][0];
284             }
285         }
286         // ok, if we are here, we have already made sure _1d_pt[1] is tween 0 and -tf_cur_rad
287         // now we hack it totally for now, since we are lame... basically, just grow square
288         tf_Spew(BordChk, ("gc2: face %d 1d case pt0 %x pt1 %x endpt0 %x\n", i, _1d_pt[0], _1d_pt[1], _1d_endpt[0]));
289 #ifdef DIAGONAL_CORNERS
290         if ((_1d_pt[0] >= -tf_cur_rad + tf_pt[2]) &&
291             (_1d_pt[0] <= _1d_endpt[0] + tf_cur_rad - tf_pt[2])) // over the attenuated facelet
292             return -_1d_pt[1];
293 #else
294         if ((_1d_pt[0] >= -tf_cur_rad) && (_1d_pt[0] <= _1d_endpt[0] + tf_cur_rad)) // over the attenuated facelet
295             return -_1d_pt[1];
296 #endif
297     }
298     return 0;
299 }
300 
301 // create normal set from vectors
302 #define set_norm(from, to)                                         \
303     tf_norm_2d[from][0] = tf_vert_2d[to][1] - tf_vert_2d[from][1]; \
304     tf_norm_2d[from][1] = tf_vert_2d[from][0] - tf_vert_2d[to][0]
305 
_tf_norm_create(void)306 void _tf_norm_create(void) {
307     int i;
308     for (i = 0; i < tf_pcnt - 1; i++) {
309         set_norm(i, i + 1);
310     }
311     set_norm(i, 0);
312 }
313 
314 #define cross_check(from, to)                         \
315     if ((tf_vert_2d[to][0] < tf_pt[0]) != cross_lr) { \
316         cross_lr = !cross_lr;                         \
317         cross_face[cross_cnt][0] = from;              \
318         cross_face[cross_cnt][1] = to;                \
319         cross_cnt++;                                  \
320     }
321 
_tf_get_crosses(void)322 void _tf_get_crosses(void) {
323     int i, cross_lr;
324 
325     cross_cnt = 0;
326     cross_lr = tf_vert_2d[0][0] < tf_pt[0];
327 
328     for (i = 0; i < tf_pcnt - 1; i++) {
329         cross_check(i, i + 1);
330     }
331     cross_check(i, 0);
332 }
333 
334 /* tf_solve_2d_case
335  *  ok, the solver for facelet sets
336  *  note... several things
337  *   1st: 5 vertices must be in the facelet list
338  *   2nd: box hints are used in the first pass and then split up
339  *   3rd: prebuilt normal/params set somehow? xtra params?
340  *   4th: pointlist is clockwise (from upper left if a box, else anywhere)
341  *
342  *  first check for trivial cases (note easy box test and such)
343  *  next build normals if necessary
344  *  then do real internal check if triv case failed
345  *  then the real border case if we have to
346  *
347  *  returns the attenuation, so 0 means not involved
348  */
349 
350 // todo: finish/optimize box cases
351 //       get min/max during norm or cross create, then triv check prior to border mess
352 
tf_solve_2d_case(int flags)353 fix tf_solve_2d_case(int flags) {
354     fix atv;
355     tf_pcnt = (flags & TF_FLG_3PNT_MASK) ? 3 : 4;
356     if (flags & TF_FLG_BOX_MASK) {
357         if (flags & TF_FLG_BOX_FULL) // really should do a standard point clip
358         {                            // do box internal/external check, set flag
359             fix xd, yd, aval;
360             xd = tf_vert_2d[0][0] - tf_pt[0]; // off left side?
361             if (xd < 0)
362                 xd = tf_pt[0] - tf_vert_2d[2][0]; // if not, right?
363             if (xd < 0)
364                 xd = 0;                       // in middle
365             yd = tf_vert_2d[2][1] - tf_pt[1]; // now top bottom
366             if (yd < 0)
367                 yd = tf_pt[1] - tf_vert_2d[0][1]; // note reverse of 2 and 0 since
368             if (yd < 0)
369                 yd = 0; //  cartesian in lower left
370 #ifndef SET_FLAGS_ON_BOX
371             if ((xd | yd) == 0)
372                 return fix_make(1, 0); // flags|=TF_FLG_ICHK_INT;
373             aval = (xd > yd) ? xd : yd;
374             if (aval > tf_cur_rad)
375                 return 0;                                  // flags|=TF_FLG_ICHK_OUT;
376             return fix_div(tf_cur_rad - aval, tf_cur_rad); // flags|=TF_FLG_ICHK_EDGE;
377 #else
378             if ((xd | yd) == 0)
379                 flags |= TF_FLG_ICHK_INT;
380             else {
381                 aval = (xd > yd) ? xd : yd;
382                 if (aval > tf_cur_rad)
383                     flags |= TF_FLG_ICHK_OUT;
384                 else {
385                     flags |= TF_FLG_ICHK_EDGE;
386                     return fix_div(tf_cur_rad - aval, tf_cur_rad);
387                 }
388             }
389             goto parse_ichk;
390 #endif
391         }
392     }
393 
394     // now set normals, really should check nhint and then do the right thing(tm)
395     //  for now, create non-unitized normals from vertices, someday should do norm hints...
396     _tf_norm_create();
397 
398     // if we havent gotten a real ichk value...
399     if ((flags & TF_FLG_ICHK_MASK) == 0) {
400         _tf_get_crosses();
401         if ((cross_cnt == 2) && _tf_internal_chk())
402             flags |= TF_FLG_ICHK_INT;
403         else
404             flags |= TF_FLG_ICHK_EDGE;
405     }
406 
407 #ifdef SET_FLAGS_ON_BOX
408 parse_ichk:
409 #endif
410     switch (flags & TF_FLG_ICHK_MASK) {
411     case TF_FLG_ICHK_INT: // return distance, set struct and all
412         return fix_make(1, 0);
413     case TF_FLG_ICHK_EDGE: // are we close enough?
414         break;             // fall through to attentuation case
415     case TF_FLG_ICHK_NONE:
416         WARN("%s: tfd: no ichk data", __FUNCTION__);
417     case TF_FLG_ICHK_OUT:
418         return fix_0;
419     }
420     // if we are here, we are in the attentuation case...
421     atv = _tf_border_check_2d();
422     if (atv > 0)
423         atv = fix_1 - fix_div(atv, tf_cur_rad);
424     return atv;
425 }
426 
427 // 3 feet, or so
428 #define STAIR_TOLERANCE fix_make(0, 0x6187)
429 #define STAIR_MIN fix_make(0, 0x0508)
_stair_check(fix walls[4][2],int flags)430 int _stair_check(fix walls[4][2], int flags) {
431     if (flags & TF_FLG_BOX_FULL) {
432         int ad;
433         ad = walls[0][1] - walls[2][1];
434         if (ad < STAIR_MIN)
435             return flags;
436         else if (ad < STAIR_TOLERANCE)
437             return flags | SS_BCD_MISC_STAIR;
438 #ifdef STAIRS_NEAR_THE_TOP
439         ad = walls[0][1] - tf_pt[1];
440 #ifdef STAIRS_ABOVE_DA_TOP
441         if (ad < tf_cur_rad)
442 #else
443         if ((ad > 0) && (ad < tf_cur_rad))
444 #endif
445         {
446             //         mprintf("Pseudo-stair %x from %x and %x\n",ad,tf_pt[1],walls[0][1]);
447             return flags | SS_BCD_MISC_STAIR;
448         }
449 //      else mprintf("no-pseudo-stair %x from %x and %x\n",ad,tf_pt[1],walls[0][1]);
450 #endif
451     } else {
452         if (tf_pcnt == 4) {
453             fix lv, rv, ad;
454 #ifdef STAIRS_NEAR_THE_TOP
455             fix xd, yd, slp, y;
456 #endif
457             lv = walls[0][1] - walls[3][1];
458             rv = walls[1][1] - walls[2][1];
459             ad = (lv + rv) >> 1;
460             if (ad < STAIR_MIN)
461                 return flags;
462             if (ad < STAIR_TOLERANCE)
463                 return flags | SS_BCD_MISC_STAIR;
464 #ifdef STAIRS_NEAR_THE_TOP
465             xd = walls[1][0] - walls[0][0];
466             yd = walls[1][1] - walls[0][1];
467             if (yd != 0) {
468                 if (xd != 0) {
469                     slp = fix_div(yd, xd);
470                     y = fix_mul(slp, tf_pt[0] - walls[0][0]) + walls[0][1]; // y=mx+b
471                 } else
472                     return flags;
473             } else
474                 y = walls[0][1];
475             ad = y - tf_pt[1];
476 #ifdef STAIRS_ABOVE_DA_TOP
477             if (ad < tf_cur_rad)
478 #else
479             if ((ad > 0) && (ad < tf_cur_rad))
480 #endif
481             {
482                 //            mprintf("Pseudo-stair %x from %x and %x\n",ad,tf_pt[1],y);
483                 return flags | SS_BCD_MISC_STAIR;
484             }
485 //         else mprintf("no-pseudo-stair %x from %x and %x\n",ad,tf_pt[1],y);
486 #endif
487         }
488     }
489     return flags;
490 }
491 
492 // note: if it turns out aligned > 2*multi, we should do set and reset in multi for rad, and no rad set here
tf_solve_aligned_face(fix pt[3],fix walls[4][2],int flags,fix * norm)493 uchar tf_solve_aligned_face(fix pt[3], fix walls[4][2], int flags, fix *norm) {
494     fix att;
495     uchar rv = FALSE;
496     //   if (norm!=NULL)
497     //      tf_turn_on(0xffff);
498     tf_Spew(AlignFce,
499             ("tfd:aligned walls %x %x %x %x %x %x %x %x, pt %x %x %x, flg %x, nrm %x %x %x\n", walls[0][0], walls[0][1],
500              walls[1][0], walls[1][1], walls[2][0], walls[2][1], walls[3][0], walls[3][1], pt[0], pt[1], pt[2], flags,
501              norm != NULL ? norm[0] : 0xb, norm != NULL ? norm[1] : 0xa, norm != NULL ? norm[2] : 0xd));
502     tf_cur_rad = tf_rad;                     // or reset in remetric
503     if ((pt[2] > 0) && (tf_cur_rad > pt[2])) // in range
504     {
505         tf_pt = pt;
506         tf_vert_2d = walls;
507         att = tf_solve_2d_case(flags);
508         if (att > 0) {
509             if (flags & SS_BCD_TYPE_WALL)
510                 flags = _stair_check(walls, flags);
511             _tf_set_flet(flags, att, pt[2], norm);
512             rv = TRUE;
513         }
514     }
515     //   if (norm!=NULL)
516     //      tf_undo_set(Ret|FletList);
517     return rv;
518 }
519 
tf_solve_remetriced_face(fix pt[3],fix walls[4][2],int flags,fix norm[3],fix metric)520 uchar tf_solve_remetriced_face(fix pt[3], fix walls[4][2], int flags, fix norm[3], fix metric) {
521     fix att;
522     uchar rv = FALSE;
523     //   tf_turn_on(0xffff);
524     tf_Spew(RemetFce, ("tfd:remetric walls %x %x %x %x %x %x %x %x, pt %x %x %x, nrm %x %x %x, flg %x, metric %x\n",
525                        walls[0][0], walls[0][1], walls[1][0], walls[1][1], walls[2][0], walls[2][1], walls[3][0],
526                        walls[3][1], pt[0], pt[1], pt[2], norm[0], norm[1], norm[2], flags, metric));
527     tf_pt = pt;
528     tf_vert_2d = walls;
529     // set current rad correctly
530     tf_cur_rad = fix_mul(metric, tf_rad);
531     if ((pt[2] > 0) && (tf_cur_rad > pt[2])) // in range
532     {
533         att = tf_solve_2d_case(flags);
534         if (att > 0) {
535             fix cdist = fix_div(pt[2], metric);
536             if (flags & SS_BCD_TYPE_WALL)
537                 flags = _stair_check(walls, flags);
538             _tf_set_flet(flags, att, cdist, norm);
539             rv = TRUE;
540         }
541     }
542     //   tf_undo_set(Ret|FletList);
543     return rv;
544 }
545 
546 // THIS IS BROKEN
547 // THE INITIAL CHECK SHOULD ADD TF_RAD^2 to rad
548 // BUT WE HAVE TO CUT FINAL IN 20 MINUTES, SO WE ARENT GOING TO CHANGE IT
tf_solve_cylinder(fix pt[3],fix irad,fix height)549 uchar tf_solve_cylinder(fix pt[3], fix irad, fix height) {
550     uchar rv = FALSE, slv = FALSE;
551     int flags;
552     fix dist_sqrd, r_dist, rad = abs(irad), urad;
553     // first check height
554     if ((pt[2] < -tf_rad) || (pt[2] > height + tf_rad))
555         return rv; // nope, high or low
556     dist_sqrd = fix_mul(pt[0], pt[0]) + fix_mul(pt[1], pt[1]);
557     urad = rad;
558     if (irad < 0)
559         urad += tf_cur_rad;
560     tf_Spew(Cylinder, ("ph %d pts %x %x scyl is in, dsqr is %x, rsq %x, rad %x\n", tf_ph, pt[0], pt[1], dist_sqrd,
561                        fix_mul(rad, rad), rad));
562     if ((irad < 0) || (fix_mul(rad, rad) > dist_sqrd)) {
563         tf_Spew(Cylinder, ("double rad hit.."));
564         if ((r_dist = fix_sqrt(dist_sqrd)) < urad) {
565             fix nrm[3], att = fix_1, cdist;
566             tf_Spew(Cylinder, ("scyl in.."));
567             if ((pt[2] < 0) || (pt[2] > height)) // flat top+bottom
568             {
569                 LG_memset(nrm, 0, 3 * 4); //            _memset32l(nrm,0,3);
570 
571                 if (r_dist > (rad >> 1))
572                     att = fix_div(r_dist - (urad >> 1), (urad >> 1));
573                 else
574                     att = fix_1;
575                 if (irad > 0) {
576                     if (pt[2] < 0) {
577                         nrm[2] = -fix_1;
578                         cdist = -pt[2];
579                     } else {
580                         nrm[2] = fix_1;
581                         cdist = pt[2] - height;
582                     }
583                     if (nrm[2] > 0)
584                         flags = SS_BCD_PRIM_ZAXIS | SS_BCD_TYPE_FLOOR;
585                     else
586                         flags = SS_BCD_PRIM_NEG_Z | SS_BCD_TYPE_CEIL;
587                     _tf_set_flet(flags, att, cdist, nrm);
588                     tf_Spew(Cylinder, ("OverCyl"));
589                     slv = TRUE;
590                 }
591             }
592             if (!slv) // unitize normal, call us done
593             {
594                 nrm[0] = fix_div(pt[0], r_dist);
595                 nrm[1] = fix_div(pt[1], r_dist);
596                 nrm[2] = 0;
597                 if (r_dist > urad - tf_cur_rad) {
598                     _tf_set_flet(SS_BCD_PRIM_MULTI | SS_BCD_TYPE_WALL, att, urad - r_dist, nrm);
599                     tf_Spew(Cylinder, ("AroundCyl"));
600                 } else {
601                     _tf_set_flet(SS_BCD_PRIM_MULTI | SS_BCD_TYPE_WALL, att, 0, nrm);
602                     tf_Spew(Cylinder, ("FullyInCyl"));
603                 }
604             }
605             rv = TRUE;
606             //         tf_turn_on(Ret|FletList);
607         }
608     }
609 #ifdef TF_TALK_SYSTEM
610     if (rv)
611         tf_Spew(Cylinder, ("\n"));
612 #endif
613     return rv;
614 }
615 
terrfunc_one_map_square(int fmask)616 void terrfunc_one_map_square(int fmask) { // add appropriately set facelet_mask appropriately
617     if (fix_from_map_height(MAP_HEIGHTS - 1 - me_height_ceil(tfunc_mptr) - me_param(tfunc_mptr)) < tfunc_maxz)
618         fmask |= FACELET_MASK_C;
619     if (fix_from_map_height(me_height_flr(tfunc_mptr) + me_param(tfunc_mptr)) > tfunc_minz)
620         fmask |= FACELET_MASK_F;
621     tf_Spew(Grab, ("grabbing at %x %x, mask %x\n", tfunc_map_x, tfunc_map_y, fmask));
622     fr_tfunc_grab_fast(fmask); // grab the facelets.. are these correct
623 }
624 
625 #define FACELET_MASK_Z 0
626 #define fcs(v1, v2) ((FACELET_MASK_##v1) | (FACELET_MASK_##v2))
627 
628 // probably could be just 5,5 by having it algorithimically flip the list if needed
629 uchar tf_wall_check[5][2][5] = {
630     {{fcs(Z, Z)}, {fcs(Z, Z)}},
631     {{fcs(Z, Z), fcs(S, Z)}, {fcs(Z, N), fcs(Z, Z)}},
632     {{fcs(Z, Z), fcs(S, N), fcs(Z, Z)}, {fcs(Z, Z), fcs(S, N), fcs(Z, Z)}},
633     {{fcs(Z, Z), fcs(S, Z), fcs(S, N), fcs(Z, Z)}, {fcs(Z, Z), fcs(S, N), fcs(Z, N), fcs(Z, Z)}},
634     {{fcs(Z, Z), fcs(S, Z), fcs(S, N), fcs(Z, N), fcs(Z, Z)}, {fcs(Z, Z), fcs(S, Z), fcs(S, N), fcs(Z, N), fcs(Z, Z)}}};
635 
tf_direct(fix fix_x,fix fix_y,fix fix_z,fix rad,int32_t ph,TFType tf_type)636 TerrainHit tf_direct(fix fix_x, fix fix_y, fix fix_z, fix rad, int32_t ph, TFType tf_type) {
637     int32_t fce_minc, xd, yd, xo, yo, centered; // fce_minc is map increment between lines, ?d LGRect size, xo clip offset
638     fix minx, miny, maxx, maxy, cenx, ceny; // for full radius of us, center for us, all really ints in the end
639 
640     tf_Spew(Calls, ("indoor %d at %x %x %x r %x\n", ph, fix_x, fix_y, fix_z, rad));
641     cenx = fix_int(fix_x);
642     ceny = fix_int(fix_y);
643     ss_edms_facelet_cnt = ss_edms_bcd_flags = ss_edms_bcd_param = 0;
644 
645     // wacky bcd stuff....
646     if (global_fullmap->cyber) {
647         MapElem *mp;
648         int32_t mb = -1, mt;
649         mp = MAP_GET_XY(cenx, ceny);
650         if ((mt = me_light_flr(mp)) != 0)
651             mb = mt - 1;
652         else if ((mt = me_light_ceil(mp)) != 0)
653             mb = 14 + mt;
654         if (mb != -1)
655             ss_edms_bcd_flags |= ((uint)v_to_cur[mb]) << SS_BCD_CURR_SHF;
656     }
657     if (tf_type == TFD_BCD)
658         return MISS;
659 
660     ObjsClearDealt();
661     tf_talk_setup();
662 #ifdef USE_OLD_PASSING
663     facelet_clear();
664 #endif
665 
666     // find bounding map box
667     tf_rad = rad;
668     tf_ph = ph;
669     minx = fix_x - rad;
670     if (minx < 0) {
671         xo = fix_int(-minx) + 1;
672         minx = 0;
673     } else {
674         minx = fix_int(minx);
675         xo = 0;
676     }
677     miny = fix_y - rad;
678     if (miny < 0) {
679         yo = fix_int(-miny) + 1;
680         miny = 0;
681     } else {
682         miny = fix_int(miny);
683         yo = 0;
684     }
685 
686     maxx = fix_int(fix_x + rad);
687     xd = maxx - minx + 1 + xo; // get unclipped x width/distance stuff
688     if (maxx >= MAP_XSIZE)
689         maxx = MAP_XSIZE - 1;
690     maxy = fix_int(fix_y + rad);
691     yd = maxy - miny + 1 + yo; // get unclipped y width/distance stuff
692     if (maxy >= MAP_YSIZE)
693         maxy = MAP_YSIZE - 1;
694     tfunc_maxz = fix_z + rad;
695     tfunc_minz = fix_z - rad; // set top and bottom height for square
696     tfunc_mptr = MAP_GET_XY(minx, miny);
697 
698     tf_raw_pt[0] = fix_x;
699     tf_loc_pt[0] = fix_x - fix_make(minx, 0); // what is the coordinate system, eh?
700     tf_raw_pt[1] = fix_y;
701     tf_loc_pt[1] = fix_y - fix_make(miny, 0);
702     tf_raw_pt[2] = tf_loc_pt[2] = fix_z;
703 
704     tf_Spew(Area, ("looking from %x %x to %x %x, cen %x %x d's %d %d o's %d %d\n", minx, miny, maxx, maxy, cenx, ceny,
705                    xd, yd, xo, yo));
706 
707     if ((xd | yd) == 1) // only one square to do...
708     {
709         tfunc_map_x = minx;
710         tfunc_map_y = miny; // do we really need this?
711         terrfunc_one_map_square(FACELET_MASK_I);
712         tf_Stat(single);
713     } else {
714         uchar *xmsk_base, *ymsk_base, *xmsk_now, *ymsk_now;
715         int32_t xb;
716 
717         centered = (((xd & 1) == 0) && ((minx - xo + (xd >> 1)) == cenx)) ? 0 : 1;
718         xmsk_base = &tf_wall_check[xd - 1][centered][xo];
719         centered = (((yd & 1) == 0) && ((miny - yo + (yd >> 1)) == ceny)) ? 0 : 1;
720         ymsk_base = &tf_wall_check[yd - 1][centered][yo];
721         xb = tf_loc_pt[0];
722         fce_minc = MAP_XSIZE - (maxx - minx) - 1;
723         for (tfunc_map_y = miny, ymsk_now = ymsk_base; tfunc_map_y <= maxy;
724              tfunc_mptr += fce_minc, tfunc_map_y++, ymsk_now++, tf_loc_pt[1] -= fix_1)
725             for (tfunc_map_x = minx, xmsk_now = xmsk_base, tf_loc_pt[0] = xb; tfunc_map_x <= maxx;
726                  tfunc_mptr++, tfunc_map_x++, xmsk_now++, tf_loc_pt[0] -= fix_1) {
727                 terrfunc_one_map_square(((*xmsk_now) << 1) | (*ymsk_now) | FACELET_MASK_I);
728                 tf_Stat(multi);
729                 if ((tf_type == TFD_RCAST) && ss_edms_facelet_cnt)
730                     return HIT_FACELET;
731             }
732     }
733     if (tf_type == TFD_FULL) { // actually figure out what is up with the facelets, send and all
734 #ifdef USE_OLD_PASSING
735         facelet_send();
736 #endif
737     }
738     tf_Spew(Ret, ("at %x %x %x r %x ret flg %x nrm %x %x %x, %x %x %x, %x %x %x\n", fix_x, fix_y, fix_z, rad,
739                   ss_edms_bcd_flags, terrain_info.cx, terrain_info.cy, terrain_info.cz, terrain_info.fx,
740                   terrain_info.fy, terrain_info.fz, terrain_info.wx, terrain_info.wy, terrain_info.wz));
741     tf_Spew(Calls, ("indoor done for %d, tt %d, saw %d\n", tf_ph, tf_type, ss_edms_facelet_cnt));
742     if (tf_talk_check(FletList))
743         _tf_list_flet();
744 
745     // should do something a little more real here, eh?
746 
747     if ((tf_ph != -1) && (ss_edms_facelet_cnt)) {
748         ObjID cobjid = physics_handle_to_id(tf_ph);
749         if (ObjProps[OPNUM(cobjid)].flags & SPCL_TERR_DMG)
750             special_terrain_hit(cobjid);
751         if (global_fullmap->cyber)
752             tile_hit(cenx, ceny);
753     }
754 
755     return (ss_edms_facelet_cnt == 0) ? MISS : HIT_FACELET;
756 }
757 
tf_global_bcd_add(int flg,int param)758 void tf_global_bcd_add(int flg, int param) {
759     if (flg & TF_FLG_HPARAM) {
760         flg &= ~TF_FLG_HPARAM;
761         ss_edms_bcd_param = param;
762     }
763     ss_edms_bcd_flags |= flg;
764 }
765 
766 /* actual call
767  * note us passing lots of annoying useless things on the stack and annoying everyone
768  */
Indoor_Terrain(fix fix_x,fix fix_y,fix fix_z,fix rad,int ph,TFType type)769 TerrainHit Indoor_Terrain(fix fix_x, fix fix_y, fix fix_z, fix rad, int ph, TFType type) {
770     return tf_direct(fix_x, fix_y, fix_z, rad, ph, type);
771 }
772