1 /*
2 MOTION_SENSOR.C
3
4 Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
5 and the "Aleph One" developers.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (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. See the
15 GNU General Public License for more details.
16
17 This license is contained in the file "COPYING",
18 which is included with this source code; it is available online at
19 http://www.gnu.org/licenses/gpl.html
20
21 Saturday, June 11, 1994 1:37:32 AM
22
23 Friday, September 16, 1994 2:04:47 PM (Jason')
24 removed all get_shape_information() calls.
25 Monday, December 5, 1994 9:19:55 PM (Jason)
26 flickers in magnetic environments
27
28 Feb 18, 2000 (Loren Petrich):
29 Made VacBobs display properly
30
31 Mar 23, 2000 (Loren Petrich):
32 Made motion-sensor monster typing more generic;
33 since it's now read off of a table, it can easily be changed
34 without rebuilding the app.
35
36 Sep 2, 2000 (Loren Petrich):
37 Idiot-proofed the shapes display, since the shape accessor
38 now returns NULL pointers for nonexistent bitmaps.
39 */
40
41 #include "cseries.h"
42 #include "map.h"
43 #include "monsters.h"
44 #include "render.h"
45 #include "interface.h"
46 #include "motion_sensor.h"
47 #include "player.h"
48 #include "network_games.h"
49 #include "InfoTree.h"
50
51 #include "HUDRenderer_SW.h"
52 #include "HUDRenderer_OGL.h"
53 #include "HUDRenderer_Lua.h"
54
55 #include <math.h>
56 #include <string.h>
57 #include <stdlib.h>
58
59 static short MonsterDisplays[NUMBER_OF_MONSTER_TYPES] =
60 {
61 // Marine
62 MType_Friend,
63 // Ticks
64 MType_Alien,
65 MType_Alien,
66 MType_Alien,
67 // S'pht
68 MType_Alien,
69 MType_Alien,
70 MType_Alien,
71 MType_Alien,
72 // Pfhor
73 MType_Alien,
74 MType_Alien,
75 MType_Alien,
76 MType_Alien,
77 // Bob
78 MType_Friend,
79 MType_Friend,
80 MType_Friend,
81 MType_Friend,
82 // Drone
83 MType_Alien,
84 MType_Alien,
85 MType_Alien,
86 MType_Alien,
87 MType_Alien,
88 // Cyborg
89 MType_Alien,
90 MType_Alien,
91 MType_Alien,
92 MType_Alien,
93 // Enforcer
94 MType_Alien,
95 MType_Alien,
96 // Hunter
97 MType_Alien,
98 MType_Alien,
99 // Trooper
100 MType_Alien,
101 MType_Alien,
102 // Big Cyborg, Hunter
103 MType_Alien,
104 MType_Alien,
105 // F'lickta
106 MType_Alien,
107 MType_Alien,
108 MType_Alien,
109 // S'pht'Kr
110 MType_Alien,
111 MType_Alien,
112 // Juggernauts
113 MType_Alien,
114 MType_Alien,
115 // Tiny ones
116 MType_Alien,
117 MType_Alien,
118 MType_Alien,
119 // VacBobs
120 MType_Friend,
121 MType_Friend,
122 MType_Friend,
123 MType_Friend,
124 };
125
126 /*
127 ??monsters which translate only on frame changes periodically drop off the motion sensor making it appear jumpy
128
129 //when entities first appear on the sensor they are initially visible
130 //entities are drawn one at a time, so the dark spot from one entity can cover the light spot from another
131 //sometimes the motion sensor shapes are not masked correctly (fixed from SHAPES.C)
132 //must save old points in real world coordinates because entities get redrawn when the player turns
133 */
134
135 #define MAXIMUM_MOTION_SENSOR_ENTITIES 12
136
137 #define NUMBER_OF_PREVIOUS_LOCATIONS 6
138
139 struct motion_sensor_definition {
140 uint16 update_frequency;
141 uint16 rescan_frequency;
142 uint16 range;
143 int16 scale;
144 };
145
146 struct motion_sensor_definition motion_sensor_settings = {
147 5, // update_frequency
148 15, // rescan_frequency
149 (8 * WORLD_ONE), // range
150 64 // scale
151 };
152
153 #define MOTION_SENSOR_UPDATE_FREQUENCY motion_sensor_settings.update_frequency
154 #define MOTION_SENSOR_RESCAN_FREQUENCY motion_sensor_settings.rescan_frequency
155 #define MOTION_SENSOR_RANGE motion_sensor_settings.range
156 #define MOTION_SENSOR_SCALE motion_sensor_settings.scale
157
158 #define OBJECT_IS_VISIBLE_TO_MOTION_SENSOR(o) true
159
160 #define FLICKER_FREQUENCY 0xe
161
162 /* ---------- structures */
163
164 /* an entity can�t just be jerked from the array, because his signal should fade out, so we
165 mark him as �being removed� and wait until his last signal fades away to actually remove
166 him */
167 #define SLOT_IS_BEING_REMOVED_BIT 0x4000
168 #define SLOT_IS_BEING_REMOVED(e) ((e)->flags&(uint16)SLOT_IS_BEING_REMOVED_BIT)
169 #define MARK_SLOT_AS_BEING_REMOVED(e) ((e)->flags|=(uint16)SLOT_IS_BEING_REMOVED_BIT)
170
171 struct entity_data
172 {
173 uint16 flags; /* [slot_used.1] [slot_being_removed.1] [unused.14] */
174
175 short monster_index;
176 shape_descriptor shape;
177
178 short remove_delay; /* only valid if this entity is being removed [0,NUMBER_OF_PREVIOUS_LOCATIONS) */
179
180 point2d previous_points[NUMBER_OF_PREVIOUS_LOCATIONS];
181 bool visible_flags[NUMBER_OF_PREVIOUS_LOCATIONS];
182
183 world_point3d last_location;
184 angle last_facing;
185 };
186
187 struct region_data
188 {
189 short x0, x1;
190 };
191
192 /* ---------- globals */
193
194 static short motion_sensor_player_index;
195
196 static short motion_sensor_side_length;
197
198 static struct region_data *sensor_region;
199 static struct entity_data *entities;
200 static short network_compass_state;
201
202 static shape_descriptor mount_shape, virgin_mount_shapes, compass_shapes;
203 static shape_descriptor alien_shapes, friendly_shapes, enemy_shapes;
204
205 static bool motion_sensor_changed;
206 static int32 ticks_since_last_update, ticks_since_last_rescan;
207
208 /* ---------- private code */
209
210 static void erase_all_entity_blips(void);
211
212 static void precalculate_sensor_region(short side_length);
213
214 static short find_or_add_motion_sensor_entity(short monster_index);
215
216 static shape_descriptor get_motion_sensor_entity_shape(short monster_index);
217
218 static void clipped_transparent_sprite_copy(struct bitmap_definition *source, struct bitmap_definition *destination,
219 struct region_data *region, short x0, short y0);
220 static void bitmap_window_copy(struct bitmap_definition *source, struct bitmap_definition *destination,
221 short x0, short y0, short x1, short y1);
222 static void unclipped_solid_sprite_copy(struct bitmap_definition *source,
223 struct bitmap_definition *destination, short x0, short y0);
224
225 /* ---------- code */
226
initialize_motion_sensor(shape_descriptor mount,shape_descriptor virgin_mounts,shape_descriptor aliens,shape_descriptor friends,shape_descriptor enemies,shape_descriptor compasses,short side_length)227 void initialize_motion_sensor(
228 shape_descriptor mount,
229 shape_descriptor virgin_mounts,
230 shape_descriptor aliens,
231 shape_descriptor friends,
232 shape_descriptor enemies,
233 shape_descriptor compasses,
234 short side_length)
235 {
236 mount_shape= mount;
237 virgin_mount_shapes= virgin_mounts;
238 enemy_shapes= enemies;
239 friendly_shapes= friends;
240 alien_shapes= aliens;
241 compass_shapes= compasses;
242
243 entities= new entity_data[MAXIMUM_MOTION_SENSOR_ENTITIES];
244 assert(entities);
245 for (int i = 0; i < MAXIMUM_MOTION_SENSOR_ENTITIES; i++) {
246 entities[i].flags = 0;
247 }
248
249 sensor_region= new region_data[side_length];
250 assert(sensor_region);
251
252 /* precalculate the sensor region */
253 precalculate_sensor_region(side_length);
254
255 /* reset_motion_sensor() should be called before the motion sensor is used, but after it�s
256 shapes are loaded (because it will do bitmap copying) */
257 }
258
259 extern bool shapes_file_is_m1();
260
reset_motion_sensor(short player_index)261 void reset_motion_sensor(
262 short player_index)
263 {
264 struct bitmap_definition *mount, *virgin_mount;
265 short i;
266
267 motion_sensor_player_index= player_index;
268 ticks_since_last_update= ticks_since_last_rescan= 0;
269
270 if (!shapes_file_is_m1())
271 {
272 get_shape_bitmap_and_shading_table(mount_shape, &mount, (void **) NULL, NONE);
273 if (!mount) return;
274 get_shape_bitmap_and_shading_table(virgin_mount_shapes, &virgin_mount, (void **) NULL, NONE);
275 if (!virgin_mount) return;
276
277 assert(mount->width==virgin_mount->width);
278 assert(mount->height==virgin_mount->height);
279 bitmap_window_copy(virgin_mount, mount, 0, 0, mount->width, mount->height);
280 }
281
282 for (i= 0; i<MAXIMUM_MOTION_SENSOR_ENTITIES; ++i) MARK_SLOT_AS_FREE(entities+i);
283
284 network_compass_state= _network_compass_all_off;
285 }
286
287 extern bool MotionSensorActive;
288
289 // regardless of frame rate, this will update the positions of the objects
290 // we are tracking (motion_sensor_scan() is called each tick)
motion_sensor_scan(void)291 void motion_sensor_scan(void)
292 {
293 if ((GET_GAME_OPTIONS() & _motion_sensor_does_not_work) || !MotionSensorActive) {
294 return;
295 }
296 if (m1_solo_player_in_terminal()) {
297 return;
298 }
299
300 struct object_data *owner_object= get_object_data(get_player_data(motion_sensor_player_index)->object_index);
301
302 /* if we need to scan for new objects, flood around the owner monster looking for other,
303 visible monsters within our range */
304 if ((--ticks_since_last_rescan) < 0)
305 {
306 struct monster_data *monster;
307 short monster_index;
308
309 for (monster_index=0,monster=monsters;monster_index<MAXIMUM_MONSTERS_PER_MAP;++monster,++monster_index)
310 {
311 if (SLOT_IS_USED(monster)&&(MONSTER_IS_PLAYER(monster)||MONSTER_IS_ACTIVE(monster)))
312 {
313 struct object_data *object= get_object_data(monster->object_index);
314 world_distance distance= guess_distance2d((world_point2d *) &object->location, (world_point2d *) &owner_object->location);
315
316 if (distance<MOTION_SENSOR_RANGE && OBJECT_IS_VISIBLE_TO_MOTION_SENSOR(object))
317 {
318 // dprintf("found valid monster #%d", monster_index);
319 find_or_add_motion_sensor_entity(object->permutation);
320 motion_sensor_changed = true;
321 }
322 }
323 }
324
325 ticks_since_last_rescan= MOTION_SENSOR_RESCAN_FREQUENCY;
326 }
327
328 if ((--ticks_since_last_update) < 0) {
329 erase_all_entity_blips();
330
331 ticks_since_last_update = MOTION_SENSOR_UPDATE_FREQUENCY;
332 }
333 }
334
render_motion_sensor(short ticks_elapsed)335 void HUD_SW_Class::render_motion_sensor(short ticks_elapsed)
336 {
337 // Clear the motion sensor and redraw blips
338 struct bitmap_definition *mount, *virgin_mount;
339
340 get_shape_bitmap_and_shading_table(mount_shape, &mount, (void **) NULL, NONE);
341 get_shape_bitmap_and_shading_table(virgin_mount_shapes, &virgin_mount, (void **) NULL, NONE);
342 if (mount && virgin_mount)
343 bitmap_window_copy(virgin_mount, mount, 0, 0, motion_sensor_side_length, motion_sensor_side_length);
344
345 draw_network_compass();
346 draw_all_entity_blips();
347 }
348
render_motion_sensor(short ticks_elapsed)349 void HUD_OGL_Class::render_motion_sensor(short ticks_elapsed)
350 {
351 // Draw background
352 screen_rectangle *r = get_interface_rectangle(_motion_sensor_rect);
353 DrawShapeAtXY(BUILD_DESCRIPTOR(_collection_interface, _motion_sensor_virgin_mount), r->left, r->top);
354
355 // We allways draw all active entities because we have to update the
356 // display on every frame
357 /*if (dynamic_world->player_count > 1)*/
358 draw_network_compass();
359 draw_all_entity_blips();
360 }
361
render_motion_sensor(short ticks_elapsed)362 void HUD_Lua_Class::render_motion_sensor(short ticks_elapsed)
363 {
364 // If we need to update the motion sensor, draw all active entities
365 draw_all_entity_blips();
366 }
367
368
369 /* the interface code will call this function and only draw the motion sensor if we return true */
motion_sensor_has_changed(void)370 bool motion_sensor_has_changed(void)
371 {
372 bool changed = motion_sensor_changed;
373 motion_sensor_changed = false;
374 return changed;
375 }
376
377 /* toggle through the ranges */
adjust_motion_sensor_range(void)378 void adjust_motion_sensor_range(void)
379 {
380 }
381
382 /* ---------- private code */
383
draw_network_compass(void)384 void HUD_Class::draw_network_compass(void)
385 {
386 #if !defined(DISABLE_NETWORKING)
387 short new_state= get_network_compass_state(motion_sensor_player_index);
388 short difference= (new_state^network_compass_state)|new_state;
389
390 if (difference&_network_compass_nw) draw_or_erase_unclipped_shape(36, 36, compass_shapes, (new_state&_network_compass_nw) != 0);
391 if (difference&_network_compass_ne) draw_or_erase_unclipped_shape(61, 36, compass_shapes+1, (new_state&_network_compass_ne) != 0);
392 if (difference&_network_compass_se) draw_or_erase_unclipped_shape(61, 61, compass_shapes+3, (new_state&_network_compass_se) != 0);
393 if (difference&_network_compass_sw) draw_or_erase_unclipped_shape(36, 61, compass_shapes+2, (new_state&_network_compass_sw) != 0);
394
395 network_compass_state= new_state;
396 #endif // !defined(DISABLE_NETWORKING)
397 }
398
erase_all_entity_blips(void)399 void erase_all_entity_blips(void)
400 {
401 struct object_data *owner_object= get_object_data(get_player_data(motion_sensor_player_index)->object_index);
402 struct entity_data *entity;
403 short entity_index;
404
405 /* first erase all locations where the entity changed locations and then did not change
406 locations, and erase it�s last location */
407 for (entity_index=0,entity=entities;entity_index<MAXIMUM_MOTION_SENSOR_ENTITIES;++entity_index,++entity)
408 {
409 if (SLOT_IS_USED(entity))
410 {
411 motion_sensor_changed = true;
412 // dprintf("entity #%d (%p) valid", entity_index, entity);
413 /* see if our monster slot is free; if it is mark this entity as being removed; of
414 course this isn�t wholly accurate and we might start tracking a new monster
415 which has been placed in our old monster�s slot, but we eat that chance
416 without remorse */
417 if (SLOT_IS_USED(monsters+entity->monster_index))
418 {
419 struct object_data *object= get_object_data(get_monster_data(entity->monster_index)->object_index);
420 world_distance distance= guess_distance2d((world_point2d *) &object->location, (world_point2d *) &owner_object->location);
421
422 /* verify that we�re still in range (and mark us as being removed if we�re not */
423 if (distance>MOTION_SENSOR_RANGE || !OBJECT_IS_VISIBLE_TO_MOTION_SENSOR(object))
424 {
425 // dprintf("removed2");
426 MARK_SLOT_AS_BEING_REMOVED(entity);
427 }
428 }
429 else
430 {
431 // dprintf("removed1");
432 MARK_SLOT_AS_BEING_REMOVED(entity);
433 }
434
435 /* adjust the arrays to make room for new entries */
436 memmove(entity->visible_flags+1, entity->visible_flags, (NUMBER_OF_PREVIOUS_LOCATIONS-1)*sizeof(bool));
437 memmove(entity->previous_points+1, entity->previous_points, (NUMBER_OF_PREVIOUS_LOCATIONS-1)*sizeof(point2d));
438 entity->visible_flags[0]= false;
439
440 /* if we�re not being removed, make room for a new location and calculate it */
441 if (!SLOT_IS_BEING_REMOVED(entity))
442 {
443 struct monster_data *monster= get_monster_data(entity->monster_index);
444 struct object_data *object= get_object_data(monster->object_index);
445
446 /* remember if this entity is visible or not */
447 if (object->transfer_mode!=_xfer_invisibility && object->transfer_mode!=_xfer_subtle_invisibility &&
448 (!(static_world->environment_flags&_environment_magnetic) || !((dynamic_world->tick_count+4*monster->object_index)&FLICKER_FREQUENCY)))
449 {
450 if (object->location.x!=entity->last_location.x || object->location.y!=entity->last_location.y ||
451 object->location.z!=entity->last_location.z || object->facing!=entity->last_facing)
452 {
453 entity->visible_flags[0]= true;
454
455 entity->last_location= object->location;
456 entity->last_facing= object->facing;
457 }
458 }
459
460 /* calculate the 2d position on the motion sensor */
461 entity->previous_points[0].x= object->location.x;
462 entity->previous_points[0].y= object->location.y;
463 transform_point2d((world_point2d *)&entity->previous_points[0], (world_point2d *)&owner_object->location, NORMALIZE_ANGLE(owner_object->facing+QUARTER_CIRCLE));
464 //entity->previous_points[0].x>>= MOTION_SENSOR_SCALE;
465 entity->previous_points[0].x /= MOTION_SENSOR_SCALE;
466 // entity->previous_points[0].y>>= MOTION_SENSOR_SCALE;
467 entity->previous_points[0].y /= MOTION_SENSOR_SCALE;
468 }
469 else
470 {
471 /* if this is the last point of an entity which was being removed; mark it as unused */
472 if ((entity->remove_delay+= 1)>=NUMBER_OF_PREVIOUS_LOCATIONS)
473 {
474 MARK_SLOT_AS_FREE(entity);
475 }
476 }
477 }
478 }
479 }
480
draw_all_entity_blips(void)481 void HUD_Class::draw_all_entity_blips(void)
482 {
483 struct entity_data *entity;
484 short entity_index, intensity;
485
486 for (intensity=NUMBER_OF_PREVIOUS_LOCATIONS-1;intensity>=0;--intensity)
487 {
488 for (entity_index=0,entity=entities;entity_index<MAXIMUM_MOTION_SENSOR_ENTITIES;++entity_index,++entity)
489 {
490 if (SLOT_IS_USED(entity))
491 {
492 if (entity->visible_flags[intensity])
493 {
494 draw_entity_blip(&entity->previous_points[intensity], entity->shape + intensity);
495 }
496 }
497 }
498 }
499 }
500
draw_all_entity_blips(void)501 void HUD_Lua_Class::draw_all_entity_blips(void)
502 {
503 struct entity_data *entity;
504 short entity_index, intensity;
505
506 clear_entity_blips();
507 for (intensity=NUMBER_OF_PREVIOUS_LOCATIONS-1;intensity>=0;--intensity)
508 {
509 for (entity_index=0,entity=entities;entity_index<MAXIMUM_MOTION_SENSOR_ENTITIES;++entity_index,++entity)
510 {
511 if (SLOT_IS_USED(entity))
512 {
513 if (entity->visible_flags[intensity])
514 {
515 short display_type = MType_Alien;
516 if (entity->shape == friendly_shapes)
517 display_type = MType_Friend;
518 else if (entity->shape == enemy_shapes)
519 display_type = MType_Enemy;
520 add_entity_blip(display_type, intensity,
521 entity->previous_points[intensity].x * MOTION_SENSOR_SCALE,
522 entity->previous_points[intensity].y * MOTION_SENSOR_SCALE);
523 }
524 }
525 }
526 }
527 }
528
draw_or_erase_unclipped_shape(short x,short y,shape_descriptor shape,bool draw)529 void HUD_SW_Class::draw_or_erase_unclipped_shape(short x, short y, shape_descriptor shape, bool draw)
530 {
531 struct bitmap_definition *mount, *virgin_mount, *blip;
532
533 get_shape_bitmap_and_shading_table(mount_shape, &mount, (void **) NULL, NONE);
534 if (!mount) return;
535 get_shape_bitmap_and_shading_table(virgin_mount_shapes, &virgin_mount, (void **) NULL, NONE);
536 if (!virgin_mount) return;
537 get_shape_bitmap_and_shading_table(shape, &blip, (void **) NULL, NONE);
538 if (!blip) return;
539
540 draw ?
541 unclipped_solid_sprite_copy(blip, mount, x, y) :
542 bitmap_window_copy(virgin_mount, mount, x, y, x+blip->width, y+blip->height);
543 }
544
draw_or_erase_unclipped_shape(short x,short y,shape_descriptor shape,bool draw)545 void HUD_OGL_Class::draw_or_erase_unclipped_shape(short x, short y, shape_descriptor shape, bool draw)
546 {
547 if (draw) {
548 screen_rectangle *r = get_interface_rectangle(_motion_sensor_rect);
549 DrawShapeAtXY(shape, x + r->left, y + r->top);
550 }
551 }
552
draw_entity_blip(point2d * location,shape_descriptor shape)553 void HUD_SW_Class::draw_entity_blip(point2d *location, shape_descriptor shape)
554 {
555 bitmap_definition *mount, *blip;
556
557 get_shape_bitmap_and_shading_table(mount_shape, &mount, (void **) NULL, NONE);
558 if (!mount) return;
559 get_shape_bitmap_and_shading_table(shape, &blip, (void **) NULL, NONE);
560 if (!blip) return;
561
562 clipped_transparent_sprite_copy(blip, mount, sensor_region,
563 location->x + (motion_sensor_side_length>>1) - (blip->width>>1),
564 location->y + (motion_sensor_side_length>>1) - (blip->height>>1));
565 }
566
draw_entity_blip(point2d * location,shape_descriptor shape)567 void HUD_OGL_Class::draw_entity_blip(point2d *location, shape_descriptor shape)
568 {
569 bitmap_definition *blip;
570 get_shape_bitmap_and_shading_table(shape, &blip, (void **) NULL, NONE);
571 if (!blip) return;
572
573 screen_rectangle *r = get_interface_rectangle(_motion_sensor_rect);
574 int x = location->x, y = location->y;
575 int c_x = r->left + (motion_sensor_side_length >> 1);
576 int c_y = r->top + (motion_sensor_side_length >> 1);
577 SetClipPlane(x, y, c_x, c_y, motion_sensor_side_length >> 1);
578 DrawShapeAtXY(shape,
579 x + c_x - (blip->width >> 1),
580 y + c_y - (blip->height >> 1),
581 true);
582 DisableClipPlane();
583 }
584
585 /* if we find an entity that is being removed, we continue with the removal process and ignore
586 the new signal; the new entity will probably not be added to the sensor again for a full
587 second or so (the range should be set so that this is reasonably hard to do) */
find_or_add_motion_sensor_entity(short monster_index)588 static short find_or_add_motion_sensor_entity(
589 short monster_index)
590 {
591 struct entity_data *entity;
592 short entity_index, best_unused_index;
593
594 best_unused_index= NONE;
595 for (entity_index=0,entity=entities;entity_index<MAXIMUM_MOTION_SENSOR_ENTITIES;++entity_index,++entity)
596 {
597 if (SLOT_IS_USED(entity))
598 {
599 if (entity->monster_index==monster_index) break;
600 }
601 else
602 {
603 if (best_unused_index==NONE) best_unused_index= entity_index;
604 }
605 }
606
607 if (entity_index==MAXIMUM_MOTION_SENSOR_ENTITIES)
608 {
609 /* not found; add new entity if we can */
610
611 if (best_unused_index!=NONE)
612 {
613 struct monster_data *monster= get_monster_data(monster_index);
614 struct object_data *object= get_object_data(monster->object_index);
615 short i;
616
617 entity= entities+best_unused_index;
618
619 entity->flags= 0;
620 entity->monster_index= monster_index;
621 entity->shape= get_motion_sensor_entity_shape(monster_index);
622 for (i=0;i<NUMBER_OF_PREVIOUS_LOCATIONS;++i) entity->visible_flags[i]= false;
623 entity->last_location= object->location;
624 entity->last_facing= object->facing;
625 entity->remove_delay= 0;
626 MARK_SLOT_AS_USED(entity);
627
628 // dprintf("new index, pointer: %d, %p", best_unused_index, entity);
629 }
630
631 entity_index= best_unused_index;
632 }
633
634 return entity_index;
635 }
636
precalculate_sensor_region(short side_length)637 static void precalculate_sensor_region(
638 short side_length)
639 {
640 double half_side_length= side_length/2.0;
641 double r= half_side_length + 1.0;
642 short i;
643
644 /* save length for assert() during rendering */
645 motion_sensor_side_length= side_length;
646
647 /* precompute [x0,x1] clipping values for each y value in the circular sensor */
648 for (i=0;i<side_length;++i)
649 {
650 double y= i - half_side_length;
651 double x= sqrt(r*r-y*y);
652
653 if (x>=r) x= r-1.0;
654 sensor_region[i].x0= int16(half_side_length-x);
655 sensor_region[i].x1= int16(x+half_side_length);
656 }
657 }
658
659 /* (x0,y0) and (x1,y1) specify a window to be copied from the source to (x2,y2) in the destination.
660 pixel index zero is transparent (handles clipping) */
bitmap_window_copy(struct bitmap_definition * source,struct bitmap_definition * destination,short x0,short y0,short x1,short y1)661 static void bitmap_window_copy(
662 struct bitmap_definition *source,
663 struct bitmap_definition *destination,
664 short x0,
665 short y0,
666 short x1,
667 short y1)
668 {
669 short count;
670 short y;
671
672 assert(x0<=x1&&y0<=y1);
673
674 if (x0<0) x0= 0;
675 if (y0<0) y0= 0;
676 if (x1>source->width) x1= source->width;
677 if (y1>source->height) y1= source->height;
678
679 assert(source->width==destination->width);
680 assert(source->height==destination->height);
681 assert(destination->width==motion_sensor_side_length);
682 assert(destination->height==motion_sensor_side_length);
683
684 for (y=y0;y<y1;++y)
685 {
686 pixel8 *read= source->row_addresses[y]+x0;
687 pixel8 *write= destination->row_addresses[y]+x0;
688
689 for (count=x1-x0;count>0;--count) *write++= *read++;
690 }
691 }
692
clipped_transparent_sprite_copy(struct bitmap_definition * source,struct bitmap_definition * destination,struct region_data * region,short x0,short y0)693 static void clipped_transparent_sprite_copy(
694 struct bitmap_definition *source,
695 struct bitmap_definition *destination,
696 struct region_data *region,
697 short x0,
698 short y0)
699 {
700 short height, y;
701
702 assert(destination->width==motion_sensor_side_length);
703 assert(destination->height==motion_sensor_side_length);
704
705 y= 0;
706 height= source->height;
707 if (y0+height>destination->height) height= destination->height-y0;
708 if (y0<0)
709 {
710 y= -y0;
711 height+= y0;
712 }
713
714 while ((height-= 1)>=0)
715 {
716 pixel8 pixel, *read, *write;
717 short width= source->width;
718 short clip_left= region[y0+y].x0, clip_right= region[y0+y].x1;
719 short offset= 0;
720
721 if (x0<clip_left) offset= clip_left-x0, width-= offset;
722 if (x0+offset+width>clip_right) width= clip_right-x0-offset;
723
724 assert(y>=0&&y<source->height);
725 assert(y0+y>=0&&y0+y<destination->height);
726
727 read= source->row_addresses[y]+offset;
728 write= destination->row_addresses[y0+y]+x0+offset;
729
730 while ((width-= 1)>=0)
731 {
732 if ((pixel= *read++)!=0) {
733 *write++= pixel;
734 } else {
735 write+= 1;
736 }
737 }
738
739 y+= 1;
740 }
741 }
742
unclipped_solid_sprite_copy(struct bitmap_definition * source,struct bitmap_definition * destination,short x0,short y0)743 static void unclipped_solid_sprite_copy(
744 struct bitmap_definition *source,
745 struct bitmap_definition *destination,
746 short x0,
747 short y0)
748 {
749 short height, y;
750
751 y= 0;
752 height= source->height;
753
754 while ((height-= 1)>=0)
755 {
756 pixel8 *read, *write;
757 short width= source->width;
758
759 assert(y>=0&&y<source->height);
760 assert(y0+y>=0&&y0+y<destination->height);
761
762 read= source->row_addresses[y];
763 write= destination->row_addresses[y0+y]+x0;
764
765 while ((width-= 1)>=0) *write++= *read++;
766
767 y+= 1;
768 }
769 }
770
get_motion_sensor_entity_shape(short monster_index)771 static shape_descriptor get_motion_sensor_entity_shape(
772 short monster_index)
773 {
774 struct monster_data *monster= get_monster_data(monster_index);
775 shape_descriptor shape;
776
777 if (MONSTER_IS_PLAYER(monster))
778 {
779 struct player_data *player= get_player_data(monster_index_to_player_index(monster_index));
780 struct player_data *owner= get_player_data(motion_sensor_player_index);
781
782 shape= ((player->team==owner->team && !(GET_GAME_OPTIONS()&_force_unique_teams)) || GET_GAME_TYPE()==_game_of_cooperative_play) ?
783 friendly_shapes : enemy_shapes;
784 }
785 else
786 {
787 /*
788 switch (monster->type)
789 {
790 case _civilian_crew:
791 case _civilian_science:
792 case _civilian_security:
793 case _civilian_assimilated:
794 // LP additions: the VacBobs
795 case _civilian_fusion_crew:
796 case _civilian_fusion_science:
797 case _civilian_fusion_security:
798 case _civilian_fusion_assimilated:
799 shape= friendly_shapes;
800 break;
801
802 default:
803 shape= alien_shapes;
804 break;
805 }
806 */
807 switch(MonsterDisplays[monster->type])
808 {
809 case MType_Friend:
810 shape = friendly_shapes;
811 break;
812
813 case MType_Alien:
814 shape = alien_shapes;
815 break;
816
817 case MType_Enemy:
818 default:
819 shape = enemy_shapes;
820 break;
821 }
822 }
823
824 return shape;
825 }
826
827 // XML elements for parsing motion-sensor specification
828 short *OriginalMonsterDisplays = NULL;
829 struct motion_sensor_definition *original_motion_sensor_settings = NULL;
830
reset_mml_motion_sensor()831 void reset_mml_motion_sensor()
832 {
833 if (original_motion_sensor_settings) {
834 motion_sensor_settings = *original_motion_sensor_settings;
835 free(original_motion_sensor_settings);
836 original_motion_sensor_settings = NULL;
837 }
838
839 if (OriginalMonsterDisplays) {
840 for (int i = 0; i < NUMBER_OF_MONSTER_TYPES; i++)
841 MonsterDisplays[i] = OriginalMonsterDisplays[i];
842 free(OriginalMonsterDisplays);
843 OriginalMonsterDisplays = NULL;
844 }
845 }
846
parse_mml_motion_sensor(const InfoTree & root)847 void parse_mml_motion_sensor(const InfoTree& root)
848 {
849 // back up old values first
850 if (!original_motion_sensor_settings) {
851 original_motion_sensor_settings = (struct motion_sensor_definition *) malloc(sizeof(struct motion_sensor_definition));
852 assert(original_motion_sensor_settings);
853 *original_motion_sensor_settings = motion_sensor_settings;
854 }
855
856 if (!OriginalMonsterDisplays) {
857 OriginalMonsterDisplays = (short *) malloc(sizeof(short) * NUMBER_OF_MONSTER_TYPES);
858 assert(OriginalMonsterDisplays);
859 for (int i = 0; i < NUMBER_OF_MONSTER_TYPES; i++)
860 OriginalMonsterDisplays[i] = MonsterDisplays[i];
861 }
862
863 root.read_attr("scale", MOTION_SENSOR_SCALE);
864 short range;
865 if (root.read_wu("range", range))
866 MOTION_SENSOR_RANGE = range;
867 root.read_attr("update_frequency", MOTION_SENSOR_UPDATE_FREQUENCY);
868 root.read_attr("rescan_frequency", MOTION_SENSOR_RESCAN_FREQUENCY);
869
870 BOOST_FOREACH(InfoTree assign, root.children_named("assign"))
871 {
872 int16 index;
873 if (!assign.read_indexed("monster", index, NUMBER_OF_MONSTER_TYPES))
874 continue;
875 assign.read_indexed("type", MonsterDisplays[index], NUMBER_OF_MDISPTYPES);
876 }
877 }
878