#include "scegra.h" #include "eruta.h" #include "bevec.h" #include "image.h" #include "rebox.h" #include "bad.h" #include "draw.h" #include "str.h" #include "flags.h" #include "store.h" #include "state.h" #include "laytext.h" #include #define SCEGRA_TEXT_MAX 128 enum ScegraNodeFlags_ { SCEGRA_NODE_HIDE = 1 }; enum ScegraNodeKind_ { SCEGRA_NODE_UNKNOWN = 0, SCEGRA_NODE_BITMAP, SCEGRA_NODE_BOX, SCEGRA_NODE_LINE, SCEGRA_NODE_TRIANGLE, SCEGRA_NODE_SPLINE, SCEGRA_NODE_PIXEL, SCEGRA_NODE_POLYGON, SCEGRA_NODE_PRIM, SCEGRA_NODE_TEXT, SCEGRA_NODE_LONGTEXT, }; /* A very simple scene graph, mainly for drawing the UI that will be managed from * the scripting side of things. To avoid memory allocation, limit the amount of * vertices allowed to SCERGRA_VERTEX_MAX (32) and characters allowed for a * string to 128. (enough to fill the screen with a font of 5 pixels wide). * Of course this is all for a LINE of text, the scegra will use multiple text * text lines for multiline displays. */ struct ScegraRound_ { float start_theta, delta_theta; }; struct ScegraBitmap_ { BeVec src_pos, src_size; float angle; int image_id; }; struct ScegraBox_ { BeVec round; }; struct ScegraLine_ { float _unused; }; struct ScegraTriangle_ { float x1, y1, x2, y2, x3, y3; }; struct ScegraSpline_ { float vertices[8]; }; struct ScegraPixel_ { float x1, y1; }; struct ScegraPolygon_ { float vertices[SCEGRA_VERTEX_MAX]; int vertex_count; }; struct ScegraPrim_ { ALLEGRO_VERTEX vertices[SCEGRA_VERTEX_MAX]; int vertex_count; }; struct ScegraText_ { float x1, x2, y, diff; char text[SCEGRA_TEXT_MAX]; int from, until; }; /* For long text that can scroll and display letter per letter. */ struct ScegraLongText_ { float x1, x2, y, diff; char * text; int line_start, line_stop, line_pos; /* Current text "window". */ int line_max, line_height; /* text constants. */ int page_lines; /* Text "window" size for one "page" of text in amount of lines. */ int paused; double delay_total; }; /** Union: ScegraData. * A union of different types of vertex data for drawing the various primitives * and bitmaps. */ union ScegraData_ { struct ScegraPixel_ pixel; struct ScegraLine_ line; struct ScegraTriangle_ triangle; struct ScegraBox_ box; struct ScegraBitmap_ bitmap; struct ScegraPolygon_ polygon; struct ScegraRound_ round; struct ScegraSpline_ spline; struct ScegraPrim_ prim; struct ScegraText_ text; struct ScegraLongText_ longtext; }; typedef union ScegraData_ ScegraData; struct ScegraNode_ { int id; int z; int step; int kind; int flags; double delay; BeVec pos; BeVec size; BeVec speed; struct ScegraStyle_ style; union ScegraData_ data; ScegraDraw * draw; ScegraUpdate * update; ScegraDone * done; }; /* Scene graph struct. */ struct Scegra_ { ScegraNode * nodes; int node_max; }; #define SCEGRA_NODES_MAX 10000 #define SCEGRA_LONGTEXT_LINE_POS_MAX 99999 /* Static storage for nodes. */ static ScegraNode scegra_nodes[SCEGRA_NODES_MAX]; /* Nodes pointers sorted in drawing order. */ static ScegraNode * scegra_nodes_todraw[SCEGRA_NODES_MAX]; /* How many nodes to draw this time. */ static int scegra_to_draw = 0; void scegranode_done(ScegraNode * self) { if (!self) return; if (self->done) self->done(self); self->id = -1; self->z = -1; } ScegraNode * scegranode_initall(ScegraNode * node, int kind, int id, BeVec pos, BeVec siz, ScegraData data, ScegraStyle style, ScegraDraw * draw, ScegraUpdate * update) { if(!node) return NULL; scegranode_done(node); node->id = id; node->z = id; node->pos = pos; node->size = siz; node->speed = bevec0(); node->data = data; node->style = style; node->draw = draw; node->update = update; node->step = 0; node->flags = 0; node->done = NULL; node->kind = kind; node->delay = 0.05; return node; } /* Gets pointer to the font for this scegra node, or to a default font. */ static Font * scegra_get_font(ScegraNode * self) { Font * font; /* Use default font if font not loaded. */ font = store_get_font(self->style.font_id); if (!font) font = state_font(state_get()); return font; } /** Gets the current text page for a longtext. */ int scegranode_page(ScegraNode * node) { struct ScegraLongText_ * lt; if (!node) return -2; if (node->id < 0) return -1; if (node->kind != SCEGRA_NODE_LONGTEXT) return -3; lt = &node->data.longtext; return lt->line_start / lt->page_lines; } /** Gets the last page number for a longtext or negative on error. */ int scegranode_last_page(ScegraNode * node) { struct ScegraLongText_ * lt; if (!node) return -2; if (node->id < 0) return -1; if (node->kind != SCEGRA_NODE_LONGTEXT) return -3; lt = &node->data.longtext; return lt->line_max / lt->page_lines; } /** Advances long text to the given page. Automatically unpauses as well. */ int scegranode_page_(ScegraNode * node, int page) { struct ScegraLongText_ * lt; int line; if (!node) return -2; if (node->id < 0) return -1; if (node->kind != SCEGRA_NODE_LONGTEXT) return -3; if (page < 0) return -4; lt = &node->data.longtext; line = page * lt->page_lines; /* Check for page overflow. */ if (line >= lt->line_max) { return -5; } lt->paused = false; lt->line_start = line; /* Negative delay is instant display. */ if (node->delay < 0) { lt->line_stop = lt->line_start + lt->page_lines; lt->line_pos = SCEGRA_LONGTEXT_LINE_POS_MAX; } else { lt->line_stop = lt->line_start + 1; lt->line_pos = 0; } return page; } void scegranode_longtext_done(ScegraNode * self) { free(self->data.longtext.text); self->data.longtext.text = NULL; } void scegra_update_generic(ScegraNode * self, double dt) { self->pos = bevec_add(self->pos, bevec_mul(self->speed, dt)); } /* This function is the helper callback that implements * updating scrolled/partial text for Longtexts. */ static bool scegra_update_custom_partial_text(int line_num, const char *line, int size, void *extra) { ScegraNode * self = extra; struct ScegraLongText_ * st = &self->data.longtext; st->line_max = line_num; /* Don't draw lines before start but keep on drawing (for later lines) */ if (line_num < st->line_start) return true; /* Don't draw lines after stop, but keep calculating to get correct amount of lines */ if (line_num >= st->line_stop) return true; /* Reveal letter by letter on last line */ if (line_num == (st->line_stop - 1)) { /* Advance the position automatically if not paused. */ if (!st->paused) { st->line_pos++; } /* Reached eol, advance to next line. */ if (st->line_pos >= size) { /* Is if the text window is full, pause, otherwise show the next line. */ if ((st->line_stop - st->line_start) >= st->page_lines) { st->paused = true; } else { st->line_stop++; st->line_pos = 0; } } } (void) line; return true; } /* Updates the longtext, enables scrolling and per character display. */ void scegra_update_longtext(ScegraNode * self, double dt) { scegra_update_generic(self, dt); float width = self->size.x - self->style.margin * 2; struct ScegraLongText_ * st = &self->data.longtext; char * text = self->data.longtext.text ? self->data.longtext.text : "NULL"; /* Delay advence of characters somewhat. */ st->delay_total += dt; if (st->delay_total < self->delay) return; st->delay_total = 0; st->line_max = 0; al_do_multiline_text(scegra_get_font(self), width, (const char *) text, scegra_update_custom_partial_text, self); st->line_max++; /* pause if the last line is reached, and prevent overflow. */ if (st->line_stop > st->line_max) { st->paused = true; st->line_pos = SCEGRA_LONGTEXT_LINE_POS_MAX; st->line_stop = st->line_max; } } /* Returns TRUE if the longtext node is at the end of the text to display, * false if not (more text to display) */ int scegranode_longtext_at_end(ScegraNode * node) { struct ScegraLongText_ * lt; if (!node) return FALSE; if (node->id < 0) return FALSE; if (node->kind != SCEGRA_NODE_LONGTEXT) return FALSE; lt = &node->data.longtext; return ((lt->line_stop >= lt->line_max) && (lt->line_pos >= SCEGRA_LONGTEXT_LINE_POS_MAX)); } void scegra_draw_box(ScegraNode * self) { BeVec p2; p2 = bevec_add(self->pos, self->size); float thick = self->style.border_thickness; if (self->style.background_image_id >= 0) { ALLEGRO_BITMAP * bmp = store_get_bitmap(self->style.background_image_id); /* XXX: need another param for corner size or is the autodetect OK? */ image_blitscale9(bmp, self->pos.x, self->pos.y, self->size.x, self->size.y, -1, -1); // self->data.box.round.x, self->data.box.round.y); } else { al_draw_filled_rounded_rectangle(self->pos.x, self->pos.y, p2.x, p2.y, self->data.box.round.x, self->data.box.round.y, self->style.background_color ); } if (self->style.border_thickness > 0.0f) { al_draw_rounded_rectangle(self->pos.x + thick/2, self->pos.y + thick/2, p2.x - thick/2, p2.y - thick/2, self->data.box.round.x - thick/4, self->data.box.round.y - thick/4, self->style.border_color, thick); } } /* Calculates the x and y position where to draw text, taking alignment and * margin into consideration. */ static BeVec scegra_calculate_text_position(ScegraNode * self) { BeVec result; int flags = self->style.text_flags; result.x = self->pos.x + self->style.margin; result.y = self->pos.y + self->style.margin; if (flags & ALLEGRO_ALIGN_CENTER) { result.x = self->pos.x + self->style.margin + self->size.x / 2; } else if (flags & ALLEGRO_ALIGN_RIGHT) { result.x = self->pos.x + self->size.x - self->style.margin; } return result; } void scegra_draw_text(ScegraNode * self) { Font * font; int flags; BeVec pos = scegra_calculate_text_position(self); font = scegra_get_font(self); flags = self->style.text_flags | ALLEGRO_ALIGN_INTEGER; /* Draw the text twice, once offset in bg color to produce a shadow, and once normally with foreground color. */ al_draw_text(font, self->style.background_color, pos.x + 1, pos.y + 1, flags, self->data.text.text); al_draw_text(font, self->style.color, pos.x, pos.y, flags, self->data.text.text); } /** Calculates the amount of lines that scegra_draw_partial_text can draw * at most with this text. */ static int scegra_partial_text_lines(ALLEGRO_FONT * font, float w, const char * text) { (void) font; (void) w; (void) text; return 0; /* XXX todo */ } /* This function is the helper callback that implements the actual drawing * for scegra_draw_partial_text. */ static bool scegra_draw_custom_partial_text(int line_num, const char *line, int size, void *extra) { float x, y; ScegraNode * self = extra; struct ScegraLongText_ * st = &self->data.longtext; ALLEGRO_USTR_INFO info; int real_size, flags; double width; ALLEGRO_FONT * font; BeVec pos; font = scegra_get_font(self); flags = self->style.text_flags | ALLEGRO_ALIGN_INTEGER; pos = scegra_calculate_text_position(self); /* Don't draw lines before start but keep on drawing (for later lines) */ if (line_num < st->line_start) return true; /* Don't draw lines after stop, drawing is done */ if (line_num >= st->line_stop) return false; real_size = size; /* calculate position */ x = pos.x; y = pos.y + (st->line_height * (line_num - st->line_start)); /* Reveal letter by letter on last line */ if (line_num == (st->line_stop - 1)) { real_size = (st->line_pos < size) ? st->line_pos : size; /* Draw continuation marker if paused. */ if (st->paused) { float x1, y1, x2, y2, x3, y3; y2 = y + 8; x2 = x + self->size.x - self->style.margin; x1 = x2 - 8; y1 = y2; x3 = x2 - 4; y3 = y2 + 8; al_draw_filled_triangle(x1, y1, x2, y2, x3, y3, self->style.color); } } al_draw_ustr(font, self->style.background_color, x + 1, y + 1, flags, al_ref_buffer(&info, line, real_size)); al_draw_ustr(font, self->style.color, x, y, flags, al_ref_buffer(&info, line, real_size)); return true; } /* Allows to draw a multi line text partially, from line_start up to * line_stop * Scraws scrolling text from a prefilled struct. */ void scegra_draw_partial_text(ScegraNode * self) { float width = self->size.x - self->style.margin * 2; struct ScegraLongText_ * st = &self->data.longtext; char * text = self->data.longtext.text ? self->data.longtext.text : "NULL"; /* It's a bit of a hack that this ends up here... */ if (st->line_height < 1.0) { st->line_height = al_get_font_line_height(scegra_get_font(self)); } al_do_multiline_text(scegra_get_font(self), width, (const char *) text, scegra_draw_custom_partial_text, self); } void scegra_draw_longtext(ScegraNode * self) { Font * font; int flags; const char * text; float width; BeVec pos = scegra_calculate_text_position(self); font = scegra_get_font(self); flags = self->style.text_flags | ALLEGRO_ALIGN_INTEGER; text = self->data.longtext.text ? self->data.longtext.text : "NULL"; width = self->size.x - self->style.margin * 2; scegra_draw_partial_text(self); /* Draw the text twice, once offset in bg color to produce a shadow, and once normally with foreground color. */ /* al_draw_multiline_text(font, self->style.background_color, pos.x + 1, pos.y + 1, width, 0, flags, text); al_draw_multiline_text(font, self->style.color, pos.x, pos.y, width, 0, flags, text); **/ } void scegra_draw_image(ScegraNode * self) { Image * image; int flags = self->style.image_flags; float angle = self->data.bitmap.angle; float cx, cy, dx, dy, xscale, yscale; cx = self->data.bitmap.src_pos.x + self->data.bitmap.src_size.x / 2; cy = self->data.bitmap.src_pos.y + self->data.bitmap.src_size.y / 2; dx = self->pos.x + self->size.x / 2; dy = self->pos.y + self->size.y / 2; xscale = self->size.x / self->data.bitmap.src_size.x; yscale = self->size.y / self->data.bitmap.src_size.y; image = store_get_bitmap(self->data.bitmap.image_id); /* Draw missing image rectangle. */ if (!image) { BeVec p2; p2 = bevec_add(self->pos, self->size); al_draw_filled_rounded_rectangle(self->pos.x, self->pos.y, p2.x, p2.y, self->data.box.round.x, self->data.box.round.y, self->style.background_color ); } else { al_draw_tinted_scaled_rotated_bitmap(image, self->style.color, cx, cy, dx, dy, xscale, yscale, angle, flags); } } ScegraStyle * scegrastyle_initempty(ScegraStyle * self) { if (!self) return NULL; self->background_image_id = -1; self->color = al_map_rgba_f(1.0,1.0,1.0,1.0); self->background_color = al_map_rgba_f(0.0,0.0,0.0,0.0); self->border_color = al_map_rgba_f(1.0,1.0,1.0,1.0); self->font_id = -1; self->line_join_style = 0; self->line_miter_limit = 1; self->border_thickness = -1; self->image_flags = 0; self->text_flags = 0; self->margin = 0; return self; } ScegraNode * scegranode_init_box(ScegraNode * self, int id, BeVec pos, BeVec siz, BeVec round, ScegraStyle style) { ScegraData data; if (!self) return NULL; data.box.round = round; scegranode_initall(self, SCEGRA_NODE_BOX, id, pos, siz, data, style, scegra_draw_box, scegra_update_generic); return self; } ScegraNode * scegranode_init_text(ScegraNode * self, int id, BeVec pos, BeVec siz, const char * text, ScegraStyle style) { ScegraData data; if (!self) return NULL; strncpy(data.text.text, text, SCEGRA_TEXT_MAX); data.text.text[SCEGRA_TEXT_MAX - 1] = '\0'; scegranode_initall(self, SCEGRA_NODE_TEXT, id, pos, siz, data, style, scegra_draw_text, scegra_update_generic); return self; } int scegranode_set_longtext_text(ScegraNode * self, const char * text) { free(self->data.longtext.text); self->data.longtext.text = cstr_dup((char *)text); if (!self->data.longtext.text) return -1; self->data.longtext.line_start = 0; self->data.longtext.line_stop = 1; self->data.longtext.line_pos = 0; self->data.longtext.delay_total = 0.0; scegranode_page_(self, 0); return 0; } ScegraNode * scegranode_init_longtext (ScegraNode * self, int id, BeVec pos, BeVec siz, char * text, ScegraStyle style) { ScegraData data; if (!self) return NULL; memset(&data.longtext, 0, sizeof(data.longtext)); data.longtext.page_lines = 4; data.longtext.line_start = 0; data.longtext.line_stop = 1; data.longtext.paused = 0; scegranode_initall(self, SCEGRA_NODE_LONGTEXT, id, pos, siz, data, style, scegra_draw_longtext, scegra_update_longtext); scegranode_set_longtext_text(self, text); self->done = scegranode_longtext_done; scegranode_page_(self, 0); return self; } ScegraNode * scegranode_init_image_ex( ScegraNode * self, int id, BeVec pos, BeVec siz, int image_id, ScegraStyle style, BeVec spos, BeVec ssiz, float angle, int flags) { ScegraData data; if (!self) return NULL; data.bitmap.angle = angle; data.bitmap.image_id = image_id; data.bitmap.src_pos = spos; data.bitmap.src_size = ssiz; scegranode_initall(self, SCEGRA_NODE_BITMAP, id, pos, siz, data, style, scegra_draw_image, scegra_update_generic); self->style.image_flags = flags; return self; } ScegraNode * scegranode_init_image(ScegraNode * self, int id, BeVec pos, BeVec siz, int image_id, ScegraStyle style) { int w, h; if (siz.x <= 0.0) { if (store_get_bitmap_width(image_id, &w)) siz.x = w; } if (siz.y <= 0.0) { if (store_get_bitmap_height(image_id, &h)) siz.y = h; } return scegranode_init_image_ex(self, id, pos, siz, image_id, style, bevec0(), siz, 0.0, 0); } /* Returns nonzero if the scegranode is in use, false if not */ static int scegranode_in_use_p(ScegraNode * node) { return node->id != -1; } /* Returns nonzero if the scene graph node id is in use. Also returns * false if the ID is out of range. */ int scegra_id_in_use_p(int index) { ScegraNode * node = scegra_get_node(index); if (!node) return FALSE; return scegranode_in_use_p(node); } /* Returns the first free ID that is larger than the minimum. Returns * negative if no more ID's are free. */ int scegra_get_free_id(int minimum) { int index; if (minimum < 0) return -1; for (index = minimum; index < SCEGRA_NODES_MAX; index++) { if (!scegra_id_in_use_p(index)) { return index; } } return -1; } /* Returns maximum amount of nodes. */ int scegra_nodes_max() { return SCEGRA_NODES_MAX; } /* Initializes the simple 2D scene graph */ void scegra_init() { int index; for (index = 0; index < SCEGRA_NODES_MAX; index++) { ScegraNode * node = scegra_nodes + index; node->id = -1; /* Negative id means unused; */ } scegra_to_draw = 0; } /* End the use of the simple 2D scene graph */ void scegra_done() { int index; for (index = 0; index < SCEGRA_NODES_MAX; index++) { ScegraNode * node = scegra_nodes + index; scegranode_done(node); } scegra_to_draw = 0; } /* Compares a scene graph node with another, for drawing order. Things with low z come before those with high z, and with same z, low id comes before high id. qsort compatible. No NULL values allowed!!! */ static inline int scegranode_compare_for_drawing(const void * p1, const void * p2) { register const ScegraNode * self = *((ScegraNode **) p1); register const ScegraNode * other = *((ScegraNode **) p2); register int self_val, other_val; /* Compare Z if no nulls. */ self_val = self->z; other_val = other->z; if (self_val < other_val) return -1; if (self_val > other_val) return 1; /* compare id if z is equal */ self_val = self->id; other_val = other->id; if (self_val < other_val) return -1; if (self_val > other_val) return 1; return 0; } /* Updates the 2d scene graph. */ void scegra_update(double dt) { register int index; scegra_to_draw = 0; for (index = 0; index < SCEGRA_NODES_MAX; index++) { register ScegraNode * node = scegra_nodes + index; if (node->id < 0) continue; /* Negative id means unused; */ if(node->update) { node->update(node, dt); } /* Only add node to draw array if not hidden and drawable. */ if((!flags_get(node->flags, SCEGRA_NODE_HIDE)) && (node->draw)) { scegra_nodes_todraw[scegra_to_draw] = node; scegra_to_draw++; } } /* Sort scene graph nodes for drawing. */ qsort(scegra_nodes_todraw, scegra_to_draw, sizeof(ScegraNode*), scegranode_compare_for_drawing); } /* Draws the 2d scene graph. */ void scegra_draw() { int index; /* All nodes in the todraw will be drawn up to scegra_to_draw, there are no other drawable bodes in there. */ for (index = 0; index < scegra_to_draw; index++) { ScegraNode * node = scegra_nodes_todraw[index]; node->draw(node); } } /* Returns true if out of bounds for the scene graph, or false if ok. */ int scegra_out_of_bounds(int index) { if (index < 0) return TRUE; if (index > scegra_nodes_max()) return TRUE; return FALSE; } /* Returns a node from the scene graph or NULL if out of bounds. */ ScegraNode * scegra_get_node(int index) { if (scegra_out_of_bounds(index)) return NULL; return scegra_nodes + index; } /* Returns the ID of the scene graph node at index. -1 means it's free, *-2 means out of range. */ int scegra_get_id(int index) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; return node->id; } /* Sets the scene graph node as not in use. */ int scegra_disable_node(int index) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; scegranode_done(node); return node->id = -1; } /* Initializes the node as a box. */ int scegra_make_box(int id, BeVec pos, BeVec siz, BeVec round, ScegraStyle style) { ScegraNode * node = scegra_get_node(id); if (!node) return -2; scegranode_init_box(node, id, pos, siz, round, style); return node->id; } /* Initialies the node at index as a text. Text can only span one line and must be short. Line wrapping must be implemented by making many text nodes for every line of text. */ int scegra_make_text(int id, BeVec pos, BeVec siz, const char * text, ScegraStyle style) { ScegraNode * node = scegra_get_node(id); if (!node) return -2; scegranode_init_text(node, id, pos, siz, text, style); return node->id; } /* Initialies the node at index as a long text. Long text is arbitrary size and will * wrap automatically to the width of the scegra element. */ int scegra_make_longtext(int id, BeVec pos, BeVec siz, char * text, ScegraStyle style) { ScegraNode * node = scegra_get_node(id); if (!node) return -2; scegranode_init_longtext(node, id, pos, siz, text, style); return node->id; } /* Initializes the node at index as an image node. */ int scegra_make_image(int id, BeVec pos, BeVec siz, int image_id, ScegraStyle style) { ScegraNode * node = scegra_get_node(id); if (!node) return -2; scegranode_init_image(node, id, pos, siz, image_id, style); return node->id; } /* Gets the style of the scegra object, or if not active, gets a default style. */ ScegraStyle scegra_get_style(int sindex) { ScegraStyle style; ScegraNode * snode = scegra_get_node(sindex); if (snode && (snode->id >= 0)) { style = snode->style; } else { scegrastyle_initempty(&style); } return style; } /* Initializes the node as a box with a style copied from the node at sindex, * or if that is not in use, a default style. */ int scegra_make_box_style_from(int id, BeVec pos, BeVec siz, BeVec round, int sindex) { return scegra_make_box(id, pos, siz, round, scegra_get_style(sindex)); } /* Initializes the node as a text with a style copied from the node at sindex, * or if that is not in use, a default style. */ int scegra_make_text_style_from(int id, BeVec pos, BeVec siz, const char * text, int sindex) { return scegra_make_text(id, pos, siz, text, scegra_get_style(sindex)); } /* Initializes the node as a text long with a style copied from the node at sindex, * or if that is not in use, a default style. */ int scegra_make_longtext_style_from(int id, BeVec pos, BeVec siz, const char * text, int sindex) { return scegra_make_longtext(id, pos, siz, (char *)text, scegra_get_style(sindex)); } /* Initializes the node as a image with a style copied from the node at sindex, * or if that is not in use, a default style. */ int scegra_make_image_style_from(int id, BeVec pos, BeVec siz, int image_id, int sindex) { return scegra_make_image(id, pos, siz, image_id, scegra_get_style(sindex)); } /* Returns the Z level of the scene graph node at index. -1 means it's free, *-2 means out of range. */ int scegra_z(int index) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; return node->z; } /* Sets the Z level of the scene graph node at index. -1 means it's free, *-2 means out of range. */ int scegra_z_(int index, int z) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; return node->z = z; } /* Sets the FG color of the scegra node (used to draw, e.g. text or lines (but not borders) */ int scegra_color_(int index, int r, int g, int b, int a) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; node->style.color = al_map_rgba(r, g, b, a); return node->z; } /* Sets the border color of the scegra node (used to draw, e.g. text or lines (but not borders) */ int scegra_border_color_(int index, int r, int g, int b, int a) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; node->style.border_color = al_map_rgba(r, g, b, a); return node->z; } /* Sets the background color of the scegra node (used to draw, e.g. text or lines (but not borders) */ int scegra_background_color_(int index, int r, int g, int b, int a) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; node->style.background_color = al_map_rgba(r, g, b, a); return node->z; } /* Sets the border thickness. Set 0 or negative to disable border. */ int scegra_border_thickness_(int index, float t) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; node->style.border_thickness = t; return node->z; } /* Sets the margin. Set 0 or negative to disable border. */ int scegra_margin_(int index, float m) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; node->style.margin = m; return node->z; } /* Sets the scegra node's position. */ int scegra_position_(int index, float x, float y) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; node->pos = bevec(x, y); return node->z; } /* Sets the scegra node's size. */ int scegra_size_(int index, float w, float h) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; node->size = bevec(w, h); return node->z; } /* Sets the scegra node's speed. */ int scegra_speed_(int index, float x, float y) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; node->speed = bevec(x, y); return node->z; } /* Sets the scegra node's background image id . */ int scegra_background_image_id_(int index, int rindex) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; node->style.background_image_id = rindex; return node->z; } /* Sets the scegra node's background font id . */ int scegra_font_id_(int index, int rindex) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; node->style.font_id = rindex; return node->z; } /* Sets the scegra node's foreground image id . */ int scegra_image_id_(int index, int rindex) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; node->data.bitmap.image_id = rindex; return node->z; } /* Sets the scegra node to be visible or hidden depending on is_visible. */ int scegra_visible_(int index, int is_visible) { int result; ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; result = flags_get(node->flags, SCEGRA_NODE_HIDE) ? 1 : 0; flags_put(&node->flags, SCEGRA_NODE_HIDE, !is_visible); return result; } /* Sets the scegra node's drawing angle for images. */ int scegra_angle_(int index, float angle) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; node->data.bitmap.angle = angle; return node->z; } /* Sets the scegra node's drawing flags for images. */ int scegra_image_flags_(int index, int flags) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; node->style.image_flags = flags; return node->z; } /* Sets the scegra node's drawing flags for texts. */ int scegra_text_flags_(int index, int flags) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; node->style.text_flags = flags; return node->z; } /* Updates the scegra node's text if it is a text or longtext. Returns the node's z value * on sucess or negative on failure, if it is not a text or longtext node. */ int scegra_text_(int index, const char * text) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; if (node->kind == SCEGRA_NODE_TEXT) { strncpy(node->data.text.text, text, SCEGRA_TEXT_MAX); } else if (node->kind == SCEGRA_NODE_LONGTEXT) { scegranode_longtext_done(node); scegranode_set_longtext_text(node, text); } else { return -3; } return node->z; } /* Somewhat unrelated, show or hide the system mouse cursor. * Returns the state after calling, true means show, false means not shown. */ int scegra_show_system_mouse_cursor(int show) { ALLEGRO_DISPLAY * display; display = al_get_current_display(); if(!display) return FALSE; if (show) { return al_show_mouse_cursor(display); } else { return (!al_hide_mouse_cursor(display)); } } /** Gets the speed of the scregra node. Returns negative on error. */ int scegra_speed(int index, float * x, float * y) { ScegraNode * node = scegra_get_node(index); if (!x || !y) return -3; if (!node) return -2; if (node->id < 0) return -1; (*x) = node->speed.x; (*y) = node->speed.y; return node->z; } /** Gets the size of the scregra node. Returns negative on error. */ int scegra_size(int index, float * w, float * h) { ScegraNode * node = scegra_get_node(index); if (!w || !h) return -3; if (!node) return -2; if (node->id < 0) return -1; (*w) = node->size.x; (*h) = node->size.y; return node->z; } int scegra_position(int index, float *x, float * y) { /** Gets the positiob of the scregra node. Returns negative on error. */ ScegraNode * node = scegra_get_node(index); if (!x || !y) return -3; if (!node) return -2; if (node->id < 0) return -1; (*x) = node->pos.x; (*y) = node->pos.y; return node->z; } int scegra_border_thickness(int index, float * t); int scegra_background_color(int index, int * r, int * g,int * b,int * a); int scegra_border_color(int index,int * r,int * g,int * b, int * a); int scegra_color(int index, int * r, int * g, int * b,int * a); /** Returns the natural size of the bitmap of the image. Will be * set to -1 if the bitmap isn't loaded yet. */ int scegra_image_bitmap_size(int index, int * w, int * h) { int image_id; ScegraNode * node = scegra_get_node(index); if (!w || !h) return -3; if (!node) return -2; if (node->id < 0) return -1; image_id = node->data.bitmap.image_id; if(!store_get_bitmap_width(image_id, w)) (*w) = -1; if(!store_get_bitmap_height(image_id, h)) (*h) = -1; return node->z; } /** Sets the last line to display for a long text */ int scegra_line_stop_(int index, int stop) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; if (node->kind != SCEGRA_NODE_LONGTEXT) return -3; node->data.longtext.line_stop = stop; return node->z; } /** Sets the first line to display for a long text */ int scegra_line_start_(int index, int start) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; if (node->kind != SCEGRA_NODE_LONGTEXT) return -3; node->data.longtext.line_start = start; return node->z; } /** Sets display delay between individual characters for a long text */ int scegra_delay_(int index, double delay) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; if (node->kind != SCEGRA_NODE_LONGTEXT) return -3; node->delay = delay; return node->z; } /** Gets the last line to display for a long text or negative on error*/ int scegra_line_stop(int index) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; if (node->kind != SCEGRA_NODE_LONGTEXT) return -3; return node->data.longtext.line_stop; } /** Gets the first line to display for a long text */ int scegra_line_start(int index) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; if (node->kind != SCEGRA_NODE_LONGTEXT) return -3; return node->data.longtext.line_start; } /** Gets display delay between individual characters for a long text */ double scegra_delay(int index) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; if (node->kind != SCEGRA_NODE_LONGTEXT) return -3; return node->delay; } /** Sets amount of shown lines for a long text */ int scegra_page_lines_(int index, int lines) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; if (node->kind != SCEGRA_NODE_LONGTEXT) return -3; node->data.longtext.page_lines = lines; return node->z; } /** Gets amount of lines for a "page" of long text */ int scegra_page_lines(int index) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; if (node->kind != SCEGRA_NODE_LONGTEXT) return -3; return node->data.longtext.page_lines; } /** Sets paused state of long text */ int scegra_paused_(int index, int paused) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; if (node->kind != SCEGRA_NODE_LONGTEXT) return -3; node->data.longtext.paused = paused; return node->z; } /** Gets paused state of long text */ int scegra_paused(int index) { ScegraNode * node = scegra_get_node(index); if (!node) return -2; if (node->id < 0) return -1; if (node->kind != SCEGRA_NODE_LONGTEXT) return -3; return node->data.longtext.paused; } /** Gets the current text page for a longtext. */ int scegra_page(int index) { ScegraNode * node = scegra_get_node(index); return scegranode_page(node); } /** Gets the number of the last text page for a longtext. */ int scegra_last_page(int index) { ScegraNode * node = scegra_get_node(index); return scegranode_last_page(node); } /** Returns nonzero if the long text is at the end of it's display. */ int scegra_at_end(int index) { ScegraNode * node = scegra_get_node(index); return scegranode_longtext_at_end(node); } /** Advances long text to the given page. Automatically unpauses as well. */ int scegra_page_(int index, int page) { ScegraNode * node = scegra_get_node(index); return scegranode_page_(node, page); } /** Advances long text to the next page. Automatically unpauses as well. */ int scegra_next_page(int index) { int page = scegra_page(index); if (page < 0) return page; return scegra_page_(index, page + 1); } /** Advances long text to the previous page. Automatically unpauses as well. */ int scegra_previous_page(int index) { int page = scegra_page(index); if (page < 0) return page; return scegra_page_(index, page - 1); } /* void scegra_drawframe(ScegraNode * self) { draw_frame(self->box.at.x, self->box.at.y, self->box.size.x, self->box.size.y, self->style.bordersize, self->style.forecolor, self->style.backcolor); } void scegra_drawroundframe(ScegraNode * self) { draw_roundframe(self->box.at.x , self->box.at.y, self->box.size.x, self->box.size.y, self->style.bordersize, self->style.forecolor, self->style.backcolor); } */ /* * ALLEGRO drawing functions: * al_draw_arc (3) - Allegro 5 API al_draw_bitmap (3) - Allegro 5 API al_draw_bitmap_region (3) - Allegro 5 API al_draw_circle (3) - Allegro 5 API al_draw_ellipse (3) - Allegro 5 API al_draw_elliptical_arc (3) - Allegro 5 API al_draw_filled_circle (3) - Allegro 5 API al_draw_filled_ellipse (3) - Allegro 5 API al_draw_filled_pieslice (3) - Allegro 5 API al_draw_filled_polygon (3) - Allegro 5 API al_draw_filled_polygon_with_holes (3) - Allegro 5 API al_draw_filled_rectangle (3) - Allegro 5 API al_draw_filled_rounded_rectangle (3) - Allegro 5 API al_draw_filled_triangle (3) - Allegro 5 API al_draw_indexed_prim (3) - Allegro 5 API al_draw_justified_text (3) - Allegro 5 API al_draw_justified_textf (3) - Allegro 5 API al_draw_justified_ustr (3) - Allegro 5 API q (3) - Allegro 5 API al_draw_pieslice (3) - Allegro 5 API al_draw_pixel (3) - Allegro 5 API al_draw_polygon (3) - Allegro 5 API al_draw_polygon_with_holes (3) - Allegro 5 API al_draw_polyline (3) - Allegro 5 API al_draw_polyline_ex (3) - Allegro 5 API al_draw_prim (3) - Allegro 5 API al_draw_rectangle (3) - Allegro 5 API al_draw_ribbon (3) - Allegro 5 API al_draw_rotated_bitmap (3) - Allegro 5 API al_draw_rounded_rectangle (3) - Allegro 5 API al_draw_scaled_bitmap (3) - Allegro 5 API al_draw_scaled_rotated_bitmap (3) - Allegro 5 API al_draw_soft_line (3) - Allegro 5 API al_draw_soft_triangle (3) - Allegro 5 API al_draw_spline (3) - Allegro 5 API al_draw_text (3) - Allegro 5 API al_draw_textf (3) - Allegro 5 API al_draw_tinted_bitmap (3) - Allegro 5 API al_draw_tinted_bitmap_region (3) - Allegro 5 API al_draw_tinted_rotated_bitmap (3) - Allegro 5 API al_draw_tinted_scaled_bitmap (3) - Allegro 5 API al_draw_tinted_scaled_rotated_bitmap (3) - Allegro 5 API al_draw_tinted_scaled_rotated_bitm... (3) - Allegro 5 API al_draw_triangle (3) - Allegro 5 API al_draw_ustr (3) - Allegro 5 API al_draw_vertex_buffer (3) - Allegro 5 API So the most important drawing functions I'll implement and group together are: * Circle / arc drawing and Ellipse / elliptical arc drawing. * Rectangle drawing and Rounded rectangle drawing. * Triangle drawing. * Polygon drawing. * Line drawing. * Point drawing. * Spline Drawing. * Bitmap Drawing. */ #define TUPLE(A,B) tuple_##A##B #define DEFINE_TUPLE(A, B) typedef struct TUPLE(A,B) { A head; B tail; } TUPLE(A,B) #define TUPLE_HEAD(T) (T).head #define TUPLE_TAIL(T) (T).tail #define TUPLEPTR_HEAD(T) (T)->head #define TUPLEPTR_TAIL(T) (T)->tail typedef char * charp; DEFINE_TUPLE(int, charp);