PageRenderTime 28ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/freedroidrpg-0.15.1/src/colldet.c

#
C | 687 lines | 368 code | 107 blank | 212 comment | 101 complexity | b82f8a23d9380ff8368ddf9e27ca20ec MD5 | raw file
Possible License(s): AGPL-1.0
  1. /*
  2. *
  3. * Copyright (c) 2008-2010 Arthur Huillet
  4. *
  5. *
  6. * This file is part of Freedroid
  7. *
  8. * Freedroid is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; either version 2 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * Freedroid is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with Freedroid; see the file COPYING. If not, write to the
  20. * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  21. * MA 02111-1307 USA
  22. *
  23. */
  24. #define _colldet_c
  25. #include "system.h"
  26. #include "defs.h"
  27. #include "struct.h"
  28. #include "proto.h"
  29. #include "global.h"
  30. /* This function is Copyright (c) 1999 ID Software, from Quake3 source code, released under GPL */
  31. static inline float Q_rsqrt(float number)
  32. {
  33. float x2;
  34. union {
  35. int32_t i;
  36. float y;
  37. } a;
  38. const float threehalfs = 1.5F;
  39. x2 = number * 0.5F;
  40. a.y = number;
  41. a.i = 0x5f3759df - (a.i >> 1);
  42. a.y = a.y * (threehalfs - (x2 * a.y * a.y));
  43. a.y = a.y * (threehalfs - (x2 * a.y * a.y));
  44. return a.y;
  45. }
  46. inline int normalize_vect(float x1, float y1, float *x2, float *y2)
  47. {
  48. /* Normalize X_0, X_1 */
  49. float tmplen2 = (x1 - *x2) * (x1 - *x2) + (y1 - *y2) * (y1 - *y2);
  50. if (tmplen2 == 1)
  51. return 0;
  52. if (fabsf(tmplen2) < 0.00005f)
  53. return 1; //could not normalize
  54. *x2 = x1 + (*x2 - x1) * Q_rsqrt(tmplen2);
  55. *y2 = y1 + (*y2 - y1) * Q_rsqrt(tmplen2);
  56. return 0;
  57. }
  58. /**
  59. * Compute the squared distance between a segment and a point.
  60. *
  61. * @param x1 segment x1
  62. * @param y1 segment y1
  63. * @param x2 segment x2
  64. * @param y2 segment y2
  65. * @param px point x
  66. * @param py point y
  67. *
  68. * @return squared distance
  69. */
  70. static inline float calc_squared_distance_seg_point_normalized(float x1, float y1, float x2, float y2, float x2n, float y2n, float px, float py)
  71. {
  72. float dotprod = (x2 - x1) * (px - x1) + (y2 - y1) * (py - y1);
  73. /* Check if we are on the segment with dotproduct */
  74. if (dotprod > 0 && dotprod < (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)) {
  75. /* Distance formula taken at http://www.softsurfer.com/Archive/algorithm_0102/Eqn_dcross2.gif */
  76. /* the distance basically is the cross product of the normalized (X_0, X_1) vector with (X_0, X) */
  77. float distline = fabsf(((y1 - y2n) * px + (x2n - x1) * py + (x1 * y2n - x2n * y1)));
  78. return distline;
  79. } else {
  80. /* We are not quite done yet ! */
  81. if (dotprod < 0) {
  82. return ((px - x1) * (px - x1) + (py - y1) * (py - y1));
  83. } else //dotprod > length^2
  84. {
  85. return ((px - x2) * (px - x2) + (py - y2) * (py - y2));
  86. }
  87. }
  88. }
  89. /**
  90. * This colldet filter is used to ignore the obstacles (such as doors)
  91. * that can be traversed by walking bot.
  92. */
  93. int WalkablePassFilterCallback(colldet_filter * this, obstacle * obs, int obs_idx)
  94. {
  95. if (get_obstacle_spec(obs->type)->flags & IS_WALKABLE)
  96. return TRUE;
  97. if (this->next)
  98. return (this->next->callback(this->next, obs, obs_idx));
  99. return FALSE;
  100. }
  101. colldet_filter WalkablePassFilter = { WalkablePassFilterCallback, NULL, 0, NULL };
  102. colldet_filter WalkableWithMarginPassFilter = { WalkablePassFilterCallback, NULL, COLLDET_WALKABLE_MARGIN, NULL };
  103. /**
  104. * This colldet filter is used to ignore the obstacles (such as water)
  105. * that can be traversed by a flying object.
  106. */
  107. int FlyablePassFilterCallback(colldet_filter * this, obstacle * obs, int obs_idx)
  108. {
  109. if (get_obstacle_spec(obs->type)->flags & GROUND_LEVEL)
  110. return TRUE;
  111. if (this->next)
  112. return (this->next->callback(this->next, obs, obs_idx));
  113. return FALSE;
  114. }
  115. colldet_filter FlyablePassFilter = { FlyablePassFilterCallback, NULL, 0, NULL };
  116. /**
  117. * This colldet filter is used to ignore the obstacles
  118. * that can be traversed by light.
  119. * Thus, it merely checks for visibility.
  120. */
  121. int VisiblePassFilterCallback(colldet_filter * this, obstacle * obs, int obs_idx)
  122. {
  123. if (!(get_obstacle_spec(obs->type)->flags & BLOCKS_VISION_TOO))
  124. return TRUE;
  125. if (this->next)
  126. return (this->next->callback(this->next, obs, obs_idx));
  127. return FALSE;
  128. }
  129. colldet_filter VisiblePassFilter = { VisiblePassFilterCallback, NULL, 0, NULL };
  130. /**
  131. * This colldet filter is used to ignore a given obstacle during
  132. * collision detection
  133. */
  134. int ObstacleByIdPassFilterCallback(colldet_filter * this, obstacle * obs, int obs_idx)
  135. {
  136. if (obs_idx == *(int *)(this->data))
  137. return TRUE;
  138. if (this->next)
  139. return (this->next->callback(this->next, obs, obs_idx));
  140. return FALSE;
  141. }
  142. colldet_filter ObstacleByIdPassFilter = { ObstacleByIdPassFilterCallback, NULL, 0, NULL };
  143. colldet_filter WalkableExceptIdPassFilter = { ObstacleByIdPassFilterCallback, NULL, 0, &WalkablePassFilter };
  144. colldet_filter FlyableExceptIdPassFilter = { ObstacleByIdPassFilterCallback, NULL, 0, &FlyablePassFilter };
  145. static const float Druid_Radius = 0.5;
  146. /**
  147. * This function checks if there are any droids at a given location.
  148. */
  149. int location_free_of_droids(float x, float y, int levelnum, freeway_context *ctx)
  150. {
  151. if (ctx->check_tux) {
  152. float dist = (Me.pos.x - x)*(Me.pos.x - x) + (Me.pos.y - y)*(Me.pos.y - y);
  153. if (dist < Druid_Radius*Druid_Radius)
  154. return FALSE;
  155. }
  156. enemy *this_enemy;
  157. BROWSE_LEVEL_BOTS(this_enemy, levelnum) {
  158. if ((this_enemy->pure_wait > 0) ||
  159. (ctx->except_bots[0] != NULL && ctx->except_bots[0] == this_enemy) ||
  160. (ctx->except_bots[1] != NULL && ctx->except_bots[1] == this_enemy))
  161. continue;
  162. float dist = (this_enemy->pos.x - x)*(this_enemy->pos.x - x) + (this_enemy->pos.y - y)*(this_enemy->pos.y - y);
  163. if (dist < Druid_Radius*Druid_Radius)
  164. return FALSE;
  165. }
  166. return TRUE;
  167. }
  168. /**
  169. * This function checks if the connection between two points is free of
  170. * droids.
  171. *
  172. * OBSTACLES ARE NOT TAKEN INTO CONSIDERATION, ONLY DROIDS!!!
  173. *
  174. */
  175. int way_free_of_droids(float x1, float y1, float x2, float y2, int levelnum, freeway_context * ctx)
  176. {
  177. float minx = min(x1, x2) - 0.5;
  178. float miny = min(y1, y2) - 0.5;
  179. float maxx = max(x1, x2) + 0.5;
  180. float maxy = max(y1, y2) + 0.5;
  181. float x2n = x2;
  182. float y2n = y2;
  183. normalize_vect(x1, y1, &x2n, &y2n);
  184. if (ctx->check_tux) {
  185. float dist = calc_squared_distance_seg_point_normalized(x1, y1, x2, y2, x2n, y2n, Me.pos.x, Me.pos.y);
  186. if (dist < Druid_Radius*Druid_Radius)
  187. return FALSE;
  188. }
  189. enemy *this_enemy;
  190. BROWSE_LEVEL_BOTS(this_enemy, levelnum) {
  191. if ((this_enemy->pure_wait > 0) ||
  192. (ctx->except_bots[0] != NULL && ctx->except_bots[0] == this_enemy) ||
  193. (ctx->except_bots[1] != NULL && ctx->except_bots[1] == this_enemy))
  194. continue;
  195. if (this_enemy->pos.x < minx || this_enemy->pos.y < miny || this_enemy->pos.y > maxy || this_enemy->pos.x > maxx)
  196. continue;
  197. float dist = calc_squared_distance_seg_point_normalized(x1, y1, x2, y2, x2n, y2n, this_enemy->pos.x, this_enemy->pos.y);
  198. if (dist < Druid_Radius*Druid_Radius)
  199. return FALSE;
  200. }
  201. return TRUE;
  202. }
  203. enum {
  204. RECT_IN = 0,
  205. RECT_OUT_LEFT = 1,
  206. RECT_OUT_RIGHT = 2,
  207. RECT_OUT_UP = 4,
  208. RECT_OUT_DOWN = 8,
  209. };
  210. static inline char get_point_flag(float xmin, float ymin, float xmax, float ymax, float px, float py)
  211. {
  212. char out = RECT_IN;
  213. //check if we are left
  214. if (px < xmin)
  215. out |= RECT_OUT_LEFT;
  216. else if (px > xmax)
  217. out |= RECT_OUT_RIGHT;
  218. //check up/down
  219. if (py < ymin)
  220. out |= RECT_OUT_DOWN;
  221. else if (py > ymax)
  222. out |= RECT_OUT_UP;
  223. return out;
  224. }
  225. /**
  226. * This function checks if a given position is free of obstacles
  227. *
  228. * Return FALSE if collision detected.
  229. */
  230. int SinglePointColldet(float x, float y, int z, colldet_filter * filter)
  231. {
  232. return DirectLineColldet(x, y, x, y, z, filter);
  233. } // int SinglePointColldet ( float x, float y, int z, colldet_filter* filter )
  234. /** This function checks if the line can be traversed along directly against obstacles.
  235. * It also handles the case of point tests (x1 == x2 && y1 == y2) properly.
  236. *
  237. * This function scans all the obstacles that are inside the bounding box of the
  238. * segment, and check if the segment intersects these obstacles.
  239. *
  240. * Note: this function compute the intersection between a segment and the obstacles
  241. * of one given level. This is a helper function used by DirectLineColldet(), which
  242. * is the real function to use. see below.
  243. *
  244. * Return FALSE if collision detected.
  245. */
  246. static int dlc_on_one_level(int x_tile_start, int x_tile_end, int y_tile_start, int y_tile_end,
  247. gps * p1, gps * p2, level * lvl, colldet_filter * filter)
  248. {
  249. int x_tile, y_tile;
  250. int tstamp = next_glue_timestamp();
  251. char ispoint = ((p1->x == p2->x) && (p1->y == p2->y));
  252. for (y_tile = y_tile_start; y_tile <= y_tile_end; y_tile++) {
  253. for (x_tile = x_tile_start; x_tile <= x_tile_end; x_tile++) {
  254. // We can list all obstacles here
  255. int glue_index;
  256. for (glue_index = 0; glue_index < lvl->map[y_tile][x_tile].glued_obstacles.size; glue_index++) {
  257. int *glued_obstacles = lvl->map[y_tile][x_tile].glued_obstacles.arr;
  258. int obstacle_index = glued_obstacles[glue_index];
  259. if (obstacle_index == (-1))
  260. break;
  261. obstacle *our_obs = &(lvl->obstacle_list[obstacle_index]);
  262. if (our_obs->timestamp == tstamp) {
  263. continue;
  264. }
  265. our_obs->timestamp = tstamp;
  266. // If the obstacle doesn't even have a collision rectangle, then
  267. // of course it's easy, cause then there can't be any collision
  268. //
  269. obstacle_spec *obstacle_spec = get_obstacle_spec(our_obs->type);
  270. if (obstacle_spec->block_area_type == COLLISION_TYPE_NONE)
  271. continue;
  272. // Filter out some obstacles, if asked
  273. if (filter && filter->callback(filter, our_obs, obstacle_index))
  274. continue;
  275. // So we have our obstacle
  276. // Check the flags of both points against the rectangle of the object
  277. moderately_finepoint rect1 = { our_obs->pos.x + obstacle_spec->left_border,
  278. our_obs->pos.y + obstacle_spec->upper_border
  279. };
  280. moderately_finepoint rect2 = { our_obs->pos.x + obstacle_spec->right_border,
  281. our_obs->pos.y + obstacle_spec->lower_border
  282. };
  283. // When DLC is called by the pathfinder, we grow a bit the obstacle's size.
  284. // Motivation: the path followed by a character (Tux or a bot) can diverge a bit
  285. // from the path computed by the pathfinder. If that computed path is passing very close
  286. // to an obstacle, then the actual path could go inside the obstacle, leading the character
  287. // to be stuck.
  288. //
  289. // Also, we will want to use this when dropping items.
  290. if (filter && filter->extra_margin != 0.0) {
  291. rect1.x -= filter->extra_margin;
  292. rect1.y -= filter->extra_margin;
  293. rect2.x += filter->extra_margin;
  294. rect2.y += filter->extra_margin;
  295. }
  296. char p1flags = get_point_flag(rect1.x, rect1.y, rect2.x, rect2.y, p1->x, p1->y);
  297. char p2flags = get_point_flag(rect1.x, rect1.y, rect2.x, rect2.y, p2->x, p2->y);
  298. // Handle obvious cases
  299. if (p1flags & p2flags) // both points on the same side, no collision
  300. continue;
  301. if ((p1flags == RECT_IN) || (p2flags == RECT_IN)) //we're in? collision without a doubt
  302. return FALSE;
  303. if (ispoint) // degenerated line, and outside the rectangle, no collision
  304. continue;
  305. // possible collision, check if the line is crossing the rectangle
  306. // by looking if all vertices of the rectangle are in the same half-space
  307. //
  308. // 1- line equation : a*x + b*y + c = 0
  309. float line_a = -(p1->y - p2->y);
  310. float line_b = p1->x - p2->x;
  311. float line_c = p2->x * p1->y - p2->y * p1->x;
  312. // 2- check each vertex, halt as soon as one is on the other halfspace -> collision
  313. char first_vertex_sign = (line_a * rect1.x + line_b * rect1.y + line_c) > 0 ? 0 : 1;
  314. char other_vertex_sign = (line_a * rect2.x + line_b * rect1.y + line_c) > 0 ? 0 : 1;
  315. if (first_vertex_sign != other_vertex_sign)
  316. return FALSE;
  317. other_vertex_sign = (line_a * rect2.x + line_b * rect2.y + line_c) > 0 ? 0 : 1;
  318. if (first_vertex_sign != other_vertex_sign)
  319. return FALSE;
  320. other_vertex_sign = (line_a * rect1.x + line_b * rect2.y + line_c) > 0 ? 0 : 1;
  321. if (first_vertex_sign != other_vertex_sign)
  322. return FALSE;
  323. }
  324. } //for x_tile
  325. } //for y_tile
  326. return TRUE;
  327. }
  328. /** This function checks if the line can be traversed along directly against obstacles.
  329. * It also handles the case of point tests (x1 == x2 && y1 == y2) properly.
  330. *
  331. * Return FALSE if collision detected.
  332. *
  333. * (see dlc_on_one_level() for the core collision detection)
  334. *
  335. * The bounding box around the segment can span up to 4 levels. We thus have to use
  336. * virtual gps coordinates.
  337. *
  338. * The overall algorithm should be :
  339. * foreach tile of the bounding box along Y axis {
  340. * foreach tile of the bounding box along X axis {
  341. * resolve virtual coord (X,Y,Z) into real coord (X1,Y1,Z1)
  342. * foreach obstacle at (X1,Y1,Z1) {
  343. * get obstacle's virtual position according to Z level
  344. * check collision between segment and obstacle
  345. * }
  346. * }
  347. * }
  348. * There will thus be a lot of gps transformations, with a real impact performance.
  349. *
  350. * To avoid all those transformations, we will rather split the segment's bbox
  351. * into (up to) 4 parts, one part per spanned level. We then just have to transform
  352. * the segment's endpoints according to each level. For example, if part of the bbox
  353. * is on the north neighbor, we will have :
  354. *
  355. * get segment's virtual position according to north neighbor
  356. * foreach tile of the bounding box on the north neighbor along Y axis {
  357. * foreach tile of the bounding box on the north neighbor along X axis {
  358. * foreach obstacle at (X,Y,north level) {
  359. * check collision between 'virtual segment' and obstacle
  360. * }
  361. * }
  362. * }
  363. *
  364. * We don't however want to check all the neighborhood (current level + its 8 neighbors),
  365. * since at most 4 levels can be spanned by the segment's bbox.
  366. * We will here use the same trick than the one in resolved_virtual_position():
  367. * conceptually, if (X,Y) is a virtual position, the (i,j) pair defined by i = (int)(X/xlen)
  368. * and j = (int)(Y/ylen), is a reference to one of the 9 levels in the neighborhood, where
  369. * the (X,Y) point lies.
  370. *
  371. * We can then transform the bbox's top-left and bottom-right points into 2 pairs
  372. * that will reference the top-left and bottom-right levels spanned by the segment, and
  373. * we just have to loop on those levels.
  374. *
  375. * Small optimization : chances are that the biggest part of the bbox is on the current
  376. * level. We will then first check collision on the current level, before to test neighbors.
  377. */
  378. int DirectLineColldet(float x1, float y1, float x2, float y2, int z, colldet_filter * filter)
  379. {
  380. gps p1 = { x1, y1, z }; // segment real starting gps point
  381. gps p2 = { x2, y2, z }; // segment real ending gps point
  382. gps vp1, vp2; // segment's virtual endpoints
  383. // Segment's bbox.
  384. int x_tile_start = floor(min(x1, x2));
  385. int y_tile_start = floor(min(y1, y2));
  386. int x_tile_end = ceil(max(x1, x2)) - 1;
  387. int y_tile_end = ceil(max(y1, y2)) - 1;
  388. // Special case: if x1==x2 and x1 is an integral value, the previous
  389. // definition ends up with x_tile_end = x_tile_start - 1, which is definitely
  390. // not intended (same remark with Y values)
  391. if (x_tile_end < x_tile_start)
  392. x_tile_end = x_tile_start;
  393. if (y_tile_end < y_tile_start)
  394. y_tile_end = y_tile_start;
  395. int x_start, y_start, x_end, y_end; // intersection between the bbox and one level's limits
  396. struct neighbor_data_cell *ngb_cell = NULL;
  397. // First check on current level (small optimization)
  398. //
  399. level *lvl = curShip.AllLevels[z];
  400. // compute the intersection between the bbox and the current level
  401. x_start = max(0, x_tile_start);
  402. x_end = min(lvl->xlen - 1, x_tile_end);
  403. y_start = max(0, y_tile_start);
  404. y_end = min(lvl->ylen - 1, y_tile_end);
  405. // check collision
  406. if (!dlc_on_one_level(x_start, x_end, y_start, y_end, &p1, &p2, lvl, filter))
  407. return FALSE;
  408. // Get the neighborhood covered by the segment's bbox (see function's comment)
  409. // and check each of the neighbors
  410. //
  411. int x_ngb_start = NEIGHBOR_IDX(x_tile_start, lvl->xlen);
  412. int x_ngb_end = NEIGHBOR_IDX(x_tile_end, lvl->xlen);
  413. int y_ngb_start = NEIGHBOR_IDX(y_tile_start, lvl->ylen);
  414. int y_ngb_end = NEIGHBOR_IDX(y_tile_end, lvl->ylen);
  415. // Loop on the neighbors
  416. int j, i;
  417. for (j = y_ngb_start; j <= y_ngb_end; j++) {
  418. for (i = x_ngb_start; i <= x_ngb_end; i++) {
  419. if (j == 1 && i == 1)
  420. continue; // this references the current level, already checked
  421. // if the neighbor exists...
  422. if ((ngb_cell = level_neighbors_map[z][j][i])) {
  423. // get neighbor's level struct
  424. lvl = curShip.AllLevels[ngb_cell->lvl_idx];
  425. // transform bbox corners into virtual positions according to lvl
  426. // and compute intersection with lvl's limits
  427. x_start = max(0, x_tile_start + ngb_cell->delta_x);
  428. x_end = min(lvl->xlen - 1, x_tile_end + ngb_cell->delta_x);
  429. y_start = max(0, y_tile_start + ngb_cell->delta_y);
  430. y_end = min(lvl->ylen - 1, y_tile_end + ngb_cell->delta_y);
  431. // transform segment's endpoints into virtual positions according to lvl
  432. update_virtual_position(&vp1, &p1, ngb_cell->lvl_idx);
  433. update_virtual_position(&vp2, &p2, ngb_cell->lvl_idx);
  434. // check collision
  435. if (!dlc_on_one_level(x_start, x_end, y_start, y_end, &vp1, &vp2, lvl, filter))
  436. return FALSE;
  437. }
  438. }
  439. }
  440. return TRUE;
  441. }
  442. /**************************************************************
  443. * Bots and Tux escaping code
  444. */
  445. /**
  446. * Small structure used to sort a list of directions
  447. * (Used during escaping)
  448. */
  449. struct dist_elt {
  450. float value;
  451. unsigned char direction;
  452. };
  453. /**
  454. * Comparison function of two dist_elt.
  455. * (Used by qsort during escaping)
  456. */
  457. static int cmp_dist_elt(const void *elt1, const void *elt2)
  458. {
  459. return (((struct dist_elt *)elt1)->value - ((struct dist_elt *)elt2)->value);
  460. }
  461. /**
  462. * Move a character outside of a collision rectangle.
  463. * (Used during escaping)
  464. * We have no idea of where the character came from and where it was going to
  465. * when it gets stuck.
  466. * Thus, we assume that the character is actually quite near a free position.
  467. * We will then test each rectangle's edges, starting from the closest one.
  468. */
  469. int MoveOutOfObstacle(float *posX, float *posY, int posZ, obstacle * ThisObstacle, colldet_filter * filter)
  470. {
  471. enum { RIGHT, DOWN, LEFT, TOP };
  472. unsigned int i;
  473. moderately_finepoint new_pos = { 0.5, 0.5 };
  474. obstacle_spec *obstacle_spec = get_obstacle_spec(ThisObstacle->type);
  475. // Construct a sorted list of distance between character's position and the rectangle's edges
  476. //
  477. moderately_finepoint rect1 = { ThisObstacle->pos.x + obstacle_spec->left_border,
  478. ThisObstacle->pos.y + obstacle_spec->upper_border
  479. };
  480. moderately_finepoint rect2 = { ThisObstacle->pos.x + obstacle_spec->right_border,
  481. ThisObstacle->pos.y + obstacle_spec->lower_border
  482. };
  483. struct dist_elt dist_arr[] = { {rect2.x - (*posX), RIGHT},
  484. {(*posX) - rect1.x, LEFT},
  485. {rect2.y - (*posY), TOP},
  486. {(*posY) - rect1.y, DOWN}
  487. };
  488. qsort(dist_arr, 4, sizeof(struct dist_elt), cmp_dist_elt);
  489. // Check each direction of the sorted list
  490. //
  491. for (i = 0; i < 4; ++i) {
  492. switch (dist_arr[i].direction) {
  493. case (RIGHT):
  494. {
  495. new_pos.x = rect2.x + 0.1; // small increment to really be outside the rectangle
  496. new_pos.y = *posY;
  497. break;
  498. }
  499. case (DOWN):
  500. {
  501. new_pos.x = *posX;
  502. new_pos.y = rect1.y - 0.1;
  503. break;
  504. }
  505. case (LEFT):
  506. {
  507. new_pos.x = rect1.x - 0.1;
  508. new_pos.y = *posY;
  509. break;
  510. }
  511. case (TOP):
  512. {
  513. new_pos.x = *posX;
  514. new_pos.y = rect2.y + 0.1;
  515. break;
  516. }
  517. }
  518. if (SinglePointColldet(new_pos.x, new_pos.y, posZ, filter)) { // got a free position -> move the character and returns
  519. *posX = new_pos.x;
  520. *posY = new_pos.y;
  521. return TRUE;
  522. }
  523. }
  524. // No free position was found outside the obstacle ???
  525. // The caller will need to find a fallback
  526. //
  527. return FALSE;
  528. }
  529. /**
  530. * This function escapes a character from an obstacle, by
  531. * computing a new position
  532. */
  533. int EscapeFromObstacle(float *posX, float *posY, int posZ, colldet_filter * filter)
  534. {
  535. int start_x, end_x, start_y, end_y;
  536. int x, y, i, obst_index;
  537. Level ThisLevel;
  538. start_x = (int)(*posX) - 2;
  539. start_y = (int)(*posY) - 2;
  540. end_x = start_x + 4;
  541. end_y = start_y + 4;
  542. ThisLevel = curShip.AllLevels[posZ];
  543. if (start_x < 0)
  544. start_x = 0;
  545. if (start_y < 0)
  546. start_y = 0;
  547. if (end_x >= ThisLevel->xlen)
  548. end_x = ThisLevel->xlen - 1;
  549. if (end_y >= ThisLevel->ylen)
  550. end_y = ThisLevel->ylen - 1;
  551. for (y = start_y; y < end_y; ++y) {
  552. for (x = start_x; x < end_x; ++x) {
  553. for (i = 0; i < ThisLevel->map[y][x].glued_obstacles.size; i++) {
  554. int *glued_obstacles = ThisLevel->map[y][x].glued_obstacles.arr;
  555. obst_index = glued_obstacles[i];
  556. obstacle *our_obs = &(ThisLevel->obstacle_list[obst_index]);
  557. if (filter && filter->callback(filter, our_obs, obst_index))
  558. continue;
  559. // If the obstacle doesn't even have a collision rectangle, then
  560. // of course it's easy, cause then there can't be any collision
  561. //
  562. obstacle_spec *obstacle_spec = get_obstacle_spec(our_obs->type);
  563. if (obstacle_spec->block_area_type == COLLISION_TYPE_NONE)
  564. continue;
  565. // Now if the position lies inside the collision rectangle, then there's
  566. // a collision.
  567. //
  568. if ((*posX > our_obs->pos.x + obstacle_spec->left_border) &&
  569. (*posX < our_obs->pos.x + obstacle_spec->right_border) &&
  570. (*posY > our_obs->pos.y + obstacle_spec->upper_border) &&
  571. (*posY < our_obs->pos.y + obstacle_spec->lower_border)) {
  572. // Find a new position for the character
  573. return MoveOutOfObstacle(posX, posY, posZ, our_obs, filter);
  574. }
  575. }
  576. }
  577. }
  578. return FALSE;
  579. } // int EscapeFromObstacle( float* posX, float* posY, int posZ, colldet_filter* filter )
  580. #undef _colldet_c