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