1 /*  GNU Robbo
2  *  Copyright (C) 2002-2010 The GNU Robbo Team (see AUTHORS).
3  *
4  *  GNU Robbo is free software - you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2, or (at your option)
7  *  any later version.
8  *
9  *  GNU Robbo is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the impled warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with GNU CC; see the file COPYING. If not, write to the
16  *  Free Software Foundation, 59 Temple Place - Suite 330,
17  *  Boston, MA 02111-1307, USA.
18  *
19  */
20 
21 #include "game.h"
22 
23 /* The GNU Robbo Rectangular Object Engine
24    =======================================
25 
26    The purpose of this engine is to manage a GUI and widget set
27    capable of assisting in the development of a level designer.
28    It's a cut down version of ENG, a project I've been working on
29    and only recently (2009-03-22) published on Sourceforge.
30 
31    The main differences between this and ENG are :-
32    * No objects will be loaded from a file, only created in code
33    * There is no image subsystem although ROB_FreeObject will free attached images
34    * There is no actions subsystem as it already exists within GNU Robbo in a simpler form
35    * No screen or viewport system objects, just a screen sized canvas
36    * The sorted id array is dispensed with because there won't be many objects
37    * Only GNU Robbo developers will be creating objects, not users
38    * Much of the idiot-proof error checking is no longer necessary :)
39 
40 */
41 
42 /* Defines */
43 /*
44 #define DEBUG_ROB_OBJECTS
45 #define DEBUG_ROB_OBJECT_ZORDER
46 #define DEBUG_ROB_OBJECTS_ALL_DRAGGABLE
47 #define DEBUG_ROB_GET_VISIBILITY
48 #define DEBUG_ROB_GET_ENABILITY
49 #define DEBUG_ROB_EVENTS
50 #define DEBUG_ROB_PROGRESS_DUMP
51 */
52 
53 #define ROB_MAX_OBJECTS 1000
54 #define ROB_FILE_LENGTH 256
55 #define ROB_ERROR_LENGTH 256
56 
57 #define ROB_DIR "rob"
58 #define ROB_SPRITES "robsprites.bmp"
59 
60 #define ROB_SPRITES_POINTER_XOFFSET 0
61 #define ROB_SPRITES_POINTER_W 13
62 #define ROB_SPRITES_POINTER_H 19
63 #define ROB_SPRITES_POINTER_SHADOW_XOFFSET 13
64 #define ROB_SPRITES_POINTER_SHADOW_ALPHA 50
65 #define ROB_SPRITES_POINTER_SHADOW_X 4
66 #define ROB_SPRITES_POINTER_SHADOW_Y 4
67 #define ROB_SPRITES_POINTER_SHADOW_W 13
68 #define ROB_SPRITES_POINTER_SHADOW_H 19
69 
70 /* Variables */
71 ROB_OpEnv rob_op_env;
72 SDL_Surface *rob_blend_screen = NULL;
73 SDL_Surface *rob_sprites = NULL;
74 ROB_Object *rob_objects[ROB_MAX_OBJECTS];
75 ROB_Object *rob_lyr_root = NULL;
76 ROB_Object *rob_lyr_canvas = NULL;
77 ROB_Object *rob_rec_pointer = NULL;
78 ROB_Object *rob_oup = NULL;
79 ROB_Object *rob_pressed_object = NULL;
80 int rob_object_count = 0;
81 char rob_last_error[ROB_ERROR_LENGTH];
82 
83 /* Function prototypes */
84 int ROB_SetObjectZOrder(ROB_Object *rob_object, int position, ROB_Object *target);
85 void ROB_FreeAllObjects(void);
86 int ROB_FillRectA(SDL_Surface *surface, SDL_Rect *dstrect, Uint32 colour, Uint8 alpha);
87 int ROB_GetCanvasOffset(ROB_Object *rob_object, int *cx, int *cy);
88 int ROB_GetVisibility(ROB_Object *rob_object);
89 int ROB_GetEnability(ROB_Object *rob_object);
90 int ROB_GetAlpha(ROB_Object *rob_object);
91 ROB_Object *ROB_GetObjectUnderObject(ROB_Object *rob_object, int hitpoint);
92 int ROB_FindAncestor(ROB_Object *rob_object, ROB_Object *target);
93 void ROB_ManagePointerInput(int pxu, int pyu);
94 int ROB_SendEvent(ROB_Event *rob_event);
95 
96 
97 /***************************************************************************
98  * Initialise                                                              *
99  ***************************************************************************/
100 /* On entry: op_env points to the operating environment data which is copied locally */
101 
ROB_Init(ROB_OpEnv * op_env)102 void ROB_Init(ROB_OpEnv *op_env) {
103 	SDL_Surface *unconverted, *converted;
104 	ROB_Object rob_object;
105 	char filename[256];
106 
107 	#ifdef DEBUG_ROB_PROGRESS_DUMP
108 		printf("%s: Initialising\n", __func__);
109 	#endif
110 
111 	/* Create a local copy of the passed op_env */
112 	rob_op_env = *op_env;
113 
114 	/* Initialise some defaults */
115 	rob_lyr_pointer = NULL;
116 	rob_last_error[0] = 0;
117 
118 	/* Duplicate the screen surface for making alpha rectangles only */
119 	rob_blend_screen = SDL_CreateRGBSurface(screen->flags | SDL_SRCALPHA, screen->w, screen->h, screen->format->BitsPerPixel, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
120 	if (rob_blend_screen == NULL) {
121 		fprintf(stdout, "%s: Cannot create blend surface: %s", __func__, SDL_GetError());
122 		exit(1);
123 	}
124 
125 	/* Load the ROB system images */
126 	strcpy(filename, PACKAGE_DATA_DIR "/" ROB_DIR "/");
127 	strcat(filename, ROB_SPRITES);
128 	/* Load the bitmap */
129 	if ((unconverted = SDL_LoadBMP(filename)) == NULL) {
130 		fprintf(stdout, "Cannot load bitmap: %s\n", filename);
131 		exit(1);
132 	}
133 	/* Set the transparent colour */
134 	if (SDL_SetColorKey(unconverted, SDL_SRCCOLORKEY, SDL_MapRGB(unconverted->format, 0xff, 0x00, 0xff)) < 0) {
135 		fprintf(stdout, "Cannot set surface colour key: %s\n", SDL_GetError());
136 		exit(1);
137 	}
138 	/* Convert the loaded surface to the same pixel format as the screen for fast blitting */
139 	converted = SDL_DisplayFormat(unconverted);
140 	SDL_FreeSurface(unconverted);
141 	if (converted == NULL) {
142 		fprintf(stdout, "Cannot create surface: %s\n", SDL_GetError());
143 		exit(1);
144 	}
145 	rob_sprites = converted;
146 
147 	/* Create the system objects */
148 	/* Create a root object */
149 	if (ROB_SetObjectDefaults(&rob_object)) printf("%s: %s\n", __func__, ROB_GetError());
150 	rob_object.id = ROB_LYR_ROOT ;
151 	rob_object.pid = ROB_LYR_ROOT;
152 	rob_object.visible = TRUE;
153 	rob_object.enabled = TRUE;
154 	rob_object.alpha = 255;
155 	if (ROB_CreateObject(&rob_object)) printf("%s: %s\n", __func__, ROB_GetError());
156 	/* Create a canvas object */
157 	if (ROB_SetObjectDefaults(&rob_object)) printf("%s: %s\n", __func__, ROB_GetError());
158 	rob_object.id = ROB_LYR_CANVAS;
159 	rob_object.pid = ROB_LYR_ROOT;
160 	rob_object.visible = TRUE;
161 	rob_object.enabled = TRUE;
162 	rob_object.alpha = 255;
163 	rob_object.w = screen->w;
164 	rob_object.h = screen->h;
165 	if (ROB_CreateObject(&rob_object)) printf("%s: %s\n", __func__, ROB_GetError());
166 	/* Create a pointer shadow */
167 	if (ROB_SetObjectDefaults(&rob_object)) printf("%s: %s\n", __func__, ROB_GetError());
168 	rob_object.id = ROB_REC_POINTER;
169 	rob_object.pid = ROB_LYR_POINTER;
170 	rob_object.alpha = ROB_SPRITES_POINTER_SHADOW_ALPHA;
171 	rob_object.x = ROB_SPRITES_POINTER_SHADOW_X;
172 	rob_object.y = ROB_SPRITES_POINTER_SHADOW_Y;
173 	rob_object.w = ROB_SPRITES_POINTER_SHADOW_W;
174 	rob_object.h = ROB_SPRITES_POINTER_SHADOW_H;
175 	rob_object.img_surface = rob_sprites;
176 	rob_object.img_x = ROB_SPRITES_POINTER_SHADOW_XOFFSET;
177 	rob_object.img_y = 0;
178 	rob_object.img_w = rob_object.w;
179 	rob_object.img_h = rob_object.h;
180 	if (ROB_CreateObject(&rob_object)) printf("%s: %s\n", __func__, ROB_GetError());
181 	/* Create a pointer */
182 	if (ROB_SetObjectDefaults(&rob_object)) printf("%s: %s\n", __func__, ROB_GetError());
183 	rob_object.id = ROB_LYR_POINTER;
184 	rob_object.pid = ROB_LYR_CANVAS;
185 	rob_object.visible = FALSE;
186 	rob_object.enabled = TRUE;
187 	rob_object.alpha = 255;
188 	rob_object.x = screen->w / 2;
189 	rob_object.y = screen->h / 2;
190 	rob_object.w = ROB_SPRITES_POINTER_W;
191 	rob_object.h = ROB_SPRITES_POINTER_H;
192 	rob_object.img_surface = rob_sprites;
193 	rob_object.img_x = ROB_SPRITES_POINTER_XOFFSET;
194 	rob_object.img_y = 0;
195 	rob_object.img_w = rob_object.w;
196 	rob_object.img_h = rob_object.h;
197 	if (ROB_CreateObject(&rob_object)) printf("%s: %s\n", __func__, ROB_GetError());
198 	#ifdef DEBUG_ROB_OBJECTS
199 		printf("%s: rob_lyr_root=%p\n", __func__, rob_lyr_root);
200 		printf("%s: rob_lyr_canvas=%p\n", __func__, rob_lyr_canvas);
201 		printf("%s: rob_rec_pointer=%p\n", __func__, rob_rec_pointer);
202 		printf("%s: rob_lyr_pointer=%p\n", __func__, rob_lyr_pointer);
203 	#endif
204 
205 	/* Process the operating environment */
206 	ROB_SetOpEnvEventProcessor(rob_op_env.event_processor);
207 	ROB_SetOpEnvSystemPointer(rob_op_env.systempointer);
208 	ROB_SetOpEnvPointer(rob_op_env.pointer, rob_op_env.pointer_mode);
209 }
210 
211 /***************************************************************************
212  * Set Object ZOrder                                                       *
213  ***************************************************************************/
214 /* User objects must be above ROB_LYR_CANVAS and below ROB_REC_POINTER to
215    be accessible via the pointer. This function does not place any restrictions
216    on the destinaton of any objects.
217 
218    Positions :-
219 
220    ROB_ZORDER_ABOVE_ALL
221    --------------------
222    The object will be moved above the target and above all other objects who's
223    pob is also the target.
224    If the object is or results in being the target then it is not moved.
225 
226    ROB_ZORDER_ABOVE
227    ----------------
228    The object will be moved above the target.
229    If the object is the target then it is moved above itself.
230 
231    ROB_ZORDER_BELOW
232    ----------------
233    The object will be moved below the target.
234    If the object is the target then it is moved below itself */
235 
236 /* On entry: object is the object to move
237 			 position is one of the zorder positions detailed above
238 			 target is the target object of the move
239 	On exit: returns ROB_ERROR_NONE on success else
240 			 any one of the error codes in ROB_engine.h */
241 
ROB_SetObjectZOrder(ROB_Object * rob_object,int position,ROB_Object * target)242 int ROB_SetObjectZOrder(ROB_Object *rob_object, int position, ROB_Object *target) {
243 	int count, retval = ROB_ERROR_NONE;
244 	int index = rob_object->zorder, targetindex = ROB_UNDEFINED, direction = 0;
245 	ROB_Object *swap_object;
246 
247 	if (rob_object == NULL || sizeof(*rob_object) != sizeof(ROB_Object)) {
248 		sprintf(rob_last_error, "%s: object is invalid", __func__);
249 		retval = ROB_ERROR_INVALID_OBJECT;
250 	} else if (target == NULL || sizeof(*target) != sizeof(ROB_Object)) {
251 		sprintf(rob_last_error, "%s: target is invalid", __func__);
252 		retval = ROB_ERROR_INVALID_TARGET_OBJECT;
253 	} else {
254 		/* Set the target index */
255 		if (position == ROB_ZORDER_ABOVE_ALL) {
256 			/* Find the topmost object equal to target or its pob is equal to target */
257 			for (count = 0; count < rob_object_count; count++) {
258 				if (rob_objects[count] == target || rob_objects[count]->pob == target) targetindex = count;
259 			}
260 			/* Make some positional adjustments to the target index */
261 			if (targetindex == ROB_UNDEFINED) {
262 				targetindex = rob_object->zorder;	/* No seg faults please :) */
263 			} else if (rob_object->zorder > targetindex) {
264 				targetindex++;
265 			}
266 		} else if (position == ROB_ZORDER_ABOVE) {
267 			/* The target index is the zorder within the target */
268 			targetindex = target->zorder;
269 			/* Make some positional adjustments to the target index */
270 			if (rob_object->zorder == targetindex && targetindex < rob_object_count - 1) {
271 				targetindex++;
272 			} else if (rob_object->zorder > targetindex) {
273 				targetindex++;
274 			}
275 		} else if (position == ROB_ZORDER_BELOW) {
276 			/* The target index is the zorder within the target */
277 			targetindex = target->zorder;
278 			/* Make some positional adjustments to the target index */
279 			if (rob_object->zorder == targetindex && targetindex > 0) {
280 				targetindex--;
281 			} else if (rob_object->zorder < targetindex) {
282 				targetindex--;
283 			}
284 		}
285 		/* Set move direction */
286 		if (index < targetindex) {
287 			direction = 1;
288 		} else if (rob_object->zorder > targetindex) {
289 			direction = -1;
290 		}
291 		#ifdef DEBUG_ROB_OBJECT_ZORDER
292 			printf("%s: Moving id=%i zorder=%i targetindex=%i (rob_object_count=%i)\n", __func__, rob_object->id, rob_object->zorder, targetindex, rob_object_count);
293 		#endif
294 		while (index != targetindex) {
295 			#ifdef DEBUG_ROB_OBJECT_ZORDER
296 				printf("    id=%i pob=%p zorder=%i <--> id=%i pob=%p zorder=%i\n", rob_objects[index]->id, rob_objects[index]->pob, rob_objects[index]->zorder, rob_objects[index + direction]->id, rob_objects[index + direction]->pob, rob_objects[index + direction]->zorder);
297 			#endif
298 			swap_object = rob_objects[index];
299 			rob_objects[index] = rob_objects[index + direction];
300 			rob_objects[index]->zorder -= direction;
301 			index += direction;
302 			rob_objects[index] = swap_object;
303 			rob_objects[index]->zorder += direction;
304 		}
305 	}
306 
307 	return retval;
308 }
309 
310 /***************************************************************************
311  * Get Object                                                              *
312  ***************************************************************************/
313 /* On entry: id is the object id
314 	On exit: returns a pointer to the object on success else NULL */
315 
ROB_GetObject(int id)316 ROB_Object *ROB_GetObject(int id) {
317 	int count;
318 
319 	/* Linear search for id */
320 	for (count = 0; count < rob_object_count; count++) {
321 		if (rob_objects[count]->id == id) return rob_objects[count];
322 	}
323 
324 	return NULL;
325 }
326 
327 /***************************************************************************
328  * Create Object                                                           *
329  ***************************************************************************/
330 /* This will create the object.
331    There may be existing objects within rob_objects whose pob is NULL because
332    their parent was recently freed or hasn't been created yet. This function
333    will attempt to fix these NULL pobs on introduction of this new object */
334 
335 /*  On exit: returns ROB_ERROR_NONE on success else
336 			 any one of the error codes in ROB_engine.h */
337 
ROB_CreateObject(ROB_Object * rob_object)338 int ROB_CreateObject(ROB_Object *rob_object) {
339 	int retval = ROB_ERROR_NONE, count;
340 	ROB_Object *newobject;
341 
342 	if (rob_object == NULL || sizeof(*rob_object) != sizeof(ROB_Object)) {
343 		sprintf(rob_last_error, "%s: object is invalid", __func__);
344 		retval = ROB_ERROR_INVALID_OBJECT;
345 	} else if (rob_object_count >= ROB_MAX_OBJECTS) {
346 		sprintf(rob_last_error, "%s: Too many objects (max %i)", __func__, ROB_MAX_OBJECTS);
347 		retval = ROB_ERROR_TOO_MANY_OBJECTS;
348 	} else {
349 		/* Check uniqueness of id */
350 		for (count = 0; count < rob_object_count; count++) {
351 			if (rob_object->id == rob_objects[count]->id) {
352 				sprintf(rob_last_error, "%s: Invalid duplicate id property", __func__);
353 				retval = ROB_ERROR_INVALID_OBJECT_ID;
354 				break;
355 			}
356 		}
357 		if (!retval) {
358 			/* Allocate memory for the new object */
359 			newobject = (ROB_Object*) malloc(sizeof(ROB_Object));
360 			*newobject = *rob_object;
361 			#ifdef DEBUG_ROB_OBJECTS_ALL_DRAGGABLE
362 				if (newobject->draggable == FALSE) newobject->draggable = ROB_DRAG_ANYWHERE;
363 			#endif
364 			#ifdef DEBUG_ROB_OBJECTS
365 				printf("*** Start %s ***\n", __func__);
366 				printf("%s: Creating %p (%i)\n", __func__, newobject, newobject->id);
367 			#endif
368 			/* Store the object pointer in the array */
369 			rob_objects[rob_object_count] = newobject;
370 			/* Set the zorder */
371 			newobject->zorder = rob_object_count;
372 			/* Update the array element count now */
373 			rob_object_count++;
374 			/* Store pointers to system objects */
375 			if (newobject->id == ROB_LYR_ROOT) {
376 				rob_lyr_root = newobject;
377 			} else if (newobject->id == ROB_LYR_CANVAS) {
378 				rob_lyr_canvas = newobject;
379 			} else if (newobject->id == ROB_REC_POINTER) {
380 				rob_rec_pointer = newobject;
381 			} else if (newobject->id == ROB_LYR_POINTER) {
382 				rob_lyr_pointer = newobject;
383 			}
384 			/* Iterate through rob_objects and fix the pob */
385 			for (count = 0; count < rob_object_count; count++) {
386 				if (rob_objects[count]->pob == NULL) {
387 					/* This may still result in a NULL pob if the parent never exists */
388 					rob_objects[count]->pob = ROB_GetObject(rob_objects[count]->pid);
389 				}
390 			}
391 			/* Maintain the highest zorder for the pointer and shadow */
392 			if (rob_lyr_pointer != NULL && rob_rec_pointer != NULL) {
393 				ROB_SetObjectZOrder(rob_lyr_pointer, ROB_ZORDER_ABOVE, newobject);
394 				ROB_SetObjectZOrder(rob_rec_pointer, ROB_ZORDER_BELOW, rob_lyr_pointer);
395 			}
396 			#ifdef DEBUG_ROB_OBJECTS
397 				printf("newobject :-\n");
398 				printf("id=%i\n", newobject->id);
399 				printf("pid=%i\n", newobject->pid);
400 				printf("visible=%i\n", newobject->visible);
401 				printf("enabled=%i\n", newobject->enabled);
402 				printf("alpha=%i\n", newobject->alpha);
403 				printf("alpha_over=%i\n", newobject->alpha_over);
404 				printf("alpha_press=%i\n", newobject->alpha_press);
405 				printf("bgcolour=%03X\n", newobject->bgcolour);
406 				printf("bgcolour_over=%03X\n", newobject->bgcolour_over);
407 				printf("bgcolour_press=%03X\n", newobject->bgcolour_press);
408 				printf("x=%i\n", newobject->x);
409 				printf("y=%i\n", newobject->y);
410 				printf("w=%i\n", newobject->w);
411 				printf("h=%i\n", newobject->h);
412 				printf("img_surface=%p\n", newobject->img_surface);
413 				printf("img_x=%i\n", newobject->img_x);
414 				printf("img_y=%i\n", newobject->img_y);
415 				printf("img_w=%i\n", newobject->img_w);
416 				printf("img_h=%i\n", newobject->img_h);
417 				printf("img_over_surface=%p\n", newobject->img_over_surface);
418 				printf("img_over_x=%i\n", newobject->img_over_x);
419 				printf("img_over_y=%i\n", newobject->img_over_y);
420 				printf("img_over_w=%i\n", newobject->img_over_w);
421 				printf("img_over_h=%i\n", newobject->img_over_h);
422 				printf("img_press_surface=%p\n", newobject->img_press_surface);
423 				printf("img_press_x=%i\n", newobject->img_press_x);
424 				printf("img_press_y=%i\n", newobject->img_press_y);
425 				printf("img_press_w=%i\n", newobject->img_press_w);
426 				printf("img_press_h=%i\n", newobject->img_press_h);
427 				printf("draggable=%i\n", newobject->draggable);
428 				printf("drag_point=%i\n", newobject->drag_point);
429 				printf("drag_granularity=%i\n", newobject->drag_granularity);
430 				printf("zorder=%i\n", newobject->zorder);
431 				printf("pob=%p\n", newobject->pob);
432 				printf("*** Stop %s ***\n", __func__);
433 			#endif
434 		}
435 	}
436 
437 	return retval;
438 }
439 
440 /***************************************************************************
441  * Set Object Defaults                                                     *
442  ***************************************************************************/
443 /*  On exit: returns ROB_ERROR_NONE on success else
444 			 any one of the error codes in ROB_engine.h */
445 
ROB_SetObjectDefaults(ROB_Object * rob_object)446 int ROB_SetObjectDefaults(ROB_Object *rob_object) {
447 	int retval = ROB_ERROR_NONE;
448 
449 	if (rob_object == NULL || sizeof(*rob_object) != sizeof(ROB_Object)) {
450 		sprintf(rob_last_error, "%s: object is invalid", __func__);
451 		retval = ROB_ERROR_INVALID_OBJECT;
452 	} else {
453 		rob_object->id = ROB_UNDEFINED;
454 		rob_object->pid = ROB_LYR_CANVAS;
455 		rob_object->visible = ROB_INHERITED;
456 		rob_object->enabled = ROB_INHERITED;
457 		rob_object->alpha = ROB_INHERITED;
458 		rob_object->alpha_over = ROB_UNDEFINED;
459 		rob_object->alpha_press = ROB_UNDEFINED;
460 		rob_object->bgcolour = ROB_UNDEFINED;
461 		rob_object->bgcolour_over = ROB_UNDEFINED;
462 		rob_object->bgcolour_press = ROB_UNDEFINED;
463 		rob_object->x = 0;
464 		rob_object->y = 0;
465 		rob_object->w = 0;
466 		rob_object->h = 0;
467 		rob_object->img_surface = NULL;
468 		rob_object->img_x = ROB_UNDEFINED;
469 		rob_object->img_y = ROB_UNDEFINED;
470 		rob_object->img_w = ROB_UNDEFINED;
471 		rob_object->img_h = ROB_UNDEFINED;
472 		rob_object->img_over_surface = NULL;
473 		rob_object->img_over_x = ROB_UNDEFINED;
474 		rob_object->img_over_y = ROB_UNDEFINED;
475 		rob_object->img_over_w = ROB_UNDEFINED;
476 		rob_object->img_over_h = ROB_UNDEFINED;
477 		rob_object->img_press_surface = NULL;
478 		rob_object->img_press_x = ROB_UNDEFINED;
479 		rob_object->img_press_y = ROB_UNDEFINED;
480 		rob_object->img_press_w = ROB_UNDEFINED;
481 		rob_object->img_press_h = ROB_UNDEFINED;
482 		rob_object->draggable = FALSE;
483 		rob_object->drag_point = ROB_DRAG_POINT_ANYWHERE;
484 		rob_object->drag_granularity = 1;
485 		rob_object->zorder = ROB_UNDEFINED;
486 		rob_object->pob = NULL;
487 	}
488 
489 	return retval;
490 }
491 
492 /***************************************************************************
493  * Get Error                                                               *
494  ***************************************************************************/
495 /*  On exit: returns a pointer to a string containing the last internal
496 			 error plus the contents of SDL_GetError if relevant */
497 
ROB_GetError(void)498 char *ROB_GetError(void) {
499 	static char last_error[ROB_ERROR_LENGTH];
500 
501 	strcpy (last_error, rob_last_error);
502 	strcpy (rob_last_error, "");
503 
504 	return last_error;
505 }
506 
507 /***************************************************************************
508  * Quit (ROB)                                                              *
509  ***************************************************************************/
510 /*  On exit: returns ROB_ERROR_NONE on success else
511 			 any one of the error codes in ROB_engine.h */
512 
ROB_Quit(void)513 int ROB_Quit(void) {
514 	int retval = ROB_ERROR_NONE;
515 
516 	#ifdef DEBUG_ROB_PROGRESS_DUMP
517 		printf("%s: Quitting\n", __func__);
518 	#endif
519 	ROB_FreeAllObjects();
520 	if (rob_sprites) SDL_FreeSurface(rob_sprites);
521 	if (rob_blend_screen) SDL_FreeSurface(rob_blend_screen);
522 
523 	return retval;
524 }
525 
526 /***************************************************************************
527  * Free All Objects                                                        *
528  ***************************************************************************/
529 /* This mops-up any remaining objects before shutting down */
530 
ROB_FreeAllObjects(void)531 void ROB_FreeAllObjects(void) {
532 	int count;
533 
534 	if (rob_object_count > 0) {
535 		#ifdef DEBUG_ROB_PROGRESS_DUMP
536 			printf("%s: Freeing %i objects\n", __func__, rob_object_count);
537 		#endif
538 		/* Free all remaining objects (ignoring errors) */
539 		for (count = rob_object_count - 1; count >= 0; count--) {
540 			ROB_FreeObject(rob_objects[count], FALSE);
541 		}
542 	}
543 }
544 
545 /***************************************************************************
546  * Free All Child Objects                                                  *
547  ***************************************************************************/
548 /* This frees all child objects of a parent if any exist. This can be viewed
549    as a way of freeing a group of objects that share a common parent */
550 
551 /* On entry: pob is a pointer to a parent object
552 			 free_pob = TRUE to free the parent object also */
553 /*  On exit: returns ROB_ERROR_NONE on success else
554 			 any one of the error codes in ROB_engine.h */
555 
ROB_FreeAllChildObjects(ROB_Object * pob,int free_pob)556 int ROB_FreeAllChildObjects(ROB_Object *pob, int free_pob) {
557 	int retval = ROB_ERROR_NONE, count, free_count = 0;
558 
559 	if (pob == NULL || sizeof(*pob) != sizeof(ROB_Object)) {
560 		sprintf(rob_last_error, "%s: pob is invalid", __func__);
561 		retval = ROB_ERROR_INVALID_OBJECT;
562 	} else if (rob_object_count > 0) {
563 		for (count = rob_object_count - 1; count >= 0; count--) {
564 			if (rob_objects[count]->pob == pob) {
565 				ROB_FreeObject(rob_objects[count], FALSE);
566 				free_count++;
567 			}
568 		}
569 		#ifdef DEBUG_ROB_PROGRESS_DUMP
570 			printf("%s: Freeing %i objects", __func__, free_count);
571 			if (free_pob) printf(" and parent id=%i", pob->id);
572 			printf("\n");
573 		#endif
574 		if (free_pob) {
575 			ROB_FreeObject(pob, FALSE);
576 		}
577 	}
578 
579 	return retval;
580 }
581 
582 /***************************************************************************
583  * Free All Descendant Objects                                             *
584  ***************************************************************************/
585 /* This frees all descendant objects of an ancestor if any exist. This can be
586    viewed as a way of freeing a group of objects that share a common ancestor */
587 
588 /* On entry: ancestor is a pointer to an ancestor object
589 			 free_ancestor = TRUE to free the ancestor object also */
590 /*  On exit: returns ROB_ERROR_NONE on success else
591 			 any one of the error codes in ROB_engine.h */
592 
ROB_FreeAllDescendantObjects(ROB_Object * ancestor,int free_ancestor)593 int ROB_FreeAllDescendantObjects(ROB_Object *ancestor, int free_ancestor) {
594 	int retval = ROB_ERROR_NONE, count, free_count = 0;
595 
596 	if (ancestor == NULL || sizeof(*ancestor) != sizeof(ROB_Object)) {
597 		sprintf(rob_last_error, "%s: ancestor is invalid", __func__);
598 		retval = ROB_ERROR_INVALID_OBJECT;
599 	} else if (rob_object_count > 0) {
600 		for (count = rob_object_count - 1; count >= 0; count--) {
601 			if (rob_objects[count] != ancestor && ROB_FindAncestor(rob_objects[count], ancestor)) {
602 				ROB_FreeObject(rob_objects[count], FALSE);
603 				free_count++;
604 			}
605 		}
606 		#ifdef DEBUG_ROB_PROGRESS_DUMP
607 			printf("%s: Freeing %i objects", __func__, free_count);
608 			if (free_ancestor) printf(" and ancestor id=%i", ancestor->id);
609 			printf("\n");
610 		#endif
611 		if (free_ancestor) {
612 			ROB_FreeObject(ancestor, FALSE);
613 		}
614 	}
615 
616 	return retval;
617 }
618 
619 /***************************************************************************
620  * Free Object                                                             *
621  ***************************************************************************/
622 /* When freeing a parent object, the child objects will have their pob
623    nullified up until the point when (or if) the parent is reinstated.
624    Images declared within the object are freed and any objects that are
625    also referencing these images through img_surface and img_over_surface
626    will have these properties nullified */
627 
628 /* On entry: object is the object to free
629 			 free_images_only = TRUE to free ONLY any declared images */
630 /*  On exit: returns ROB_ERROR_NONE on success else
631 			 any one of the error codes in ROB_engine.h */
632 
ROB_FreeObject(ROB_Object * rob_object,int free_images_only)633 int ROB_FreeObject(ROB_Object *rob_object, int free_images_only) {
634 	int retval = ROB_ERROR_NONE, count, count2;
635 	SDL_Surface *img_surface;
636 
637 	if (rob_object_count <= 0) {
638 		sprintf(rob_last_error, "%s: There is no object to free", __func__);
639 		retval = ROB_ERROR_INVALID_OBJECT;
640 	} else if (rob_object == NULL || sizeof(*rob_object) != sizeof(ROB_Object)) {
641 		sprintf(rob_last_error, "%s: object is invalid", __func__);
642 		retval = ROB_ERROR_INVALID_OBJECT;
643 	} else {
644 		#ifdef DEBUG_ROB_OBJECTS
645 			printf("%s: Freeing %p (%i)\n", __func__, rob_object, rob_object->id);
646 		#endif
647 		/* Free img_surface and img_over_surface if either are declared */
648 		for (count = 0; count < 2; count++) {
649 			if (count == 0) {
650 				img_surface = rob_object->img_surface;
651 			} else {
652 				img_surface = rob_object->img_over_surface;
653 			}
654 			if (img_surface != NULL) {
655 				/* Nullify all surface pointers within rob_objects that reference this surface */
656 				for (count2 = 0; count2 < rob_object_count; count2++) {
657 					if (rob_objects[count2]->img_surface == img_surface) {
658 						rob_objects[count2]->img_surface = NULL;
659 					}
660 					if (rob_objects[count2]->img_over_surface == img_surface) {
661 						rob_objects[count2]->img_over_surface = NULL;
662 					}
663 				}
664 				/* Free the SDL surface but not rob_sprites or GNU Robbo images */
665 				if (img_surface != rob_sprites &&
666 					img_surface != wm_icon &&
667 					img_surface != icons &&
668 					img_surface != ciphers &&
669 					img_surface != alpha &&
670 					img_surface != bgrnd &&
671 					img_surface != k_icons) {
672 					SDL_FreeSurface(img_surface);
673 				}
674 			}
675 		}
676 		if (!free_images_only) {
677 			/* If the current object is the parent to other objects then nullify their pob */
678 			for (count = 0; count < rob_object_count; count++) {
679 				if (rob_objects[count]->pob == rob_object) rob_objects[count]->pob = NULL;
680 			}
681 			/* If anything is above the current object then move it down the array */
682 			for (count = rob_object->zorder; count < rob_object_count - 1; count++) {
683 				rob_objects[count] = rob_objects[count + 1];
684 				rob_objects[count]->zorder--;
685 			}
686 			/* Nullify rob_oup if it's the same object */
687 			if (rob_oup == rob_object) rob_oup = NULL;
688 
689 			/* Nullify rob_pressed_object if it's the same object */
690 			if (rob_pressed_object == rob_object) rob_pressed_object = NULL;
691 
692 			/* Free the memory and update the array element count now */
693 			free(rob_object);
694 			rob_object_count--;
695 		}
696 	}
697 
698 	return retval;
699 }
700 
701 /***************************************************************************
702  * Render Objects                                                          *
703  ***************************************************************************/
704 /*  On exit: returns ROB_ERROR_NONE on success else
705 			 any one of the error codes in ROB_engine.h */
706 
ROB_RenderObjects(void)707 int ROB_RenderObjects(void) {
708 	int cx, cy, ix = 0, iy = 0, iw = 0, ih = 0, alpha;
709 	int overeventfound, presseventfound, use_rleaccel = 0;
710 	int count, retval = ROB_ERROR_NONE;
711 	SDL_Surface *img_surface = NULL;
712 	SDL_Rect srcrect, dstrect;
713 	Uint32 bgcolour = 0;
714 
715 	/* Iterate through the objects */
716 	for (count = 0; count < rob_object_count; count++) {
717 		/* Get the object's visibility */
718 		if (ROB_GetVisibility(rob_objects[count])) {
719 
720 			/* Is there an over event waiting for this object? */
721 			overeventfound = presseventfound = FALSE;
722 			if (rob_objects[count] == rob_oup && ROB_GetEnability(rob_objects[count])) {
723 				overeventfound = TRUE;
724 				/* Is there a press event waiting for this object? */
725 				if (rob_objects[count] == rob_pressed_object) presseventfound = TRUE;
726 			}
727 
728 			/* Get object's canvas offset */
729 			ROB_GetCanvasOffset(rob_objects[count], &cx, &cy);
730 
731 			/* Get object's alpha and alpha_{over|press} */
732 			alpha = ROB_GetAlpha(rob_objects[count]);
733 			if (overeventfound && rob_objects[count]->alpha_over != ROB_UNDEFINED)
734 				alpha = rob_objects[count]->alpha_over;
735 			if (presseventfound && rob_objects[count]->alpha_press != ROB_UNDEFINED)
736 				alpha = rob_objects[count]->alpha_press;
737 
738 			/* Get object's bgcolour and bgcolour_{over|press} */
739 			bgcolour = rob_objects[count]->bgcolour;
740 			if (overeventfound && rob_objects[count]->bgcolour_over != ROB_UNDEFINED)
741 				bgcolour = rob_objects[count]->bgcolour_over;
742 			if (presseventfound && rob_objects[count]->bgcolour_press != ROB_UNDEFINED)
743 				bgcolour = rob_objects[count]->bgcolour_press;
744 
745 			/* Get object's img_{over_|press_}surface */
746 			img_surface = rob_objects[count]->img_surface;
747 			ix = rob_objects[count]->img_x; iy = rob_objects[count]->img_y;
748 			iw = rob_objects[count]->img_w; ih = rob_objects[count]->img_h;
749 			if (overeventfound && rob_objects[count]->img_over_surface != NULL) {
750 				img_surface = rob_objects[count]->img_over_surface;
751 				ix = rob_objects[count]->img_over_x; iy = rob_objects[count]->img_over_y;
752 				iw = rob_objects[count]->img_over_w; ih = rob_objects[count]->img_over_h;
753 			}
754 			if (presseventfound && rob_objects[count]->img_press_surface != NULL) {
755 				img_surface = rob_objects[count]->img_press_surface;
756 				ix = rob_objects[count]->img_press_x; iy = rob_objects[count]->img_press_y;
757 				iw = rob_objects[count]->img_press_w; ih = rob_objects[count]->img_press_h;
758 			}
759 
760 			/** Is there a colour to fill? */
761 			if (bgcolour != ROB_UNDEFINED) {
762 				bgcolour = SDL_MapRGB(screen->format, bgcolour >> 16 & 0xff,
763 					bgcolour >> 8 & 0xff, bgcolour & 0xff);
764 				if (alpha == 255) {
765 					/* Fill rectangle */
766 					dstrect.x = cx; dstrect.y = cy;
767 					dstrect.w = rob_objects[count]->w; dstrect.h = rob_objects[count]->h;
768 					if ((SDL_FillRect(screen, &dstrect, bgcolour)) < 0) {
769 						sprintf(rob_last_error, "%s: Cannot fill surface: %s", __func__, SDL_GetError());
770 						retval = ROB_ERROR_CANNOT_FILL_SURFACE;
771 						break;
772 					}
773 				} else {
774 					/* Fill rectangle */
775 					dstrect.x = cx; dstrect.y = cy;
776 					dstrect.w = rob_objects[count]->w; dstrect.h = rob_objects[count]->h;
777 					retval = ROB_FillRectA(screen, &dstrect, bgcolour, alpha);
778 					if (retval) break;
779 				}
780 			}
781 
782 			/** Is there an image to attach? */
783 			if (img_surface != NULL) {
784 				/* If image region is unused then use entire image */
785 				if (ix == ROB_UNDEFINED && iy == ROB_UNDEFINED &&
786 					iw == ROB_UNDEFINED && ih == ROB_UNDEFINED) {
787 					ix = 0; iy = 0; iw = img_surface->w; ih = img_surface->h;
788 				}
789 				if (alpha != 255) {
790 					/* Record surface's use of SDL_RLEACCEL.
791 					   Because the surface is not compressed until the first blit,
792 					   SDL_RLEACCELOK must be checked for too, I imagine as compression is pending */
793 					if (img_surface->flags & (SDL_RLEACCEL | SDL_RLEACCELOK)) use_rleaccel = SDL_RLEACCEL;
794 					/* Set the image surface alpha. */
795 					/* Note that this removes SDL_RLEACCEL because using it with alpha
796 					   is very very slow; it's restored at the end of the function */
797 					if ((SDL_SetAlpha(img_surface, SDL_SRCALPHA, alpha)) < 0) {
798 						sprintf(rob_last_error, "%s: Cannot set surface alpha: %s", __func__, SDL_GetError());
799 						retval = ROB_ERROR_CANNOT_SET_SURFACE_ALPHA;
800 						break;
801 					}
802 				}
803 				srcrect.x = ix; srcrect.y = iy; srcrect.w = iw; srcrect.h = ih;
804 				dstrect.x = cx; dstrect.y = cy; dstrect.w = rob_objects[count]->w; dstrect.h = rob_objects[count]->h;
805 				/* Blit surface */
806 				if ((SDL_BlitSurface(img_surface, &srcrect, screen, &dstrect)) < 0) {
807 					sprintf(rob_last_error, "%s: Cannot blit surface: %s", __func__, SDL_GetError());
808 					retval = ROB_ERROR_CANNOT_BLIT_SURFACE;
809 					break;
810 				}
811 				if (alpha != 255) {
812 					/* Disable the image surface alpha and restore SDL_RLEACCEL if set.
813 					   Note that officially NOT passing the SDL_SRCALPHA flag disables
814 					   alpha blits from this surface but then it's not possible to reenable
815 					   SDL_RLEACCEL, so I simply set the alpha value to SDL_ALPHA_OPAQUE
816 					   (255) which achieves the same result. */
817 					if ((SDL_SetAlpha(img_surface, SDL_SRCALPHA | use_rleaccel, 255)) < 0) {
818 						sprintf(rob_last_error, "%s: Cannot set surface alpha: %s", __func__, SDL_GetError());
819 						retval = ROB_ERROR_CANNOT_SET_SURFACE_ALPHA;
820 						break;
821 					}
822 				}
823 			}
824 		}
825 	}
826 
827 	return retval;
828 }
829 
830 /***************************************************************************
831  * FillRectA                                                               *
832  ***************************************************************************/
833 /* SDL_BlitSurface alpha blends, so an alpha rectangle first needs to become
834    a surface with per-surface-alpha set and then blitted to the screen */
835 
836 /*  On exit: returns ROB_ERROR_NONE on success else
837 			 any one of the error codes in ROB_engine.h */
838 
ROB_FillRectA(SDL_Surface * surface,SDL_Rect * dstrect,Uint32 colour,Uint8 alpha)839 int ROB_FillRectA(SDL_Surface *surface, SDL_Rect *dstrect, Uint32 colour, Uint8 alpha) {
840 	int retval = ROB_ERROR_NONE;
841 
842 	/* Set the blend surface alpha */
843 	if ((SDL_SetAlpha(rob_blend_screen, SDL_SRCALPHA, alpha)) < 0) {
844 		sprintf(rob_last_error, "%s: Cannot set surface alpha: %s", __func__, SDL_GetError());
845 		retval = ROB_ERROR_CANNOT_SET_SURFACE_ALPHA;
846 	}
847 	if (!retval) {
848 		/* Fill rectangle */
849 		if ((SDL_FillRect(rob_blend_screen, dstrect, colour)) < 0) {
850 			sprintf(rob_last_error, "%s: Cannot fill surface: %s", __func__, SDL_GetError());
851 			retval = ROB_ERROR_CANNOT_FILL_SURFACE;
852 		}
853 	}
854 	if (!retval) {
855 		/* Blit surface */
856 		if ((SDL_BlitSurface(rob_blend_screen, dstrect, surface, dstrect)) < 0) {
857 			sprintf(rob_last_error, "%s: Cannot blit surface: %s", __func__, SDL_GetError());
858 			retval = ROB_ERROR_CANNOT_BLIT_SURFACE;
859 		}
860 	}
861 
862 	return retval;
863 }
864 
865 /***************************************************************************
866  * Get Canvas Offset                                                       *
867  ***************************************************************************/
868 /* This gets an object's canvas offset.
869    If a NULL pob is found then it will be interpreted as rob_lyr_canvas */
870 
ROB_GetCanvasOffset(ROB_Object * rob_object,int * cx,int * cy)871 int ROB_GetCanvasOffset(ROB_Object *rob_object, int *cx, int *cy) {
872 	int retval = ROB_ERROR_NONE;
873 
874 	if (rob_object == NULL || sizeof(*rob_object) != sizeof(ROB_Object)) {
875 		sprintf(rob_last_error, "%s: object is invalid", __func__);
876 		retval = ROB_ERROR_INVALID_OBJECT;
877 	} else {
878 		*cx = rob_object->x;
879 		*cy = rob_object->y;
880 		while (rob_object != rob_lyr_canvas && rob_object != rob_lyr_root) {
881 			if (rob_object->pob == NULL) {
882 				rob_object = rob_lyr_canvas;
883 			} else {
884 				rob_object = rob_object->pob;
885 			}
886 			*cx += rob_object->x;
887 			*cy += rob_object->y;
888 		}
889 	}
890 
891 	return retval;
892 }
893 
894 /***************************************************************************
895  * Get Visibility                                                          *
896  ***************************************************************************/
897 /* This gets an object's visibility.
898    If a NULL pob is found then it will be interpreted as rob_lyr_canvas */
899 
900 /*  On exit: returns TRUE or FALSE */
901 
ROB_GetVisibility(ROB_Object * rob_object)902 int ROB_GetVisibility(ROB_Object *rob_object) {
903 
904 	if (rob_object == NULL || sizeof(*rob_object) != sizeof(ROB_Object)) {
905 		sprintf(rob_last_error, "%s: object is invalid", __func__);
906 		return TRUE;	/* If NULL object this stops segfaults */
907 	} else {
908 		#ifdef DEBUG_ROB_GET_VISIBILITY
909 			printf("\n%s: Visibility for object %i\n", __func__, rob_object->id);
910 			printf("%s: id=%i pob=%p visible=%i\n", __func__, rob_object->id, rob_object->pob, rob_object->visible);
911 		#endif
912 		while (rob_object->visible == ROB_INHERITED) {
913 			if (rob_object == rob_lyr_root || rob_object == rob_lyr_canvas) {
914 				break;
915 			} else {
916 				if (rob_object->pob == NULL) {
917 					rob_object = rob_lyr_canvas;
918 				} else {
919 					rob_object = rob_object->pob;
920 				}
921 			}
922 			#ifdef DEBUG_ROB_GET_VISIBILITY
923 				printf("%s: id=%i pob=%p visible=%i\n", __func__, rob_object->id, rob_object->pob, rob_object->visible);
924 			#endif
925 		}
926 	}
927 
928 	/* Something valid must be returned and this may not be the
929 	   case if the user has been fiddling with the system objects */
930 	if (rob_object->visible < 0 || rob_object->visible > 1) {
931 		return TRUE;
932 	} else {
933 		return rob_object->visible;
934 	}
935 }
936 
937 /***************************************************************************
938  * Get Enability                                                           *
939  ***************************************************************************/
940 /* This gets an object's enability.
941    If a NULL pob is found then it will be interpreted as rob_lyr_canvas */
942 
943 /*  On exit: returns TRUE or FALSE */
944 
ROB_GetEnability(ROB_Object * rob_object)945 int ROB_GetEnability(ROB_Object *rob_object) {
946 
947 	if (rob_object == NULL || sizeof(*rob_object) != sizeof(ROB_Object)) {
948 		sprintf(rob_last_error, "%s: object is invalid", __func__);
949 		return TRUE;	/* If NULL object this stops segfaults */
950 	} else {
951 		#ifdef DEBUG_ROB_GET_ENABILITY
952 			printf("\n%s: Enability for object %i\n", __func__, rob_object->id);
953 			printf("%s: id=%i pob=%p enabled=%i\n", __func__, rob_object->id, rob_object->pob, rob_object->enabled);
954 		#endif
955 		while (rob_object->enabled == ROB_INHERITED) {
956 			if (rob_object == rob_lyr_root || rob_object == rob_lyr_canvas) {
957 				break;
958 			} else {
959 				if (rob_object->pob == NULL) {
960 					rob_object = rob_lyr_canvas;
961 				} else {
962 					rob_object = rob_object->pob;
963 				}
964 			}
965 			#ifdef DEBUG_ROB_GET_ENABILITY
966 				printf("%s: id=%i pob=%p enabled=%i\n", __func__, rob_object->id, rob_object->pob, rob_object->enabled);
967 			#endif
968 		}
969 	}
970 
971 	/* Something valid must be returned and this may not be the
972 	   case if the user has been fiddling with the system objects */
973 	if (rob_object->enabled < 0 || rob_object->enabled > 1) {
974 		return TRUE;
975 	} else {
976 		return rob_object->enabled;
977 	}
978 }
979 
980 /***************************************************************************
981  * Get Alpha                                                               *
982  ***************************************************************************/
983 /* This gets an object's alpha.
984    If a NULL pob is found then it will be interpreted as rob_lyr_canvas */
985 
986 /*  On exit: returns 0 to 255 */
987 
ROB_GetAlpha(ROB_Object * rob_object)988 int ROB_GetAlpha(ROB_Object *rob_object) {
989 
990 	if (rob_object == NULL || sizeof(*rob_object) != sizeof(ROB_Object)) {
991 		sprintf(rob_last_error, "%s: object is invalid", __func__);
992 		return SDL_ALPHA_OPAQUE;	/* If NULL object this stops segfaults */
993 	} else {
994 		while (rob_object->alpha == ROB_INHERITED) {
995 			if (rob_object == rob_lyr_root || rob_object == rob_lyr_canvas) {
996 				break;
997 			} else {
998 				if (rob_object->pob == NULL) {
999 					rob_object = rob_lyr_canvas;
1000 				} else {
1001 					rob_object = rob_object->pob;
1002 				}
1003 			}
1004 		}
1005 	}
1006 
1007 	/* Something valid must be returned and this may not be the
1008 	   case if the user has been fiddling with the system objects */
1009 	if (rob_object->alpha < 0 || rob_object->alpha > SDL_ALPHA_OPAQUE) {
1010 		return SDL_ALPHA_OPAQUE;
1011 	} else {
1012 		return rob_object->alpha;
1013 	}
1014 }
1015 
1016 /***************************************************************************
1017  * Get Object Under Object                                                 *
1018  ***************************************************************************/
1019 /*  On exit: returns a pointer to the visible object under object on success else NULL */
1020 
ROB_GetObjectUnderObject(ROB_Object * rob_object,int hitpoint)1021 ROB_Object *ROB_GetObjectUnderObject(ROB_Object *rob_object, int hitpoint) {
1022 	int count, cx, cy, cx2, cy2;
1023 
1024 	if (rob_object == NULL || sizeof(*rob_object) != sizeof(ROB_Object)) {
1025 		sprintf(rob_last_error, "%s: object is invalid", __func__);
1026 	} else if (hitpoint != ROB_HIT_POINT_TOP_LEFT && hitpoint != ROB_HIT_POINT_CENTRE) {
1027 		sprintf(rob_last_error, "%s: hitpoint is invalid", __func__);
1028 	} else {
1029 		/* Get hit point of source object */
1030 		ROB_GetCanvasOffset(rob_object, &cx, &cy);
1031 		if (hitpoint == ROB_HIT_POINT_TOP_LEFT) {
1032 			/* cx and cy are ok */
1033 		} else if (hitpoint == ROB_HIT_POINT_CENTRE) {
1034 			cx += rob_object->w / 2;
1035 			cy += rob_object->h / 2;
1036 		}
1037 		for (count = rob_object_count - 1; count >= 0; count--) {
1038 			if (ROB_GetVisibility(rob_objects[count]) && count < rob_object->zorder) {
1039 				ROB_GetCanvasOffset(rob_objects[count], &cx2, &cy2);
1040 				if (cx >= cx2 && cx < cx2 + rob_objects[count]->w && cy >= cy2 && cy < cy2 + rob_objects[count]->h) {
1041 					return rob_objects[count];
1042 				}
1043 			}
1044 		}
1045 	}
1046 
1047 	return NULL;
1048 }
1049 
1050 /***************************************************************************
1051  * Find Ancestor                                                           *
1052  ***************************************************************************/
1053 /* This traces back from (and including) an object looking for a
1054    particular ancestor.
1055    If a NULL pob is found then it will be interpreted as rob_lyr_canvas */
1056 
1057 /*  On exit: returns TRUE if found else FALSE */
1058 
ROB_FindAncestor(ROB_Object * rob_object,ROB_Object * target)1059 int ROB_FindAncestor(ROB_Object *rob_object, ROB_Object *target) {
1060 
1061 	if (rob_object == NULL || sizeof(*rob_object) != sizeof(ROB_Object)) {
1062 		sprintf(rob_last_error, "%s: object is invalid", __func__);
1063 	} else if (target == NULL || sizeof(*target) != sizeof(ROB_Object)) {
1064 		sprintf(rob_last_error, "%s: target is invalid", __func__);
1065 	} else {
1066 		#ifdef DEBUG_ROB_FIND_ANCESTOR
1067 			printf("\n%s: Looking for object %i's ancestor %i\n", __func__, rob_object->id, target->id);
1068 			printf("%s: id=%i pob=%p\n", __func__, rob_object->id, rob_object->pob);
1069 		#endif
1070 		while (rob_object != target) {
1071 			if (rob_object == rob_lyr_root || rob_object == rob_lyr_canvas) {
1072 				return FALSE;
1073 			} else {
1074 				if (rob_object->pob == NULL) {
1075 					rob_object = rob_lyr_canvas;
1076 				} else {
1077 					rob_object = rob_object->pob;
1078 				}
1079 			}
1080 			#ifdef DEBUG_ROB_FIND_ANCESTOR
1081 				printf("%s: id=%i pob=%p\n", __func__, rob_object->id, rob_object->pob);
1082 			#endif
1083 		}
1084 	}
1085 
1086 	return TRUE;
1087 }
1088 
1089 /***************************************************************************
1090  * Generate Events                                                         *
1091  ***************************************************************************/
1092 
ROB_GenerateEvents(int * actionid)1093 void ROB_GenerateEvents(int *actionid) {
1094 	static int last_pointer_x = 0, last_pointer_y = 0;
1095 	static int press_point_x = 0, press_point_y = 0;
1096 	int mx, my, px, py, pxu = 0, pyu = 0;
1097 	ROB_Object *new_oup, *pob;
1098 	ROB_Event rob_event;
1099 
1100 	if (rob_op_env.pointer_mode == ROB_POINTER_MODE_OFF) {
1101 		rob_oup = rob_lyr_root;
1102 	} else {
1103 		/* Get or set the object currently under the pointer. If an object is currently
1104 		   being pressed and it's enabled and draggable then it is attached to the pointer */
1105 		if (rob_pressed_object != NULL && ROB_GetEnability(rob_pressed_object) && rob_pressed_object->draggable) {
1106 			/* Force it as rob_oup to prevent dragging-off through latency */
1107 			rob_oup = rob_pressed_object;
1108 			ROB_GetCanvasOffset(rob_oup->pob, &px, &py);
1109 			/* Attach the object to the pointer */
1110 			ROB_GetCanvasOffset(rob_lyr_pointer, &mx, &my);
1111 			rob_oup->x = mx - px - press_point_x;
1112 			rob_oup->y = my - py - press_point_y;
1113 			/* Adjust x/y for the granularity: n = n + (n % g) / (g / 2) * g - (n % g) */
1114 			if (rob_oup->drag_granularity > 1) {
1115 				rob_oup->x += rob_oup->x % rob_oup->drag_granularity / (rob_oup->drag_granularity / 2) * rob_oup->drag_granularity - rob_oup->x % rob_oup->drag_granularity;
1116 				rob_oup->y += rob_oup->y % rob_oup->drag_granularity / (rob_oup->drag_granularity / 2) * rob_oup->drag_granularity - rob_oup->y % rob_oup->drag_granularity;
1117 			}
1118 			/* Is dragging restricted in any way? */
1119 			if (rob_oup->draggable == ROB_DRAG_RESTRICT_TO_PARENT || rob_oup->draggable == ROB_DRAG_RESTRICT_TO_PARENT_ORIGIN) {
1120 				pob = rob_oup->pob;
1121 				if (rob_oup->draggable == ROB_DRAG_RESTRICT_TO_PARENT) {
1122 					if (rob_oup->x + rob_oup->w > pob->w) rob_oup->x = pob->w - rob_oup->w;
1123 					if (rob_oup->y + rob_oup->h > pob->h) rob_oup->y = pob->h - rob_oup->h;
1124 				}
1125 				if (rob_oup->x < 0) rob_oup->x = 0;
1126 				if (rob_oup->y < 0) rob_oup->y = 0;
1127 			}
1128 		} else {
1129 			new_oup = ROB_GetObjectUnderObject(rob_lyr_pointer, ROB_HIT_POINT_TOP_LEFT);
1130 			/* rob_oup could be NULL here because it may have been a freed object but
1131 			   new_oup will most definitely contain at least a system object */
1132 			if (rob_oup != new_oup) {
1133 				/* Send one out event if the object is not NULL and enabled */
1134 				if (rob_oup != NULL && ROB_GetEnability(rob_oup)) {
1135 					rob_event.rob_object = rob_oup; rob_event.klasse = ROB_EVENT_OUT; ROB_SendEvent(&rob_event);
1136 				}
1137 				rob_oup = new_oup;
1138 				/* Send one over event if the object is enabled */
1139 				if (ROB_GetEnability(rob_oup)) {
1140 					rob_event.rob_object = rob_oup; rob_event.klasse = ROB_EVENT_OVER; ROB_SendEvent(&rob_event);
1141 				}
1142 			}
1143 			ROB_GetCanvasOffset(rob_oup->pob, &px, &py);
1144 		}
1145 		/* Detect dragging of the pointer across enabled objects */
1146 		if (rob_pressed_object != NULL && rob_pressed_object == rob_oup && ROB_GetEnability(rob_oup)) {
1147 			if (last_pointer_x != rob_lyr_pointer->x || last_pointer_y != rob_lyr_pointer->y) {
1148 				rob_event.rob_object = rob_oup; rob_event.klasse = ROB_EVENT_DRAG; ROB_SendEvent(&rob_event);
1149 			}
1150 		}
1151 		/* Record current pointer offsets for later comparison */
1152 		last_pointer_x = rob_lyr_pointer->x;
1153 		last_pointer_y = rob_lyr_pointer->y;
1154 
1155 		/* Process any reported action */
1156 		if (*actionid != ROB_UNDEFINED) {
1157 			switch (*actionid) {
1158 				case ACTION_UP:
1159 					if (rob_op_env.pointer_mode == ROB_POINTER_MODE_SIMULATED) {
1160 						pyu = -1;
1161 					}
1162 					break;
1163 				case ACTION_UP_RIGHT:
1164 					if (rob_op_env.pointer_mode == ROB_POINTER_MODE_SIMULATED) {
1165 						pyu = -1; pxu = 1;
1166 					}
1167 					break;
1168 				case ACTION_RIGHT:
1169 					if (rob_op_env.pointer_mode == ROB_POINTER_MODE_SIMULATED) {
1170 						pxu = 1;
1171 					}
1172 					break;
1173 				case ACTION_DOWN_RIGHT:
1174 					if (rob_op_env.pointer_mode == ROB_POINTER_MODE_SIMULATED) {
1175 						pyu = 1; pxu = 1;
1176 					}
1177 					break;
1178 				case ACTION_DOWN:
1179 					if (rob_op_env.pointer_mode == ROB_POINTER_MODE_SIMULATED) {
1180 						pyu = 1;
1181 					}
1182 					break;
1183 				case ACTION_DOWN_LEFT:
1184 					if (rob_op_env.pointer_mode == ROB_POINTER_MODE_SIMULATED) {
1185 						pyu = 1; pxu = -1;
1186 					}
1187 					break;
1188 				case ACTION_LEFT:
1189 					if (rob_op_env.pointer_mode == ROB_POINTER_MODE_SIMULATED) {
1190 						pxu = -1;
1191 					}
1192 					break;
1193 				case ACTION_UP_LEFT:
1194 					if (rob_op_env.pointer_mode == ROB_POINTER_MODE_SIMULATED) {
1195 						pyu = -1; pxu = -1;
1196 					}
1197 					break;
1198 				case ACTION_SELECT:
1199 					if (rob_op_env.pointer_mode == ROB_POINTER_MODE_PHYSICAL) break;
1200 				case ACTION_PRIMARY_CLICK:
1201 					if (rob_pressed_object == NULL) {
1202 						rob_pressed_object = rob_oup;
1203 						/* Only generate events for enabled objects */
1204 						if (ROB_GetEnability(rob_oup)) {
1205 							rob_event.rob_object = rob_oup; rob_event.klasse = ROB_EVENT_PRESS; ROB_SendEvent(&rob_event);
1206 							rob_event.rob_object = rob_oup; rob_event.klasse = ROB_EVENT_SELECT; ROB_SendEvent(&rob_event);
1207 							/* For draggable [enabled] objects, calculate the drag points */
1208 							if (rob_oup->draggable) {
1209 								/* Get the current pointer coordinates */
1210 								ROB_GetCanvasOffset(rob_lyr_pointer, &mx, &my);
1211 								if (rob_oup->drag_point == ROB_DRAG_POINT_ANYWHERE) {
1212 									press_point_x = mx - px - rob_oup->x;
1213 									press_point_y = my - py - rob_oup->y;
1214 								} else if (rob_oup->drag_point == ROB_DRAG_POINT_MIDDLE) {
1215 									press_point_x = rob_oup->w / 2;
1216 									press_point_y = rob_oup->h / 2;
1217 								}
1218 							}
1219 						}
1220 					} else {	/* Something is already being pressed */
1221 						/* Only generate events for enabled objects */
1222 						if (rob_pressed_object == rob_oup && ROB_GetEnability(rob_oup)) {
1223 							rob_event.rob_object = rob_oup; rob_event.klasse = ROB_EVENT_SELECT; ROB_SendEvent(&rob_event);
1224 						}
1225 					}
1226 					break;
1227 				case ACTION_SELECT | 0x80:
1228 					if (rob_op_env.pointer_mode == ROB_POINTER_MODE_PHYSICAL) break;
1229 				case ACTION_PRIMARY_CLICK | 0x80:
1230 					if (rob_pressed_object != NULL) {
1231 						if (rob_pressed_object == rob_oup) {
1232 							/* Only generate events for enabled objects */
1233 							if (ROB_GetEnability(rob_oup)) {
1234 								rob_event.rob_object = rob_oup; rob_event.klasse = ROB_EVENT_CLICK; ROB_SendEvent(&rob_event);
1235 							}
1236 						} else {
1237 							/* Only generate events for enabled objects */
1238 							if (ROB_GetEnability(rob_pressed_object)) {
1239 								rob_event.rob_object = rob_oup; rob_event.klasse = ROB_EVENT_RELEASE; ROB_SendEvent(&rob_event);
1240 							}
1241 						}
1242 					}
1243 					rob_pressed_object = NULL;
1244 					break;
1245 			}
1246 			/* Manage pointer input if required */
1247 			if (rob_op_env.pointer_mode == ROB_POINTER_MODE_SIMULATED) ROB_ManagePointerInput(pxu, pyu);
1248 		}
1249 	}
1250 
1251 }
1252 
1253 /***************************************************************************
1254  * Manage Pointer Input                                                    *
1255  ***************************************************************************/
1256 
ROB_ManagePointerInput(int pxu,int pyu)1257 void ROB_ManagePointerInput(int pxu, int pyu) {
1258 	static int lastpxu = 0, lastpyu = 0, unitcount = 0;
1259 	int xmov, ymov;
1260 
1261 	/* Is the x or y unit the same as last time? */
1262 	if ((pxu != 0 && pxu == lastpxu) || (pyu != 0 && pyu == lastpyu)) {
1263 		unitcount++;
1264 		/* If the count reaches a threshold then change gear */
1265 		if (unitcount < rob_op_env.pointer_move_unit_threshold) {
1266 			xmov = pxu * rob_op_env.pointer_move_unit_low;
1267 			ymov = pyu * rob_op_env.pointer_move_unit_low;
1268 		} else {
1269 			xmov = pxu * rob_op_env.pointer_move_unit_high;
1270 			ymov = pyu * rob_op_env.pointer_move_unit_high;
1271 		}
1272 	} else {
1273 		unitcount = 0;
1274 		xmov = pxu * rob_op_env.pointer_move_unit_low;
1275 		ymov = pyu * rob_op_env.pointer_move_unit_low;
1276 	}
1277 	lastpxu = pxu; lastpyu = pyu;
1278 
1279 	/* Now move the pointer */
1280 	rob_lyr_pointer->x += xmov;
1281 	rob_lyr_pointer->y += ymov;
1282 
1283 	/* Keep it within the bounds of the screen */
1284 	if (rob_lyr_pointer->x < 0) {
1285 		rob_lyr_pointer->x = 0;
1286 	} else if (rob_lyr_pointer->x > screen->w - 1) {
1287 		rob_lyr_pointer->x = screen->w - 1;
1288 	}
1289 	if (rob_lyr_pointer->y < 0) {
1290 		rob_lyr_pointer->y = 0;
1291 	} else if (rob_lyr_pointer->y > screen->h - 1) {
1292 		rob_lyr_pointer->y = screen->h - 1;
1293 	}
1294 
1295 }
1296 
1297 /***************************************************************************
1298  * Send Event                                                              *
1299  ***************************************************************************/
1300 /*  On exit: returns ROB_ERROR_NONE on success else
1301 			 any one of the error codes in ROB_engine.h */
1302 
ROB_SendEvent(ROB_Event * rob_event)1303 int ROB_SendEvent(ROB_Event *rob_event) {
1304 	int retval = ROB_ERROR_NONE;
1305 
1306 	if (rob_event == NULL || sizeof(*rob_event) != sizeof(ROB_Event)) {
1307 		sprintf(rob_last_error, "%s: event is invalid", __func__);
1308 		retval = ROB_ERROR_INVALID_EVENT;
1309 	} else {
1310 		#ifdef DEBUG_ROB_EVENTS
1311 			ROB_ShowEvent(rob_event, __func__);
1312 		#endif
1313 		rob_op_env.event_processor(rob_event);
1314 	}
1315 
1316 	return retval;
1317 }
1318 
1319 /***************************************************************************
1320  * Show Event                                                              *
1321  ***************************************************************************/
1322 
ROB_ShowEvent(ROB_Event * rob_event,const char * func)1323 int ROB_ShowEvent(ROB_Event *rob_event, const char *func) {
1324 	int retval = ROB_ERROR_NONE;
1325 
1326 	if (rob_event == NULL || sizeof(*rob_event) != sizeof(ROB_Event)) {
1327 		sprintf(rob_last_error, "%s: event is invalid", func);
1328 		retval = ROB_ERROR_INVALID_EVENT;
1329 	} else {
1330 		printf("%i: ", cycle_count);
1331 		switch (rob_event->klasse) {
1332 			case ROB_EVENT_OVER:
1333 				printf("%s: ROB_EVENT_OVER", func);
1334 				break;
1335 			case ROB_EVENT_OUT:
1336 				printf("%s: ROB_EVENT_OUT", func);
1337 				break;
1338 			case ROB_EVENT_PRESS:
1339 				printf("%s: ROB_EVENT_PRESS", func);
1340 				break;
1341 			case ROB_EVENT_CLICK:
1342 				printf("%s: ROB_EVENT_CLICK", func);
1343 				break;
1344 			case ROB_EVENT_RELEASE:
1345 				printf("%s: ROB_EVENT_RELEASE", func);
1346 				break;
1347 			case ROB_EVENT_DRAG:
1348 				printf("%s: ROB_EVENT_DRAG", func);
1349 				break;
1350 			case ROB_EVENT_SELECT:
1351 				printf("%s: ROB_EVENT_SELECT", func);
1352 				break;
1353 			default:
1354 				printf("%s: Unknown", func);
1355 				break;
1356 		}
1357 		printf(" object=%p id=%i x=%i y=%i w=%i h=%i\n", rob_event->rob_object, rob_event->rob_object->id, rob_event->rob_object->x, rob_event->rob_object->y, rob_event->rob_object->w, rob_event->rob_object->h);
1358 	}
1359 
1360 	return retval;
1361 }
1362 
1363 /***************************************************************************
1364  * Set Operating Environment - System Pointer                              *
1365  ***************************************************************************/
1366 /* On entry: systempointer is true or false */
1367 /*  On exit: returns ROB_ERROR_NONE on success else
1368 			 any one of the error codes in ROB_engine.h */
1369 
ROB_SetOpEnvSystemPointer(int systempointer)1370 int ROB_SetOpEnvSystemPointer(int systempointer) {
1371 	int retval = ROB_ERROR_NONE;
1372 
1373 	#ifdef DEBUG_ROB_PROGRESS_DUMP
1374 		printf("%s: systempointer=%i\n", __func__, systempointer);
1375 	#endif
1376 	/* Store new value(s) */
1377 	rob_op_env.systempointer = systempointer;
1378 	SDL_ShowCursor(systempointer);
1379 
1380 	return retval;
1381 }
1382 
1383 /***************************************************************************
1384  * Set Operating Environment - Pointer                                     *
1385  ***************************************************************************/
1386 /* On entry: pointer's visibility is true or false
1387 			 pointer_mode is one of the modes as listed in ROB_engine.h */
1388 /*  On exit: returns ROB_ERROR_NONE on success else
1389 			 any one of the error codes in ROB_engine.h */
1390 
ROB_SetOpEnvPointer(int pointer,int pointer_mode)1391 int ROB_SetOpEnvPointer(int pointer, int pointer_mode) {
1392 	int retval = ROB_ERROR_NONE;
1393 	#ifdef DEBUG_ROB_PROGRESS_DUMP
1394 		char *pointer_modes[] = {"off", "physical", "simulated"};
1395 	#endif
1396 
1397 	#ifdef DEBUG_ROB_PROGRESS_DUMP
1398 		printf("%s: pointer=%i pointer_mode=%s\n", __func__, pointer, pointer_modes[pointer_mode]);
1399 	#endif
1400 	/* Store new value(s) */
1401 	rob_op_env.pointer = pointer;
1402 	rob_op_env.pointer_mode = pointer_mode;
1403 	if (rob_object_count > 0) rob_lyr_pointer->visible = pointer;
1404 
1405 	return retval;
1406 }
1407 
1408 /***************************************************************************
1409  * Set Operating Environment - Event Processor                             *
1410  ***************************************************************************/
1411 /* On entry: event_processor is a pointer to a function that receives events */
1412 /*  On exit: returns ROB_ERROR_NONE on success else
1413 			 any one of the error codes in ROB_engine.h */
1414 
ROB_SetOpEnvEventProcessor(void (* event_processor)(ROB_Event * rob_event))1415 int ROB_SetOpEnvEventProcessor(void (*event_processor)(ROB_Event *rob_event)) {
1416 	int retval = ROB_ERROR_NONE;
1417 
1418 	#ifdef DEBUG_ROB_PROGRESS_DUMP
1419 		printf("%s: %p\n", __func__, event_processor);
1420 	#endif
1421 	/* Store new value(s) */
1422 	rob_op_env.event_processor = event_processor;
1423 
1424 	return retval;
1425 }
1426 
1427 
1428 
1429 
1430 
1431 
1432 
1433 
1434 
1435 
1436