/* ScummVM - Scumm Interpreter * Copyright (C) 2004 The ScummVM project * * The ReInherit Engine is (C)2000-2003 by Daniel Balsom. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * $Header$ * */ /* Description: Object map / Object click-area module Notes: Polygon Hit Test code ( HitTestPoly() ) adapted from code (C) Eric Haines appearing in Graphics Gems IV, "Point in Polygon Strategies." p. 24-46, code: p. 34-45 */ #include "reinherit.h" #include "yslib.h" /* * Uses the following modules: \*--------------------------------------------------------------------------*/ #include "cvar_mod.h" #include "console_mod.h" #include "gfx_mod.h" #include "font_mod.h" /* * Module options \*--------------------------------------------------------------------------*/ #define R_OBJECTMAP_DEBUG R_DEBUG_INFO /* * Begin module \*--------------------------------------------------------------------------*/ #include "objectmap_mod.h" #include "objectmap.h" namespace Saga { static R_OBJECTMAP_INFO OMInfo; int OBJECTMAP_Register(void) { CVAR_RegisterFunc(CF_object_info, "object_info", NULL, R_CVAR_NONE, 0, 0); return R_SUCCESS; } int OBJECTMAP_Init(void) /*--------------------------------------------------------------------------*\ * Initializes the object map module, creates module allocation context \*--------------------------------------------------------------------------*/ { R_printf(R_STDOUT, "OBJECTMAP Module: Initializing...\n"); OMInfo.initialized = 1; return R_SUCCESS; } int OBJECTMAP_Shutdown(void) /*--------------------------------------------------------------------------*\ * Shuts down the object map module, destroys module allocation context \*--------------------------------------------------------------------------*/ { if (!OMInfo.initialized) { return R_FAILURE; } R_printf(R_STDOUT, "OBJECTMAP Module: Shutting down...\n"); OBJECTMAP_Free(); OBJECTMAP_FreeNames(); R_printf(R_STDOUT, "OBJECTMAP Module: Shutdown AOK.\n"); OMInfo.initialized = 0; return R_SUCCESS; } int OBJECTMAP_Load(const uchar * om_res, size_t om_res_len) /*--------------------------------------------------------------------------*\ * Loads an object map resource ( objects ( clickareas ( points ) ) ) \*--------------------------------------------------------------------------*/ { const unsigned char *read_p = om_res; R_OBJECTMAP_ENTRY *object_map; R_CLICKAREA *clickarea; R_POINT *point; int i, k, m; YS_IGNORE_PARAM(om_res_len); if (!OMInfo.initialized) { R_printf(R_STDERR, "Error: Object map module not initialized!\n"); return R_FAILURE; } if (OMInfo.objects_loaded) { OBJECTMAP_Free(); } /* Obtain object count N and allocate space for N objects * \*------------------------------------------------------------- */ OMInfo.n_objects = ys_read_u16_le(read_p, &read_p); OMInfo.object_maps = (R_OBJECTMAP_ENTRY *)malloc(OMInfo.n_objects * sizeof *OMInfo.object_maps); if (OMInfo.object_maps == NULL) { R_printf(R_STDERR, "Error: Memory allocation failed.\n"); return R_MEM; } /* Load all N objects * \*------------------------------------------------------------- */ for (i = 0; i < OMInfo.n_objects; i++) { object_map = &OMInfo.object_maps[i]; object_map->unknown0 = ys_read_u8(read_p, &read_p); object_map->n_clickareas = ys_read_u8(read_p, &read_p); object_map->flags = ys_read_u16_le(read_p, &read_p); object_map->object_num = ys_read_u16_le(read_p, &read_p); object_map->script_num = ys_read_u16_le(read_p, &read_p); object_map->clickareas = (R_CLICKAREA *)malloc(object_map->n_clickareas * sizeof *(object_map->clickareas)); if (object_map->clickareas == NULL) { R_printf(R_STDERR, "Error: Memory allocation failed.\n"); return R_MEM; } /* Load all clickareas for this object */ for (k = 0; k < object_map->n_clickareas; k++) { clickarea = &object_map->clickareas[k]; clickarea->n_points = ys_read_u16_le(read_p, &read_p); assert(clickarea->n_points != 0); clickarea->points = (R_POINT *)malloc(clickarea->n_points * sizeof *(clickarea->points)); if (clickarea->points == NULL) { R_printf(R_STDERR, "Error: Memory allocation failed.\n"); return R_MEM; } /* Load all points for this clickarea */ for (m = 0; m < clickarea->n_points; m++) { point = &clickarea->points[m]; point->x = ys_read_s16_le(read_p, &read_p); point->y = ys_read_s16_le(read_p, &read_p); } # if R_OBJECTMAP_DEBUG >= R_DEBUG_PARANOID R_printf(R_STDOUT, "OBJECTMAP_Load(): " "Read %d points for clickarea %d in object %d.\n", clickarea->n_points, k, object_map->object_num); # endif } /* End load all clickareas */ } /* End load all objects */ /*-------------------------------------------------------------*/ OMInfo.objects_loaded = 1; return R_SUCCESS; } int OBJECTMAP_Free(void) /*--------------------------------------------------------------------------*\ * Frees all storage allocated for the current object map data \*--------------------------------------------------------------------------*/ { R_OBJECTMAP_ENTRY *object_map; R_CLICKAREA *clickarea; int i, k; if (!OMInfo.objects_loaded) { return R_FAILURE; } for (i = 0; i < OMInfo.n_objects; i++) { object_map = &OMInfo.object_maps[i]; for (k = 0; k < object_map->n_clickareas; k++) { clickarea = &object_map->clickareas[k]; free(clickarea->points); } free(object_map->clickareas); } if (OMInfo.n_objects) { free(OMInfo.object_maps); } OMInfo.objects_loaded = 0; return R_SUCCESS; } int OBJECTMAP_LoadNames(const unsigned char *onl_res, size_t onl_res_len) /*--------------------------------------------------------------------------*\ * Loads an object name list resource \*--------------------------------------------------------------------------*/ { const unsigned char *read_p = onl_res; int table_len; int n_names; size_t name_offset; int i; YS_IGNORE_PARAM(onl_res_len); if (OMInfo.names_loaded) { OBJECTMAP_FreeNames(); } table_len = ys_read_u16_le(read_p, &read_p); n_names = table_len / 2 - 2; OMInfo.n_names = n_names; #if 0 # if R_OBJECTMAP_DEBUG >= R_DEBUG_INFO R_printf(R_STDOUT, "OBJECTMAP_LoadNames: Loading %d object names.\n", n_names); # endif #endif OMInfo.names = (const char **)malloc(n_names * sizeof *OMInfo.names); if (OMInfo.names == NULL) { R_printf(R_STDERR, "Error: Memory allocation failed.\n"); return R_MEM; } for (i = 0; i < n_names; i++) { name_offset = ys_read_u16_le(read_p, &read_p); OMInfo.names[i] = (const char *)(onl_res + name_offset); # if R_OBJECTMAP_DEBUG >= R_DEBUG_VERBOSE R_printf(R_STDOUT, "Loaded object name string: %s\n", OMInfo.names[i]); # endif } OMInfo.names_loaded = 1; return R_SUCCESS; } int OBJECTMAP_FreeNames(void) /*--------------------------------------------------------------------------*\ * Frees all storage allocated for the current object name list data \*--------------------------------------------------------------------------*/ { if (!OMInfo.names_loaded) { return R_FAILURE; } if (OMInfo.n_names) { free(OMInfo.names); } OMInfo.names_loaded = 0; return R_SUCCESS; } int OBJECTMAP_GetName(int object, const char **name) /*--------------------------------------------------------------------------*\ * If 'object' is a valid object number in the currently loaded object * name list resource, the funciton sets '*name' to the descriptive string * corresponding to 'object' and returns R_SUCCESS. Otherwise it returns * R_FAILURE. \*--------------------------------------------------------------------------*/ { if (!OMInfo.names_loaded) { return R_FAILURE; } if ((object <= 0) || (object > OMInfo.n_names)) { return R_FAILURE; } *name = OMInfo.names[object - 1]; return R_SUCCESS; } int OBJECTMAP_GetFlags(int object, uint * flags) { int i; if (!OMInfo.names_loaded) { return R_FAILURE; } if ((object <= 0) || (object > OMInfo.n_names)) { return R_FAILURE; } for (i = 0; i < OMInfo.n_objects; i++) { if (OMInfo.object_maps[i].object_num == object) { *flags = OMInfo.object_maps[i].flags; return R_SUCCESS; } } return R_FAILURE; } int OBJECTMAP_GetEPNum(int object, int *ep_num) /*--------------------------------------------------------------------------*\ * If 'object' is a valid object number in the currently loaded object * name list resource, the funciton sets '*ep_num' to the entrypoint number * corresponding to 'object' and returns R_SUCCESS. Otherwise, it returns * R_FAILURE. \*--------------------------------------------------------------------------*/ { int i; if (!OMInfo.names_loaded) { return R_FAILURE; } if ((object < 0) || (object > (OMInfo.n_objects + 1))) { return R_FAILURE; } for (i = 0; i < OMInfo.n_objects; i++) { if (OMInfo.object_maps[i].object_num == object) { *ep_num = OMInfo.object_maps[i].script_num; return R_SUCCESS; } } return R_FAILURE; } int OBJECTMAP_Draw(R_SURFACE * ds, R_POINT * imouse_pt, int color, int color2) /*--------------------------------------------------------------------------*\ * Uses GFX_DrawLine to display all clickareas for each object in the * currently loaded object map resource. \*--------------------------------------------------------------------------*/ { R_OBJECTMAP_ENTRY *object_map; R_CLICKAREA *clickarea; char txt_buf[32]; int draw_color = color; int draw_txt = 0; int hit_object = 0; int object_num = 0; int pointcount = 0; int i, k; assert(OMInfo.initialized); if (!OMInfo.objects_loaded) { return R_FAILURE; } if (imouse_pt != NULL) { if (OBJECTMAP_HitTest(imouse_pt, &object_num) == R_SUCCESS) { hit_object = 1; } } for (i = 0; i < OMInfo.n_objects; i++) { draw_color = color; if (hit_object && (object_num == OMInfo.object_maps[i].object_num)) { snprintf(txt_buf, sizeof txt_buf, "obj %d: ? %d, f %X", OMInfo.object_maps[i].object_num, OMInfo.object_maps[i].unknown0, OMInfo.object_maps[i].flags); draw_txt = 1; draw_color = color2; } object_map = &OMInfo.object_maps[i]; for (k = 0; k < object_map->n_clickareas; k++) { clickarea = &object_map->clickareas[k]; pointcount = 0; if (clickarea->n_points == 2) { /* 2 points represent a box */ GFX_DrawFrame(ds, &clickarea->points[0], &clickarea->points[1], draw_color); } else if (clickarea->n_points > 2) { /* Otherwise draw a polyline */ GFX_DrawPolyLine(ds, clickarea->points, clickarea->n_points, draw_color); } } /* end for() clickareas */ } /* end for() objects */ if (draw_txt) { FONT_Draw(SMALL_FONT_ID, ds, txt_buf, 0, 2, 2, SYSGFX_GetWhite(), SYSGFX_GetBlack(), FONT_OUTLINE); } return R_SUCCESS; } static bool MATH_HitTestPoly(R_POINT * points, unsigned int npoints, R_POINT test_point) { int yflag0; int yflag1; bool inside_flag = false; unsigned int pt; R_POINT *vtx0 = &points[npoints - 1]; R_POINT *vtx1 = &points[0]; yflag0 = (vtx0->y >= test_point.y); for (pt = 0; pt < npoints; pt++, vtx1++) { yflag1 = (vtx1->y >= test_point.y); if (yflag0 != yflag1) { if (((vtx1->y - test_point.y) * (vtx0->x - vtx1->x) >= (vtx1->x - test_point.x) * (vtx0->y - vtx1->y)) == yflag1) { inside_flag = !inside_flag; } } yflag0 = yflag1; vtx0 = vtx1; } return inside_flag; } int OBJECTMAP_HitTest(R_POINT * imouse_pt, int *object_num) { R_POINT imouse; R_OBJECTMAP_ENTRY *object_map; R_CLICKAREA *clickarea; R_POINT *points; int n_points; int i, k; assert((imouse_pt != NULL) && (object_num != NULL)); imouse.x = imouse_pt->x; imouse.y = imouse_pt->y; /* Loop through all scene objects */ for (i = 0; i < OMInfo.n_objects; i++) { object_map = &OMInfo.object_maps[i]; /* Hit-test all clickareas for this object */ for (k = 0; k < object_map->n_clickareas; k++) { clickarea = &object_map->clickareas[k]; n_points = clickarea->n_points; points = clickarea->points; if (n_points == 2) { /* Hit-test a box region */ if ((imouse.x > points[0].x) && (imouse.x <= points[1].x) && (imouse.y > points[0].y) && (imouse.y <= points[1].y)) { *object_num = object_map->object_num; return R_SUCCESS; } } else if (n_points > 2) { /* Hit-test a polygon */ if (MATH_HitTestPoly(points, n_points, imouse)) { *object_num = object_map->object_num; return R_SUCCESS; } } } /* end for() clickareas */ } /* end for() objects */ *object_num = 0; return R_FAILURE; } static void CF_object_info(int argc, char *argv[]) { int i; YS_IGNORE_PARAM(argc); YS_IGNORE_PARAM(argv); if (!OMInfo.initialized) { return; } CON_Print("%d objects loaded.", OMInfo.n_objects); for (i = 0; i < OMInfo.n_objects; i++) { CON_Print("%s:", OMInfo.names[i]); CON_Print ("%d. Unk1: %d, flags: %X, name_i: %d, scr_n: %d, ca_ct: %d", i, OMInfo.object_maps[i].unknown0, OMInfo.object_maps[i].flags, OMInfo.object_maps[i].object_num, OMInfo.object_maps[i].script_num, OMInfo.object_maps[i].n_clickareas); } return; } } // End of namespace Saga