diff --git a/CMakeLists.txt b/CMakeLists.txt index ec2fe1c..b4f7595 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,6 +100,7 @@ target_sources(xsystem4 PRIVATE src/parts/flash.c src/parts/flat.c src/parts/input.c + src/parts/layoutbox.c src/parts/motion.c src/parts/parts.c src/parts/render.c diff --git a/include/parts.h b/include/parts.h index 0f75fbc..e2dcca4 100644 --- a/include/parts.h +++ b/include/parts.h @@ -285,4 +285,23 @@ bool PE_StartPartsFlat(int parts_no, int state); bool PE_GoFramePartsFlat(int parts_no, int frame_no, int state); int PE_GetPartsFlatEndFrame(int parts_no, int state); +// layoutbox.c +void PE_SetLayoutBoxLayoutType(int parts_no, int type); +int PE_GetLayoutBoxLayoutType(int parts_no); +void PE_SetLayoutBoxReturn(int parts_no, bool return_flag, int return_size); +bool PE_IsLayoutBoxReturn(int parts_no); +int PE_GetLayoutBoxReturnSize(int parts_no); +void PE_SetLayoutBoxAlign(int parts_no, int align); +int PE_GetLayoutBoxAlign(int parts_no); +void PE_SetComponentMargin(int parts_no, int top, int bottom, int left, int right); +int PE_GetComponentMarginTop(int parts_no); +int PE_GetComponentMarginBottom(int parts_no); +int PE_GetComponentMarginLeft(int parts_no); +int PE_GetComponentMarginRight(int parts_no); +void PE_set_layoutbox_padding(int parts_no, int top, int bottom, int left, int right); +int PE_get_layoutbox_padding_top(int parts_no); +int PE_get_layoutbox_padding_bottom(int parts_no); +int PE_get_layoutbox_padding_left(int parts_no); +int PE_get_layoutbox_padding_right(int parts_no); + #endif /* SYSTEM4_PARTS_H */ diff --git a/src/hll/PartsEngine.c b/src/hll/PartsEngine.c index 6539cee..7e0df7d 100644 --- a/src/hll/PartsEngine.c +++ b/src/hll/PartsEngine.c @@ -282,6 +282,30 @@ static int PartsEngine_PartsFunc(int func_id, struct page **array_int, REQUIRE_INTS(2); PE_parts_set_lock_input_state(ints[0].i, !!ints[1].i); return 1; + case 57: // void AppendChild(int number, int child_number) + REQUIRE_INTS(2); + PE_SetParentPartsNumber(ints[1].i, ints[0].i); + return 1; + case 91: // void SetLayoutBoxPadding(int parts_no, int top, int bottom, int left, int right) + REQUIRE_INTS(5); + PE_set_layoutbox_padding(ints[0].i, ints[1].i, ints[2].i, ints[3].i, ints[4].i); + return 1; + case 92: // int GetLayoutBoxPaddingTop(int parts_no) + REQUIRE_INTS(2); + ints[1].i = PE_get_layoutbox_padding_top(ints[0].i); + return 1; + case 93: // int GetLayoutBoxPaddingBottom(int parts_no) + REQUIRE_INTS(2); + ints[1].i = PE_get_layoutbox_padding_bottom(ints[0].i); + return 1; + case 94: // int GetLayoutBoxPaddingLeft(int parts_no) + REQUIRE_INTS(2); + ints[1].i = PE_get_layoutbox_padding_left(ints[0].i); + return 1; + case 95: // int GetLayoutBoxPaddingRight(int parts_no) + REQUIRE_INTS(2); + ints[1].i = PE_get_layoutbox_padding_right(ints[0].i); + return 1; case 103: // void GetPartsCGSurfaceArea(int parts_no, int *x, int *y, int *w, int *h, int state) REQUIRE_INTS(6); PE_GetPartsCGSurfaceArea(ints[0].i, &ints[1].i, &ints[2].i, &ints[3].i, &ints[4].i, ints[5].i); @@ -552,11 +576,11 @@ HLL_LIBRARY(PartsEngine, HLL_TODO_EXPORT(GetComponentRotateX, PartsEngine_GetComponentRotateX), HLL_TODO_EXPORT(GetComponentRotateY, PartsEngine_GetComponentRotateY), HLL_EXPORT(GetComponentRotateZ, PE_GetPartsRotateZ), - HLL_TODO_EXPORT(SetComponentMargin, PartsEngine_SetComponentMargin), - HLL_TODO_EXPORT(GetComponentMarginTop, PartsEngine_GetComponentMarginTop), - HLL_TODO_EXPORT(GetComponentMarginBottom, PartsEngine_GetComponentMarginBottom), - HLL_TODO_EXPORT(GetComponentMarginLeft, PartsEngine_GetComponentMarginLeft), - HLL_TODO_EXPORT(GetComponentMarginRight, PartsEngine_GetComponentMarginRight), + HLL_EXPORT(SetComponentMargin, PE_SetComponentMargin), + HLL_EXPORT(GetComponentMarginTop, PE_GetComponentMarginTop), + HLL_EXPORT(GetComponentMarginBottom, PE_GetComponentMarginBottom), + HLL_EXPORT(GetComponentMarginLeft, PE_GetComponentMarginLeft), + HLL_EXPORT(GetComponentMarginRight, PE_GetComponentMarginRight), HLL_EXPORT(SetComponentAlphaClipper, PE_SetPartsAlphaClipperPartsNumber), HLL_TODO_EXPORT(GetComponentAlphaClipper, PartsEngine_GetComponentAlphaClipper), HLL_TODO_EXPORT(SetComponentTextureFilterType, PartsEngine_SetComponentTextureFilterType), @@ -724,13 +748,13 @@ HLL_LIBRARY(PartsEngine, HLL_TODO_EXPORT(GetMultiTextBoxSelectB, PartsEngine_GetMultiTextBoxSelectB), HLL_TODO_EXPORT(SetMultiTextBoxCGName, PartsEngine_SetMultiTextBoxCGName), HLL_TODO_EXPORT(GetMultiTextBoxCGName, PartsEngine_GetMultiTextBoxCGName), - HLL_TODO_EXPORT(SetLayoutBoxLayoutType, PartsEngine_SetLayoutBoxLayoutType), - HLL_TODO_EXPORT(GetLayoutBoxLayoutType, PartsEngine_GetLayoutBoxLayoutType), - HLL_TODO_EXPORT(SetLayoutBoxReturn, PartsEngine_SetLayoutBoxReturn), - HLL_TODO_EXPORT(IsLayoutBoxReturn, PartsEngine_IsLayoutBoxReturn), - HLL_TODO_EXPORT(GetLayoutBoxReturnSize, PartsEngine_GetLayoutBoxReturnSize), - HLL_TODO_EXPORT(SetLayoutBoxAlign, PartsEngine_SetLayoutBoxAlign), - HLL_TODO_EXPORT(GetLayoutBoxAlign, PartsEngine_GetLayoutBoxAlign), + HLL_EXPORT(SetLayoutBoxLayoutType, PE_SetLayoutBoxLayoutType), + HLL_EXPORT(GetLayoutBoxLayoutType, PE_GetLayoutBoxLayoutType), + HLL_EXPORT(SetLayoutBoxReturn, PE_SetLayoutBoxReturn), + HLL_EXPORT(IsLayoutBoxReturn, PE_IsLayoutBoxReturn), + HLL_EXPORT(GetLayoutBoxReturnSize, PE_GetLayoutBoxReturnSize), + HLL_EXPORT(SetLayoutBoxAlign, PE_SetLayoutBoxAlign), + HLL_EXPORT(GetLayoutBoxAlign, PE_GetLayoutBoxAlign), HLL_EXPORT(Parts_SetPartsCG, PE_SetPartsCG), HLL_EXPORT(Parts_GetPartsCGName, PE_GetPartsCGName), HLL_EXPORT(Parts_SetPartsCGSurfaceArea, PE_SetPartsCGSurfaceArea), diff --git a/src/meson.build b/src/meson.build index 26c1861..a1a2a2d 100644 --- a/src/meson.build +++ b/src/meson.build @@ -61,6 +61,7 @@ xsystem4 = [version_h, 'parts/flash.c', 'parts/flat.c', 'parts/input.c', + 'parts/layoutbox.c', 'parts/message.c', 'parts/motion.c', 'parts/parts.c', diff --git a/src/parts/debug.c b/src/parts/debug.c index c5a24c7..04a1b84 100644 --- a/src/parts/debug.c +++ b/src/parts/debug.c @@ -272,6 +272,10 @@ static cJSON *parts_state_to_json(struct parts_state *state, bool verbose) break; case PARTS_RECT_DETECTION: break; + case PARTS_LAYOUT_BOX: + cJSON_AddNumberToObject(obj, "layout_type", state->layout_box.layout_type); + cJSON_AddNumberToObject(obj, "align", state->layout_box.align); + break; } return obj; @@ -566,6 +570,9 @@ static void parts_list_print(struct parts *parts, int indent) case PARTS_RECT_DETECTION: sys_message("(rect_detection)"); break; + case PARTS_LAYOUT_BOX: + sys_message("(layout_box type=%d)", state->layout_box.layout_type); + break; } sys_message(" @ controller=%d z=%d (%d)\n", parts->controller_no, parts->global.z, parts->local.z); diff --git a/src/parts/layoutbox.c b/src/parts/layoutbox.c new file mode 100644 index 0000000..1bdb833 --- /dev/null +++ b/src/parts/layoutbox.c @@ -0,0 +1,337 @@ +/* Copyright (C) 2026 kichikuou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include + +#include "system4/flat.h" + +#include "parts.h" +#include "parts_internal.h" + +void PE_SetComponentMargin(int parts_no, int top, int bottom, int left, int right) +{ + struct parts *parts = parts_get(parts_no); + parts->margin_top = top; + parts->margin_bottom = bottom; + parts->margin_left = left; + parts->margin_right = right; +} + +int PE_GetComponentMarginTop(int parts_no) +{ + return parts_get(parts_no)->margin_top; +} + +int PE_GetComponentMarginBottom(int parts_no) +{ + return parts_get(parts_no)->margin_bottom; +} + +int PE_GetComponentMarginLeft(int parts_no) +{ + return parts_get(parts_no)->margin_left; +} + +int PE_GetComponentMarginRight(int parts_no) +{ + return parts_get(parts_no)->margin_right; +} + +void PE_SetLayoutBoxLayoutType(int parts_no, int type) +{ + struct parts *parts = parts_get(parts_no); + struct parts_layout_box *lb = parts_get_layout_box(parts); + if (lb->layout_type != type) { + lb->layout_type = type; + parts_component_dirty(parts); + } +} + +int PE_GetLayoutBoxLayoutType(int parts_no) +{ + struct parts *parts = parts_try_get(parts_no); + if (!parts || parts->states[0].type != PARTS_LAYOUT_BOX) + return PARTS_LAYOUT_FREE; + return parts->states[0].layout_box.layout_type; +} + +void PE_SetLayoutBoxReturn(int parts_no, bool return_flag, int return_size) +{ + struct parts *parts = parts_get(parts_no); + struct parts_layout_box *lb = parts_get_layout_box(parts); + lb->wrap = return_flag; + lb->wrap_size = return_size; + parts_component_dirty(parts); +} + +bool PE_IsLayoutBoxReturn(int parts_no) +{ + struct parts *parts = parts_try_get(parts_no); + if (!parts || parts->states[0].type != PARTS_LAYOUT_BOX) + return false; + return parts->states[0].layout_box.wrap; +} + +int PE_GetLayoutBoxReturnSize(int parts_no) +{ + struct parts *parts = parts_try_get(parts_no); + if (!parts || parts->states[0].type != PARTS_LAYOUT_BOX) + return 0; + return parts->states[0].layout_box.wrap_size; +} + +void PE_SetLayoutBoxAlign(int parts_no, int align) +{ + struct parts *parts = parts_get(parts_no); + struct parts_layout_box *lb = parts_get_layout_box(parts); + if (lb->align != align) { + lb->align = align; + parts_component_dirty(parts); + } +} + +int PE_GetLayoutBoxAlign(int parts_no) +{ + struct parts *parts = parts_try_get(parts_no); + if (!parts || parts->states[0].type != PARTS_LAYOUT_BOX) + return 0; + return parts->states[0].layout_box.align; +} + +void PE_set_layoutbox_padding(int parts_no, int top, int bottom, int left, int right) +{ + struct parts *parts = parts_get(parts_no); + struct parts_layout_box *lb = parts_get_layout_box(parts); + lb->padding_top = top; + lb->padding_bottom = bottom; + lb->padding_left = left; + lb->padding_right = right; + parts_component_dirty(parts); +} + +int PE_get_layoutbox_padding_top(int parts_no) +{ + struct parts *parts = parts_try_get(parts_no); + if (!parts || parts->states[0].type != PARTS_LAYOUT_BOX) + return 0; + return parts->states[0].layout_box.padding_top; +} + +int PE_get_layoutbox_padding_bottom(int parts_no) +{ + struct parts *parts = parts_try_get(parts_no); + if (!parts || parts->states[0].type != PARTS_LAYOUT_BOX) + return 0; + return parts->states[0].layout_box.padding_bottom; +} + +int PE_get_layoutbox_padding_left(int parts_no) +{ + struct parts *parts = parts_try_get(parts_no); + if (!parts || parts->states[0].type != PARTS_LAYOUT_BOX) + return 0; + return parts->states[0].layout_box.padding_left; +} + +int PE_get_layoutbox_padding_right(int parts_no) +{ + struct parts *parts = parts_try_get(parts_no); + if (!parts || parts->states[0].type != PARTS_LAYOUT_BOX) + return 0; + return parts->states[0].layout_box.padding_right; +} + +static void parts_get_layout_size(struct parts *parts, int *w, int *h) +{ + struct parts_state *state = &parts->states[parts->state]; + if (state->type == PARTS_FLAT && state->flat.flat) { + // The layout size of Flat is specified in the header, and this may + // differ from the hitbox size. + *w = state->flat.flat->hdr.width; + *h = state->flat.flat->hdr.height; + } else { + *w = state->common.w; + *h = state->common.h; + } +} + +static int align_offset_x(int align, int total_w) +{ + switch (align) { + case 2: case 5: case 8: return -(total_w / 2); + case 3: case 6: case 9: return -total_w; + default: return 0; + } +} + +static int align_offset_y(int align, int total_h) +{ + switch (align) { + case 4: case 5: case 6: return -(total_h / 2); + case 7: case 8: case 9: return -total_h; + default: return 0; + } +} + +// Calculates the total size of a horizontal layout box by summing child widths per row. +static void parts_layout_calc_size_horizontal(struct parts_layout_box *lb, struct parts *parts, + int *out_w, int *out_h) +{ + int row_w = 0, max_row_w = 0, max_row_h = 0, total_h = 0; + struct parts *child; + PARTS_FOREACH_CHILD(child, parts) { + int child_w, child_h; + parts_get_layout_size(child, &child_w, &child_h); + int cw = child->margin_left + child_w + child->margin_right; + int ch = child->margin_top + child_h + child->margin_bottom; + if (lb->wrap && row_w + cw > lb->wrap_size && row_w > 0) { + if (row_w > max_row_w) + max_row_w = row_w; + total_h += max_row_h; + row_w = 0; + max_row_h = 0; + } + row_w += cw; + if (ch > max_row_h) + max_row_h = ch; + } + if (row_w > max_row_w) + max_row_w = row_w; + total_h += max_row_h; + *out_w = max_row_w + lb->padding_left + lb->padding_right; + *out_h = total_h + lb->padding_top + lb->padding_bottom; +} + +// Calculates the total size of a vertical layout box by summing child heights per column. +static void parts_layout_calc_size_vertical(struct parts_layout_box *lb, struct parts *parts, + int *out_w, int *out_h) +{ + int col_h = 0, max_col_h = 0, max_col_w = 0, total_w = 0; + struct parts *child; + PARTS_FOREACH_CHILD(child, parts) { + int child_w, child_h; + parts_get_layout_size(child, &child_w, &child_h); + int cw = child->margin_left + child_w + child->margin_right; + int ch = child->margin_top + child_h + child->margin_bottom; + if (lb->wrap && col_h + ch > lb->wrap_size && col_h > 0) { + if (col_h > max_col_h) + max_col_h = col_h; + total_w += max_col_w; + col_h = 0; + max_col_w = 0; + } + col_h += ch; + if (cw > max_col_w) + max_col_w = cw; + } + if (col_h > max_col_h) + max_col_h = col_h; + total_w += max_col_w; + *out_w = total_w + lb->padding_left + lb->padding_right; + *out_h = max_col_h + lb->padding_top + lb->padding_bottom; +} + +void parts_do_layout(struct parts *parts) +{ + if (parts->states[0].type != PARTS_LAYOUT_BOX) + return; + struct parts_layout_box *lb = &parts->states[0].layout_box; + if (lb->layout_type == PARTS_LAYOUT_FREE) + return; + + int total_w, total_h; + if (lb->layout_type == PARTS_LAYOUT_HORIZONTAL) + parts_layout_calc_size_horizontal(lb, parts, &total_w, &total_h); + else + parts_layout_calc_size_vertical(lb, parts, &total_w, &total_h); + + int align = lb->align; + bool is_left = (align == 1 || align == 4 || align == 7); + bool is_right = (align == 3 || align == 6 || align == 9); + bool is_top = (align == 1 || align == 2 || align == 3); + bool is_bottom = (align == 7 || align == 8 || align == 9); + + int origin_x = align_offset_x(parts->origin_mode, total_w); + int origin_y = align_offset_y(parts->origin_mode, total_h); + int align_x = align_offset_x(align, total_w); + int align_y = align_offset_y(align, total_h); + + int start_x = (is_left ? lb->padding_left : -lb->padding_right) + + (origin_x - align_x); + int start_y = (is_top ? lb->padding_top : -lb->padding_bottom) + + (origin_y - align_y); + + int x_dir = is_right ? -1 : 1; + int y_dir = is_bottom ? -1 : 1; + + int cur_x = start_x; + int cur_y = start_y; + int max_cross = 0; + + struct parts *child; + PARTS_FOREACH_CHILD(child, parts) { + int child_w, child_h; + parts_get_layout_size(child, &child_w, &child_h); + int cw = child->margin_left + child_w + child->margin_right; + int ch = child->margin_top + child_h + child->margin_bottom; + + // The layout box forces the child's origin_mode to match its own align. + if (child->origin_mode != align) { + child->origin_mode = align; + parts_recalculate_hitbox(child); + } + + if (lb->layout_type == PARTS_LAYOUT_HORIZONTAL) { + if (lb->wrap && abs((cur_x + cw * x_dir) - start_x) > lb->wrap_size + && max_cross > 0) { + cur_y += max_cross * y_dir; + max_cross = 0; + cur_x = start_x; + } + } else { + if (lb->wrap && abs((cur_y + ch * y_dir) - start_y) > lb->wrap_size + && max_cross > 0) { + cur_x += max_cross * x_dir; + max_cross = 0; + cur_y = start_y; + } + } + + // margin offset depends on alignment direction + int mx = is_left ? child->margin_left : is_right ? -child->margin_right : 0; + int my = is_top ? child->margin_top : is_bottom ? -child->margin_bottom : 0; + + int pos_x = cur_x + mx; + int pos_y = cur_y + my; + if (child->local.pos.x != pos_x || child->local.pos.y != pos_y) { + child->local.pos.x = pos_x; + child->local.pos.y = pos_y; + parts_recalculate_hitbox(child); + parts_component_dirty(child); + } + + if (lb->layout_type == PARTS_LAYOUT_HORIZONTAL) { + cur_x += cw * x_dir; + if (ch > max_cross) + max_cross = ch; + } else { + cur_y += ch * y_dir; + if (cw > max_cross) + max_cross = cw; + } + } +} diff --git a/src/parts/parts.c b/src/parts/parts.c index 91ebbf2..196dba4 100644 --- a/src/parts/parts.c +++ b/src/parts/parts.c @@ -174,6 +174,8 @@ static void parts_state_free(struct parts_state *state) { switch (state->type) { case PARTS_UNINITIALIZED: + case PARTS_RECT_DETECTION: + case PARTS_LAYOUT_BOX: break; case PARTS_CG: gfx_delete_texture(&state->common.texture); @@ -213,8 +215,6 @@ static void parts_state_free(struct parts_state *state) if (state->movie.sprite_no >= 0) sact_SP_Delete(state->movie.sprite_no); break; - case PARTS_RECT_DETECTION: - break; } memset(state, 0, sizeof(struct parts_state)); } @@ -266,6 +266,10 @@ void parts_state_reset(struct parts_state *state, enum parts_type type) case PARTS_MOVIE: state->movie.sprite_no = -1; break; + case PARTS_LAYOUT_BOX: + state->layout_box.layout_type = PARTS_LAYOUT_VERTICAL; + state->layout_box.align = 1; + break; } } @@ -349,6 +353,14 @@ struct parts_movie *parts_get_movie(struct parts *parts, int state) return &parts->states[state].movie; } +struct parts_layout_box *parts_get_layout_box(struct parts *parts) +{ + if (parts->states[0].type != PARTS_LAYOUT_BOX) { + parts_state_reset(&parts->states[0], PARTS_LAYOUT_BOX); + } + return &parts->states[0].layout_box; +} + static Point calculate_offset(int mode, int w, int h) { switch (mode) { @@ -1077,6 +1089,8 @@ static void parts_update_component(struct parts *parts) parts->dirty = false; } + parts_do_layout(parts); + struct parts *child; PARTS_FOREACH_CHILD(child, parts) { parts_update_component(child); @@ -1099,6 +1113,10 @@ void PE_UpdateComponent(possibly_unused int passed_time) } parts->parent = parent; TAILQ_INSERT_TAIL(&parent->children, parts, child_list_entry); + + // if parent is layout box, mark it dirty so that it can re-layout its children + if (parent->states[0].type == PARTS_LAYOUT_BOX) + parts_component_dirty(parent); } // TODO: should the child be orphaned if it already has a parent and an invalid // parent no is given? @@ -1931,6 +1949,7 @@ void PE_SetComponentType(int parts_no, int type, int state) struct parts *parts = parts_get(parts_no); enum parts_type pt = PARTS_UNINITIALIZED; switch (type) { + case 8: pt = PARTS_LAYOUT_BOX; break; case 11: pt = PARTS_CG; break; case 12: pt = PARTS_ANIMATION; break; case 13: pt = PARTS_TEXT; break; @@ -1957,6 +1976,7 @@ int PE_GetComponentType(int parts_no, int state) return -1; switch (parts->states[state].type) { + case PARTS_LAYOUT_BOX: return 8; case PARTS_UNINITIALIZED: // defaluts to CG case PARTS_CG: return 11; diff --git a/src/parts/parts_internal.h b/src/parts/parts_internal.h index 8e02750..c073d9d 100644 --- a/src/parts/parts_internal.h +++ b/src/parts/parts_internal.h @@ -106,7 +106,8 @@ enum parts_type { PARTS_FLAT, PARTS_MOVIE, PARTS_RECT_DETECTION, -#define PARTS_NR_TYPES (PARTS_RECT_DETECTION+1) + PARTS_LAYOUT_BOX, +#define PARTS_NR_TYPES (PARTS_LAYOUT_BOX+1) }; struct parts_common { @@ -320,6 +321,29 @@ struct parts_movie { int sprite_no; // SACT sprite number used as movie render target }; +enum parts_layout_type { + PARTS_LAYOUT_FREE = 0, // no automatic layout + PARTS_LAYOUT_VERTICAL = 1, + PARTS_LAYOUT_HORIZONTAL = 2, +}; + +// In AliceSoft's PartsEngine implementation, LayoutBox is a component type +// that is NOT per-state. We store it per-state for uniformity with other +// component types, but parts_get_layout_box() and all code in layoutbox.c +// operate on states[0] only. This is safe as long as game scripts never +// convert a LayoutBox parts to/from another component type. +struct parts_layout_box { + struct parts_common common; + enum parts_layout_type layout_type; + bool wrap; + int wrap_size; + int align; + int padding_top; + int padding_bottom; + int padding_left; + int padding_right; +}; + struct parts_state { enum parts_type type; union { @@ -333,6 +357,7 @@ struct parts_state { struct parts_flash flash; struct parts_flat flat; struct parts_movie movie; + struct parts_layout_box layout_box; }; }; @@ -379,6 +404,10 @@ struct parts { int draw_filter; bool message_window; int alpha_clipper_parts_no; + int margin_top; + int margin_bottom; + int margin_left; + int margin_right; struct parts_motion_list motion; int controller_no; }; @@ -414,6 +443,7 @@ struct parts_construction_process *parts_get_construction_process(struct parts * struct parts_flash *parts_get_flash(struct parts *parts, int state); struct parts_flat *parts_get_flat(struct parts *parts, int state); struct parts_movie *parts_get_movie(struct parts *parts, int state); +struct parts_layout_box *parts_get_layout_box(struct parts *parts); void parts_set_pos(struct parts *parts, Point pos); void parts_set_global_pos(Point pos); void parts_set_dims(struct parts *parts, struct parts_common *common, int w, int h); @@ -508,6 +538,9 @@ bool parts_flat_load(struct parts *parts, struct parts_flat *f, struct string *f bool parts_flat_update(struct parts_flat *f, int passed_time); int parts_flat_find_library(struct flat *fl, const char *name); +// layoutbox.c +void parts_do_layout(struct parts *parts); + // debug.c struct sprite; void parts_debug_init(void); @@ -519,14 +552,4 @@ static inline bool parts_state_valid(int state) return state >= 0 && state <= 2; } -static inline int parts_get_width(struct parts *parts) -{ - return parts->states[parts->state].common.w; -} - -static inline int parts_get_height(struct parts *parts) -{ - return parts->states[parts->state].common.h; -} - #endif /* SYSTEM4_PARTS_INTERNAL_H */ diff --git a/src/parts/render.c b/src/parts/render.c index a926a30..7282061 100644 --- a/src/parts/render.c +++ b/src/parts/render.c @@ -411,6 +411,8 @@ void parts_render(struct parts *parts) struct parts_state *state = &parts->states[parts->state]; switch (state->type) { case PARTS_UNINITIALIZED: + case PARTS_RECT_DETECTION: + case PARTS_LAYOUT_BOX: break; case PARTS_CG: case PARTS_ANIMATION: @@ -431,8 +433,6 @@ void parts_render(struct parts *parts) case PARTS_FLAT: parts_render_flat(parts, &state->flat); break; - case PARTS_RECT_DETECTION: - break; } } diff --git a/src/parts/save.c b/src/parts/save.c index 991bf90..71e6d5d 100644 --- a/src/parts/save.c +++ b/src/parts/save.c @@ -350,6 +350,30 @@ static void load_parts_flat(struct iarray_reader *r, struct parts *parts, flat->needs_advance = true; } +static void save_parts_layout_box(struct iarray_writer *w, struct parts_layout_box *lb) +{ + iarray_write(w, lb->layout_type); + iarray_write(w, lb->wrap); + iarray_write(w, lb->wrap_size); + iarray_write(w, lb->align); + iarray_write(w, lb->padding_top); + iarray_write(w, lb->padding_bottom); + iarray_write(w, lb->padding_left); + iarray_write(w, lb->padding_right); +} + +static void load_parts_layout_box(struct iarray_reader *r, struct parts_layout_box *lb) +{ + lb->layout_type = iarray_read(r); + lb->wrap = iarray_read(r); + lb->wrap_size = iarray_read(r); + lb->align = iarray_read(r); + lb->padding_top = iarray_read(r); + lb->padding_bottom = iarray_read(r); + lb->padding_left = iarray_read(r); + lb->padding_right = iarray_read(r); +} + static void save_parts_state(struct iarray_writer *w, struct parts_state *state) { iarray_write(w, state->type); @@ -360,6 +384,8 @@ static void save_parts_state(struct iarray_writer *w, struct parts_state *state) iarray_write_rectangle(w, &state->common.surface_area); switch (state->type) { case PARTS_UNINITIALIZED: + case PARTS_MOVIE: + case PARTS_RECT_DETECTION: break; case PARTS_CG: save_parts_cg(w, &state->cg); @@ -386,8 +412,8 @@ static void save_parts_state(struct iarray_writer *w, struct parts_state *state) case PARTS_FLAT: save_parts_flat(w, &state->flat); break; - case PARTS_MOVIE: - case PARTS_RECT_DETECTION: + case PARTS_LAYOUT_BOX: + save_parts_layout_box(w, &state->layout_box); break; } } @@ -403,6 +429,8 @@ static void load_parts_state(struct iarray_reader *r, struct parts *parts, iarray_read_rectangle(r, &state->common.surface_area); switch (state->type) { case PARTS_UNINITIALIZED: + case PARTS_MOVIE: + case PARTS_RECT_DETECTION: break; case PARTS_CG: load_parts_cg(r, parts, &state->cg); @@ -431,8 +459,8 @@ static void load_parts_state(struct iarray_reader *r, struct parts *parts, case PARTS_FLAT: load_parts_flat(r, parts, &state->flat); break; - case PARTS_MOVIE: - case PARTS_RECT_DETECTION: + case PARTS_LAYOUT_BOX: + load_parts_layout_box(r, &state->layout_box); break; } } @@ -538,6 +566,10 @@ static void save_parts(struct iarray_writer *w, struct parts *parts) iarray_write(w, parts->controller_no); iarray_write(w, parts->pass_cursor); iarray_write(w, parts->lock_input_state); + iarray_write(w, parts->margin_top); + iarray_write(w, parts->margin_bottom); + iarray_write(w, parts->margin_left); + iarray_write(w, parts->margin_right); } unsigned motion_count_pos = iarray_writer_pos(w); @@ -584,7 +616,12 @@ static void load_parts(struct iarray_reader *r, int version) parts->controller_no = iarray_read(r); parts->pass_cursor = iarray_read(r); parts->lock_input_state = iarray_read(r); + parts->margin_top = iarray_read(r); + parts->margin_bottom = iarray_read(r); + parts->margin_left = iarray_read(r); + parts->margin_right = iarray_read(r); } + int motion_count = iarray_read(r); for (int i = 0; i < motion_count; i++) { struct parts_motion *motion = load_parts_motion(r);