1 /* ResidualVM - A 3D game interpreter
2 *
3 * ResidualVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the AUTHORS
5 * file distributed with this source distribution.
6 *
7 * Additional copyright for this file:
8 * Copyright (C) 1999-2000 Revolution Software Ltd.
9 * This code is based on source code created by Revolution Software,
10 * used with permission.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 *
26 */
27
28 #include "engines/icb/p4.h"
29 #include "engines/icb/common/px_common.h"
30 #include "engines/icb/common/px_game_object.h"
31 #include "engines/icb/common/px_scriptengine.h"
32 #include "engines/icb/common/ptr_util.h"
33 #include "engines/icb/session.h"
34 #include "engines/icb/object_structs.h"
35 #include "engines/icb/debug.h"
36 #include "engines/icb/player.h"
37 #include "engines/icb/global_objects.h"
38 #include "engines/icb/global_switches.h"
39 #include "engines/icb/res_man.h"
40 #include "engines/icb/floors.h"
41 #include "engines/icb/mission.h"
42
43 namespace ICB {
44
45 #define RUBBER 30
46 #define REAL_RUBBER (30 * REAL_ONE)
47
48 PXvector *posi;
49 uint32 this_rect;
50 _floor *obfloor;
51
fn_floor_and_floor_camera_linked(int32 & result,int32 * params)52 mcodeFunctionReturnCodes fn_floor_and_floor_camera_linked(int32 &result, int32 *params) { return (MS->fn_floor_and_floor_camera_linked(result, params)); }
53
fn_switch_to_manual_camera(int32 & result,int32 * params)54 mcodeFunctionReturnCodes fn_switch_to_manual_camera(int32 &result, int32 *params) { return (MS->fn_switch_to_manual_camera(result, params)); }
55
fn_cancel_manual_camera(int32 & result,int32 * params)56 mcodeFunctionReturnCodes fn_cancel_manual_camera(int32 &result, int32 *params) { return (MS->fn_cancel_manual_camera(result, params)); }
57
fn_is_current_camera(int32 & result,int32 * params)58 mcodeFunctionReturnCodes fn_is_current_camera(int32 &result, int32 *params) { return (MS->fn_is_current_camera(result, params)); }
59
fn_is_current_location(int32 & result,int32 * params)60 mcodeFunctionReturnCodes fn_is_current_location(int32 &result, int32 *params) { return (MS->fn_is_current_location(result, params)); }
61
fn_floor_and_floor_camera_linked(int32 &,int32 * params)62 mcodeFunctionReturnCodes _game_session::fn_floor_and_floor_camera_linked(int32 &, int32 *params) {
63 // params 0 ascii name of home floor
64 // 1 ascii name of viewable floor name
65
66 // search for a camera of this name
67 // confirm floor exists
68 // add floor number to cameras extra floor list
69
70 uint32 home_floor_num;
71 uint32 floor_num;
72 const char *home_floor_name = (const char *)MemoryUtil::resolvePtr(params[0]);
73 const char *floor_name = (const char *)MemoryUtil::resolvePtr(params[1]);
74
75 home_floor_num = floor_def->Fetch_floor_number_by_name(home_floor_name);
76 if (home_floor_num == PX_LINKED_DATA_FILE_ERROR)
77 Fatal_error("fn_floor_and_floor_camera_linked cant find floor [%s]", home_floor_name);
78
79 floor_num = floor_def->Fetch_floor_number_by_name(floor_name);
80 if (floor_num == PX_LINKED_DATA_FILE_ERROR)
81 Fatal_error("fn_floor_and_floor_camera_linked cant find floor [%s]", floor_name);
82
83 if (home_floor_num == floor_num)
84 Fatal_error("fn_floor_and_floor_camera_linked finds [%s] and [%s] are same floor!", home_floor_name, floor_name);
85
86 // add viewable floor number to the camera that displays our primary home floor
87
88 uint32 index1 = floor_to_camera_index[home_floor_num];
89 uint32 index2 = floor_to_camera_index[floor_num];
90
91 cam_floor_list[index1].extra_floors[cam_floor_list[index1].num_extra_floors++] = floor_num;
92 cam_floor_list[index2].extra_floors[cam_floor_list[index2].num_extra_floors++] = home_floor_num;
93
94 if (cam_floor_list[index1].num_extra_floors == MAX_extra_floors)
95 Fatal_error("fn_floor_and_floor_camera_linked too many extra floors");
96
97 return IR_CONT;
98 }
99
Build_camera_table()100 void _game_session::Build_camera_table() {
101 // read each floor to note the name of the camera
102 // each new camera name is put into a list of names
103 // the floor number gets the numer of that camerea in the list
104 uint32 j, k;
105 _floor *floor;
106 uint32 tot;
107
108 Zdebug("\n***********building camera table**************");
109
110 num_cameras = 0; // reset
111
112 // reset extra cams table
113 for (j = 0; j < MAX_floors; j++)
114 cam_floor_list[j].num_extra_floors = 0;
115
116 tot = floor_def->Fetch_total_floors();
117 if (!tot)
118 Fatal_error("Build_camera_table finds no floors?");
119
120 for (j = 0; j < tot; j++) {
121 floor = (_floor *)floor_def->Fetch_floor_number(j);
122 if (floor->camera_name_offset) {
123 // is this camera name already been assigned a slot?
124 k = 0;
125 while ((k < num_cameras) && (strcmp(camera_name_list[k], (((char *)floor) + floor->camera_name_offset))))
126 ++k;
127
128 if (k == num_cameras) { // didnt find this camera
129 Zdebug(" new camera %d [%s] [%s]", num_cameras, (((char *)floor) + floor->camera_name_offset), floor->camera_cluster);
130 camera_name_list[num_cameras] = (((char *)floor) + floor->camera_name_offset); // add camera name to list of names
131 camera_cluster_list[num_cameras] = floor->camera_cluster;
132 floor_to_camera_index[j] = num_cameras; // set this floors camera number
133
134 num_cameras++;
135 } else {
136 floor_to_camera_index[j] = k; // set this floors camera number
137 }
138
139 Zdebug(" floor %d gets camera number %d", j, floor_to_camera_index[j]);
140 } else {
141 // camera name is missing
142 floor_to_camera_index[j] = NO_CAMERA_CHOSEN;
143 Zdebug("floor %d camera missing!!!!!!!!!!!", j);
144 }
145 }
146
147 Zdebug("***********building camera table**************\n");
148 }
149
Object_visible_to_camera(uint32 id)150 bool8 _game_session::Object_visible_to_camera(uint32 id) {
151 // is id visible to current camera
152
153 uint32 j, num_extra;
154 #define FLOOR_NO logic_structs[id]->owner_floor_rect
155 #define MM logic_structs[id]->mega
156
157 // Don't want to see held objects
158 if (logic_structs[id]->ob_status == OB_STATUS_HELD)
159 return (FALSE8);
160
161 // high level user clip
162 if ((MM) && (!MM->display_me))
163 return FALSE8;
164
165 // check for no camera chosen - 1st cycle
166 if (cur_camera_number == NO_CAMERA_CHOSEN)
167 return (FALSE8);
168
169 // if following the player then always TRUE8 - need this because of rubber band
170 if ((!g_mission->camera_follow_id_overide) && (id == player.Fetch_player_id()))
171 return TRUE8;
172
173 if (floor_to_camera_index[FLOOR_NO] == cur_camera_number)
174 return (TRUE8);
175
176 // now check if the current floor is registered as an extra floor viewable from current camera
177 num_extra = cam_floor_list[cur_camera_number].num_extra_floors;
178 for (j = 0; j < num_extra; j++)
179 if (cam_floor_list[cur_camera_number].extra_floors[j] == FLOOR_NO)
180 return (TRUE8);
181
182 // definitely not on screen
183 return (FALSE8);
184 }
185
Reset_camera_director()186 void _game_session::Reset_camera_director() {
187 // by masking this off we force a new camera to be initialised in current view mode
188 cur_camera_number = NO_CAMERA_CHOSEN;
189 manual_camera = FALSE8;
190 wa_camera = FALSE8;
191 wa_tied_to_pin = FALSE8;
192 wa_tied_to_exit_pin = FALSE8;
193 this_rect = 0; // will be legal floor which is all that matters
194 }
195
Contains(int32 x1,int32 y1,int32 x2,int32 y2,int32 mx,int32 my)196 int32 Contains(int32 x1, int32 y1, int32 x2, int32 y2, int32 mx, int32 my) {
197 int32 tmp, x3;
198
199 x1 = x1 << 1;
200 y1 = y1 << 1;
201 x2 = x2 << 1;
202 y2 = y2 << 1;
203 mx = (mx << 1) + 1;
204 my = (my << 1) + 1;
205
206 if (((y1 < my) && (y2 > my)) || ((y1 > my) && (y2 < my))) {
207 if (x1 > x2) {
208 tmp = x1;
209 x1 = x2;
210 x2 = tmp;
211 tmp = y1;
212 y1 = y2;
213 y2 = tmp;
214 }
215 if (y1 < y2)
216 x3 = x1 + (my - y1) * (x2 - x1) / (y2 - y1);
217 else
218 x3 = x2 - (my - y2) * (x2 - x1) / (y1 - y2);
219 if (x3 < mx)
220 return 1;
221 }
222 return 0;
223 }
224
Camera_director()225 void _game_session::Camera_director() {
226 // check which object the camera is linked to
227 // check which floor rect that object is on
228 // if no camera run a script
229 // else if new camera switch it in
230
231 const __aWalkArea *wa;
232 uint32 k;
233 uint32 hit;
234 PXreal sub1, sub2, len, y;
235
236 Prepare_camera_floors();
237
238 // a manual script camera completely overides the floor and WA system cameras
239 if ((manual_camera) || (camera_lock))
240 return;
241
242 // do walkarea stuff first
243 // are we currently on one?
244 // if so are we still on it
245 // if not are we on one in the list
246 // if so start it
247 // else go through normal floor system
248
249 if (wa_camera) {
250 // we are currently using a WA camera
251
252 wa = MS->wa_list[wa_number];
253
254 y = floor_def->Return_true_y((PXreal)wa->y);
255
256 if ((y >= obfloor->base_height) && (y < (floor_def->Fetch_floors_volume_height(this_rect)))) {
257
258 if (wa_tied_to_pin) {
259 // we are still tied to the pin point
260 // if we are within the stretch distance from the pin then we remain on the wa camera - even if we are now
261 // actually outside of it
262
263 sub1 = (PXreal)posi->x - wa_pin_x;
264 sub2 = (PXreal)posi->z - wa_pin_z;
265
266 // dist
267 len = (PXreal)((sub1 * sub1) + (sub2 * sub2));
268
269 Tdebug("rubber.txt", "len %3.2f pos %3.2f, %3.2f pin %3.2f, %3.2f", len, posi->x, posi->z, wa_pin_x, wa_pin_z);
270
271 if (len < (PXreal)(REAL_RUBBER * REAL_RUBBER))
272 return; // still constrained by the band
273
274 // band is broken
275 wa_tied_to_pin = FALSE8; // release
276 }
277
278 if ((posi->x > wa->x) && (posi->x < (wa->x + wa->w)) && (posi->z > wa->z) && (posi->z < (wa->z + wa->h))) {
279 // we're inside the rectangle
280 // are we within the polygon
281
282 hit = 0; // result is 0, miss
283
284 for (k = 0; k < (wa->noPoints - 1); k++) {
285 if (Contains(wa->points[k].x, wa->points[k].z, wa->points[k + 1].x, wa->points[k + 1].z, (uint32)posi->x, (uint32)posi->z))
286 hit = 1 - hit;
287 }
288
289 if (hit) { // hurrah - still hitting
290 cur_camera_number = floor_to_camera_index[this_rect];
291 return;
292 }
293 }
294 }
295
296 // only stick an off pin in if we're not wandering straight onto another
297 if (!Process_wa_list()) {
298 // not hitting anymore
299 if (!g_mission->camera_follow_id_overide)
300 this_rect = floor_def->Return_non_rubber_floor_no(logic_structs[player.Fetch_player_id()], this_rect);
301 else
302 this_rect = floor_def->Return_non_rubber_floor_no(logic_structs[g_mission->camera_follow_id_overide], this_rect);
303
304 // stick a pin in that we must leave before hitting a wa again
305 wa_pin_x = posi->x;
306 wa_pin_y = posi->y;
307 wa_pin_z = posi->z;
308 wa_tied_to_exit_pin = TRUE8;
309
310 cur_camera_number = NO_CAMERA_CHOSEN; // force a choose
311 wa_camera = FALSE8; // not any more
312 }
313 }
314
315 // not on one so check em all to see if we are
316 // but first check to see if we are tied to a get-off-wa pin
317
318 if (wa_tied_to_exit_pin) {
319
320 if (wa_pin_y != posi->y) { // as soon as we change y the pin breaks
321 wa_tied_to_exit_pin = FALSE8; // off
322 } else {
323 sub1 = (PXreal)posi->x - wa_pin_x;
324 sub2 = (PXreal)posi->z - wa_pin_z;
325
326 // dist
327 len = (PXreal)((sub1 * sub1) + (sub2 * sub2));
328
329 if (len > (PXreal)(REAL_RUBBER * REAL_RUBBER)) {
330 // band is broken
331 wa_tied_to_exit_pin = FALSE8; // release
332 }
333 }
334 } else {
335 Process_wa_list();
336 }
337
338 // not on a WA camera so check via the floor rects
339
340 if (this_rect == PXNULL) { // object not on a rect??
341 // we cant pick a camera set view
342 // if a set is set up then that will continue to be used
343 // if there isnt a set at all (this is first cycle) then engine will switch to nethack mode
344 return;
345 }
346
347 // has player changed camera - work this out from primary camera
348 if (cur_camera_number != floor_to_camera_index[this_rect]) {
349 // we are now on a floor with a different camera
350 // BUT
351 // we allow some elastic on the old one before switching
352
353 // get floor that we last changed camera on
354 // if we are within its bounds then remain
355
356 if (cur_camera_number != NO_CAMERA_CHOSEN) {
357 _floor *floor;
358 floor = (_floor *)floor_def->Fetch_floor_number(anchor_floor);
359
360 if ((posi->y == (PXreal)(floor->base_height)) && (posi->x >= (PXreal)(floor->rect.x1 - RUBBER)) && (posi->x <= (PXreal)(floor->rect.x2 + RUBBER)) &&
361 (posi->z >= (PXreal)(floor->rect.z1 - RUBBER)) && (posi->z <= (PXreal)(floor->rect.z2 + RUBBER)))
362 return; // still within the rubber banded camera
363 }
364
365 if (floor_to_camera_index[this_rect] == 0xffffffff) { // no named camera so its a more complex logic switch
366 // ok, lets assume that there was no special camera!
367 g_px->display_mode = TEMP_NETHACK; // stick us into temporary nethack mode which will bounce out again if it can
368 Zdebug("no named camera! - entering TEMP_NETHACK");
369 } else { // ok, there is a named camera! and we know its different from current
370 // different from current
371 Zdebug(" make cam=%s %s", camera_name_list[floor_to_camera_index[this_rect]], camera_cluster_list[floor_to_camera_index[this_rect]]);
372
373 // set camera number
374 cur_camera_number = floor_to_camera_index[this_rect];
375 anchor_floor = this_rect;
376
377 // if we're not in NETHACK mode then switch in the set
378 if (g_px->display_mode != NETHACK) {
379 g_px->display_mode = THREED; // force back in-case we were in TEMP_NETHACK
380
381 // init the new set - Initialise_set will record the name of the new camera
382 // it will also handle missing set files and bung us into TEMP_NETHACK mode if it has to
383 Initialise_set(camera_name_list[floor_to_camera_index[this_rect]], camera_cluster_list[floor_to_camera_index[this_rect]]); // name);
384
385 // force in the anims of megas not playing stand - techs and the like
386 MS->One_logic_cycle();
387 }
388 }
389 }
390 }
391
Prepare_camera_floors()392 void _game_session::Prepare_camera_floors() {
393 // get floor numbers from followed object
394 // get a mega class objects world position - can be player or other mega
395 if (!g_mission->camera_follow_id_overide) {
396 if (!player.Player_exists())
397 Fatal_error("camera director cant choose a scene as player object has been shut down");
398
399 if (logic_structs[player.Fetch_player_id()]->ob_status == OB_STATUS_HELD)
400 Fatal_error("camera director cant choose a scene as player object has been shut down");
401
402 posi = (PXvector *)&logic_structs[player.Fetch_player_id()]->mega->actor_xyz;
403
404 this_rect = logic_structs[player.Fetch_player_id()]->owner_floor_rect;
405 } else {
406 // following another mega character
407
408 posi = (PXvector *)&logic_structs[g_mission->camera_follow_id_overide]->mega->actor_xyz;
409
410 this_rect = logic_structs[g_mission->camera_follow_id_overide]->owner_floor_rect;
411 }
412
413 // fetch the floor
414 obfloor = (_floor *)floor_def->Fetch_floor_number(this_rect);
415 }
416
Process_wa_list()417 bool8 _game_session::Process_wa_list() {
418 const __aWalkArea *wa;
419 uint32 j, k;
420 uint32 hit;
421 char *name;
422 PXreal y;
423
424 for (j = 0; j < MS->total_was; j++) {
425 wa = MS->wa_list[j];
426
427 y = floor_def->Return_true_y((PXreal)wa->y);
428
429 if ((y >= obfloor->base_height) && (y < (floor_def->Fetch_floors_volume_height(this_rect)))) {
430 if ((posi->x > wa->x) && (posi->x < (wa->x + wa->w)) && (posi->z > wa->z) && (posi->z < (wa->z + wa->h))) {
431 // we're inside the rectangle
432 // are we within the polygon
433
434 hit = 0; // result is 0, miss
435
436 for (k = 0; k < (wa->noPoints - 1); k++) {
437 if (Contains(wa->points[k].x, wa->points[k].z, wa->points[k + 1].x, wa->points[k + 1].z, (uint32)posi->x, (uint32)posi->z))
438 hit = 1 - hit;
439 }
440
441 if (hit) { // hey we hit the closed poly
442 name = (char *)const_cast<ICB::__point *>(&wa->points[wa->noPoints]);
443
444 Tdebug("cam_changes.txt", " WA camera name %s cluster %s", name, wa->cameraCluster);
445
446 Initialise_set(name, wa->cameraCluster); // name, clusterVersion of the name
447
448 wa_camera = TRUE8;
449 wa_number = j;
450 wa_pin_x = posi->x;
451 wa_pin_z = posi->z;
452 wa_tied_to_pin = TRUE8;
453
454 // in case we are coming here afresh - from a reset camera director - i.e. from nethack mode
455 // find the floor and set the current camera
456 // in other words we fake the system to think that the floor camera is chosen - thats how we decide
457 // who is on camera.
458
459 if (!g_mission->camera_follow_id_overide) {
460 this_rect = floor_def->Return_floor_rect(posi->x, posi->z, posi->y, 0);
461 } else {
462 this_rect = floor_def->Return_non_rubber_floor_no(logic_structs[g_mission->camera_follow_id_overide], this_rect);
463 }
464
465 // set camera number
466 cur_camera_number = floor_to_camera_index[this_rect];
467
468 Tdebug("cam_changes.txt", " floor %d", cur_camera_number);
469
470 return TRUE8;
471 }
472 }
473 }
474 }
475
476 return FALSE8;
477 }
478
fn_switch_to_manual_camera(int32 &,int32 * params)479 mcodeFunctionReturnCodes _game_session::fn_switch_to_manual_camera(int32 &, int32 *params) {
480 // switch in a manual camera
481
482 // params 0 stub name of room - CORRIDOR\pc\camera
483 // 1 stub name of camera - corridor\pc\CAMERA
484 // 2 name of primary floor
485 uint32 floor_num;
486 uint32 len;
487 char h_buf[8];
488 const char *room_name = (const char *)MemoryUtil::resolvePtr(params[0]);
489 const char *camera_name = (const char *)MemoryUtil::resolvePtr(params[1]);
490 const char *floor_name = (const char *)MemoryUtil::resolvePtr(params[2]);
491
492 // get primary floor number
493 floor_num = floor_def->Fetch_floor_number_by_name(floor_name);
494
495 // set camera number
496 cur_camera_number = floor_to_camera_index[floor_num];
497
498 manual_camera = TRUE8;
499
500 len = sprintf(manual_camera_name, "%s\\pc\\%s", room_name, camera_name);
501 if (len > ENGINE_STRING_LEN)
502 Fatal_error("fn_switch_to_manual_camera string len error");
503
504 HashFile(manual_camera_name, h_buf);
505
506 Tdebug("cam_changes.txt", " built name %s %s", (const char *)temp_buf, h_buf);
507 Initialise_set(manual_camera_name, h_buf); // name);
508
509 return IR_CONT;
510 }
511
fn_is_current_camera(int32 & result,int32 * params)512 mcodeFunctionReturnCodes _game_session::fn_is_current_camera(int32 &result, int32 *params) {
513 // check passed string against current camera
514 // params 0 ascii name
515 const char *camera_name = (const char *)MemoryUtil::resolvePtr(params[0]);
516
517 // check for not set initialised yet - 1st game cycle
518 if (!set.OK()) {
519 result = 0;
520 return IR_CONT;
521 }
522
523 if (!strstr(set.GetSetName(), camera_name))
524 result = 0; // no
525 else
526 result = 1;
527
528 return IR_CONT;
529 }
530
fn_is_current_location(int32 & result,int32 * params)531 mcodeFunctionReturnCodes _game_session::fn_is_current_location(int32 &result, int32 *params) {
532 char h_buf[8];
533 uint32 len;
534 const char *location_name = (const char *)MemoryUtil::resolvePtr(params[0]);
535
536 // First we need to know which location the player is in, because the information level
537 // for this is automatically at full.
538 //uint32 nPlayerFloorIndex = MS->logic_structs[MS->player.Fetch_player_id()]->owner_floor_rect;
539
540 Message_box("is %s current location?", location_name);
541
542 len = sprintf(manual_camera_name, "%s\\pc\\%s", location_name, set.GetSetName());
543 if (len > ENGINE_STRING_LEN)
544 Fatal_error("fn_is_current_location string len error");
545
546 HashFile(manual_camera_name, h_buf);
547 result = 1;
548 // continue script
549 return IR_CONT;
550 }
551
fn_cancel_manual_camera(int32 &,int32 *)552 mcodeFunctionReturnCodes _game_session::fn_cancel_manual_camera(int32 &, int32 *) {
553 // cancel the manual camera
554 Tdebug("cam_changes.txt", "Releasing manual camera");
555
556 if (!manual_camera)
557 return IR_CONT;
558
559 Reset_camera_director();
560
561 return (IR_CONT);
562 }
563
564 // Compute if mega's are on or off current camera and send events to them to say "ON_CAMERA" / "OFF_CAMERA"
UpdateOnOffCamera()565 void _game_session::UpdateOnOffCamera() {
566 PXcamera &camera = GetCamera();
567 _logic *log;
568
569 for (uint32 j = 0; j < number_of_voxel_ids; j++) {
570 // fetch the logic structure for the game object that has a voxel image to render
571 log = logic_structs[voxel_id_list[j]];
572
573 // shift the this cycle camera flag to last cycle camera flag
574 log->mega->ShiftViewState();
575
576 // person owned by current camera floor? TEMP check
577 if (Object_visible_to_camera(voxel_id_list[j])) {
578 bool8 result = TRUE8;
579 PXvector filmPosition;
580 PXWorldToFilm(log->mega->actor_xyz, camera, result, filmPosition);
581
582 // i.e. his feet are visible !
583 if (result && (filmPosition.z < -g_actor_hither_plane))
584 log->mega->SetThisViewState(ON_CAMERA);
585 }
586
587 // Now test the view state flags and send an appropriate event
588 // Just walked on camera
589 if (log->mega->viewState == OFF_ON_CAMERA) {
590 g_oEventManager->PostNamedEvent(EVENT_ON_CAMERA, voxel_id_list[j]);
591 }
592 // Just walked off camera
593 else if (log->mega->viewState == ON_OFF_CAMERA) {
594 g_oEventManager->PostNamedEvent(EVENT_OFF_CAMERA, voxel_id_list[j]);
595 }
596 }
597 }
598
599 } // End of namespace ICB
600