diff --git a/objects/obj_controller/Step_1.gml b/objects/obj_controller/Step_1.gml index e8292e24cd..4f1add4565 100644 --- a/objects/obj_controller/Step_1.gml +++ b/objects/obj_controller/Step_1.gml @@ -10,8 +10,3 @@ obj_cursor.image_yscale=1; - - - - - diff --git a/objects/obj_tooltip/Step_1.gml b/objects/obj_tooltip/Step_1.gml index a57267badf..851a02df7b 100644 --- a/objects/obj_tooltip/Step_1.gml +++ b/objects/obj_tooltip/Step_1.gml @@ -2,4 +2,4 @@ for (var i=0;i0){ @@ -26,6 +39,24 @@ function pop_draw_return_values(){ } +// -------------------- +// 🟩 UI ELEMENTS +// -------------------- + + +/// @function ReactiveString(text, x1, y1, data) +/// @constructor +/// @category UI +/// @description Represents a reactive text element that can update, draw itself, and respond to hits. +/// @param {string} text The text to display. +/// @param {real} [x1=0] The X position. +/// @param {real} [y1=0] The Y position. +/// @param {struct|bool} [data=false] Optional struct of properties to apply. +/// @returns {ReactiveString} +/// +/// @example +/// var rs = new ReactiveString("Hello", 100, 200); +/// rs.draw(); function ReactiveString(text,x1=0,y1=0,data = false) constructor{ self.x1 = x1; self.y1 = y1; @@ -87,8 +118,16 @@ function ReactiveString(text,x1=0,y1=0,data = false) constructor{ } } - -//position, icon, text, text_max_width, tooltip, text_position, font = fnt_40k_14, colour = CM_GREEN_COLOR +/// @function LabeledIcon(icon, text, x1, y1, data) +/// @constructor +/// @category UI +/// @description UI element combining a sprite and text with optional tooltip. +/// @param {sprite} icon The sprite asset. +/// @param {string} text The text label. +/// @param {real} [x1=0] X position. +/// @param {real} [y1=0] Y position. +/// @param {struct|bool} [data=false] Optional struct of properties to apply. +/// @returns {LabeledIcon} function LabeledIcon(icon, text,x1=0,y1=0,data = false) constructor{ self.x1 = x1; @@ -151,11 +190,32 @@ function LabeledIcon(icon, text,x1=0,y1=0,data = false) constructor{ } + +/// @function draw_sprite_as_button(position, choice_sprite, scale, hover_sprite) +/// @description Draws a sprite as a clickable button, returning its bounding box. +/// @param {array} position [x, y] top-left corner. +/// @param {sprite} choice_sprite Sprite to draw. +/// @param {array} [scale=[1,1]] Scale factors [x,y]. +/// @param {sprite} [hover_sprite=-1] Optional hover sprite. +/// @returns {array} [x1, y1, x2, y2] bounding box. function draw_sprite_as_button(position, choice_sprite, scale = [1,1], hover_sprite = -1){ var _pos = [position[0],position[1], position[0]+(sprite_get_width(choice_sprite)*scale[0]), position[1] + (sprite_get_height(choice_sprite)*scale[1])]; draw_sprite_ext(choice_sprite,0,position[0],position[1], scale[0], scale[1], 0, c_white, scr_hit(_pos) ? 1 : 0.9); return _pos; } + +/// @function draw_unit_buttons(position, text, size_mod, colour, halign, font, alpha_mult, bg, bg_color) +/// @description Draws a styled button with text, optional background and hover effects. +/// @param {array} position Either [x, y] or [x1, y1, x2, y2]. +/// @param {string} text Text to display. +/// @param {array} [size_mod=[1.5,1.5]] Text scaling. +/// @param {color} [colour=c_gray] Text color. +/// @param {real} [_halign=fa_center] Text horizontal alignment. +/// @param {font} [font=fnt_40k_14b] Font resource. +/// @param {real} [alpha_mult=1] Alpha multiplier. +/// @param {bool} [bg=false] Draw background rectangle. +/// @param {color} [bg_color=c_black] Background color. +/// @returns {array} [x1, y1, x2, y2] bounding box. function draw_unit_buttons(position, text, size_mod=[1.5,1.5],colour=c_gray,_halign=fa_center, font=fnt_40k_14b, alpha_mult=1, bg=false, bg_color=c_black){ // TODO: fix halign usage // Store current state of all global vars @@ -200,7 +260,12 @@ function draw_unit_buttons(position, text, size_mod=[1.5,1.5],colour=c_gray,_hal } -//object containing draw_unit_buttons +/// @function UnitButtonObject(data) +/// @constructor +/// @category UI +/// @description Represents an interactive UI button with styles, tooltips, and binding support. +/// @param {struct|bool} [data=false] Initial property overrides. +/// @returns {UnitButtonObject} function UnitButtonObject(data = false) constructor{ x1 = 0; y1 = 0; @@ -336,6 +401,13 @@ function UnitButtonObject(data = false) constructor{ } } + +/// @function PurchaseButton(req) +/// @constructor +/// @category UI +/// @description Specialized UnitButtonObject requiring requisition points to click. +/// @param {real} req Required requisition cost. +/// @returns {PurchaseButton} function PurchaseButton(req) : UnitButtonObject() constructor{ req_value = req; static draw = function(allow_click=true){ @@ -417,6 +489,17 @@ function slider_bar() constructor{ pop_draw_return_values(); } } + + +/// @function TextBarArea(XX, YY, Max_width, requires_input) +/// @constructor +/// @category UI +/// @description Input text area with background and cursor handling. +/// @param {real} XX X position. +/// @param {real} YY Y position. +/// @param {real} [Max_width=400] Max width of text bar. +/// @param {bool} [requires_input=false] If true, input is required. +/// @returns {TextBarArea} function TextBarArea(XX,YY,Max_width = 400, requires_input = false) constructor{ allow_input=false; self.requires_input = requires_input; @@ -493,6 +576,15 @@ function TextBarArea(XX,YY,Max_width = 400, requires_input = false) constructor{ } + +/// @function drop_down(selection, draw_x, draw_y, options, open_marker) +/// @description Renders a drop-down selection list and updates choice. +/// @param {string} selection Current selected option. +/// @param {real} draw_x X position. +/// @param {real} draw_y Y position. +/// @param {array} options List of string options. +/// @param {bool} open_marker Whether dropdown is currently open. +/// @returns {array} [new_selection, open_marker] function drop_down(selection, draw_x, draw_y, options,open_marker){ add_draw_return_values(); if (selection!=""){ @@ -531,6 +623,15 @@ function drop_down(selection, draw_x, draw_y, options,open_marker){ pop_draw_return_values(); } + +/// @function MultiSelect(options_array, title, data) +/// @constructor +/// @category UI +/// @description Multi-option toggle group allowing multiple selections. +/// @param {array} options_array Array of option labels. +/// @param {string} title Title string. +/// @param {struct} [data={}] Optional overrides. +/// @returns {MultiSelect} function MultiSelect(options_array, title, data = {})constructor{ self.title = title; x_gap = 10; @@ -621,12 +722,27 @@ function MultiSelect(options_array, title, data = {})constructor{ } } + +/// @function item_data_updater(data) +/// @description Utility to copy struct data into `self`. +/// @param {struct} data Data to apply. +/// @returns {undefined} function item_data_updater(data){ var _data_presets = struct_get_names(data); for (var i=0;i 0 && string_length(draw_title) < max_width){ - if (draw_title){ - draw_text(x1+(max_width/2) - (string_length(draw_title)/2), y1, title); - } - } else { - if (draw_title){ - draw_text(x1 + (string_length(draw_title)/2), y1, title); - } - } + static draw_option = function (_x, _y, index) { + var _cur_opt = toggles[index]; + _cur_opt.x1 = _x; + _cur_opt.y1 = _y; + _cur_opt.update(); + _cur_opt.active = (index == current_selection); + _cur_opt.button_color = _cur_opt.active ? active_col : inactive_col; + return _cur_opt; + }; - changed = false; - var _start_current_selection = current_selection; - var _prev_x = x1; - var _prev_y = y1+string_height(title)+10; - var items_on_row = 0; - for (var i=0;i x2 ? _prev_x : x2; - y2 = _prev_y + _cur_opt.height; - if (max_width>0){ - if (_prev_x - x1 > max_width){ - _prev_x = x1; - _prev_y += _cur_opt.height+y_gap; - items_on_row = 0; - } - } - } - if (_start_current_selection != current_selection){ - changed = true; - } - pop_draw_return_values(); - } + draw_set_valign(fa_top); + draw_set_color(active_col); + draw_set_font(title_font); + draw_set_alpha(1); - static selection_val = function(value){ - if (current_selection == -1){ - return noone; - } - return toggles[current_selection][$value]; - } + var title_h = 0; + if (draw_title) { + if (max_width > 0) { + draw_set_halign(fa_center); + draw_text(x1 + max_width * 0.5, y1, title); + } else { + draw_set_halign(fa_left); + draw_text(x1, y1, title); + } + title_h = string_height(title) + 10; + } + + changed = false; + var _start_current_selection = current_selection; + + var _prev_x = x1; + var _prev_y = y1 + title_h; + + var row_items = []; // holds structs: { btn: , idx: } + var row_width = 0; + var row_height = 0; + + for (var i = 0; i < array_length(toggles); i++) { + var _cur_opt = draw_option(_prev_x, _prev_y, i); + + _prev_x = _cur_opt.x2 + x_gap; + row_width = _prev_x - x1; + row_height = max(row_height, _cur_opt.height); + + var row_full = (max_width > 0) && (row_width > max_width); + var last_item = (i == array_length(toggles) - 1); + + array_push(row_items, { btn: _cur_opt, idx: i }); + + if (row_full || last_item) { + // Calculate final row width and optional centering offset + var _first_btn = row_items[0].btn; + var _last_btn = row_items[array_length(row_items) - 1].btn; + + var _total_row_width = _last_btn.x2 - _first_btn.x1; + var _container_width = (max_width > 0) ? max_width : _total_row_width; + var _offset_x = center ? (_container_width - _total_row_width) * 0.5 : 0; + + // Draw row items at their final positions + for (var j = 0; j < array_length(row_items); j++) { + var btn = row_items[j].btn; + var idx = row_items[j].idx; + btn.x1 += _offset_x; // shift to center + btn.update(); + btn.draw(); + if (btn.clicked() && allow_changes) { + current_selection = idx; // <-- no array_index_of needed + } + } + + // Advance to next row + var row_right_edge = x1 + max(_container_width, _total_row_width); + x2 = max(x2, row_right_edge); + _prev_x = x1; + _prev_y += row_height + y_gap; + y2 = _prev_y; + + // Reset accumulators + row_items = []; + row_width = 0; + row_height = 0; + } + } + + if (_start_current_selection != current_selection) { + changed = true; + } + pop_draw_return_values(); + }; + + static selection_val = function (value) { + if (current_selection == -1) return noone; + return toggles[current_selection][$value]; + }; } + +/// @function ToggleButton(data) +/// @constructor +/// @category UI +/// @description A toggleable button element with hover and active states. +/// @param {struct} [data={}] Initial properties. +/// @returns {ToggleButton} function ToggleButton(data={}) constructor { x1 = 0; y1 = 0; @@ -835,6 +997,14 @@ function ToggleButton(data={}) constructor { }; } + + +/// @function InteractiveButton(data) +/// @constructor +/// @category UI +/// @description A button with separate active/inactive tooltips and click sounds. +/// @param {struct} [data={}] Initial properties. +/// @returns {InteractiveButton} function InteractiveButton(data={}) constructor { x1 = 0; y1 = 0; @@ -922,6 +1092,14 @@ function InteractiveButton(data={}) constructor { }; } + +/// @function list_traveler(list, cur_val, move_up_coords, move_down_coords) +/// @description Cycles through values in a list by clicking move-up/down regions. +/// @param {array} list Array of values. +/// @param {any} cur_val Current value. +/// @param {array} move_up_coords Bounding box for up button. +/// @param {array} move_down_coords Bounding box for down button. +/// @returns {any} New value from list. function list_traveler(list, cur_val, move_up_coords, move_down_coords){ var _new_val = cur_val; var _found = false; diff --git a/scripts/scr_chapter_new/scr_chapter_new.gml b/scripts/scr_chapter_new/scr_chapter_new.gml index 39ca5ef319..f572f3f53c 100644 --- a/scripts/scr_chapter_new/scr_chapter_new.gml +++ b/scripts/scr_chapter_new/scr_chapter_new.gml @@ -189,13 +189,12 @@ function ChapterData() constructor { /// @mixin obj_creation /// @description called when a chapter's icon is clicked on the first page after the main menu. /// used to set up initialise the data that is later fed into `scr_initialize_custom` when the game starts -function scr_chapter_new(argument0) { +function scr_chapter_new(chapter_identifier) { full_liveries = ""; // until chapter objects are in full use kicks off livery propogation company_liveries = ""; - // argument0 = chapter obj_creation.use_chapter_object = false; // for the new json testing var chapter_id = eCHAPTERS.UNKNOWN; @@ -216,7 +215,8 @@ function scr_chapter_new(argument0) { world_feature = array_create(20, ""); - points=100;maxpoints=100; + points=100; + maxpoints=100; function load_default_gear(_role_id, _role_name, _wep1, _wep2, _armour, _mobi, _gear){ for(var i = 100; i <=102; i++){ @@ -249,7 +249,7 @@ function scr_chapter_new(argument0) { for(var c = 0; c < array_length(obj_creation.all_chapters); c++){ - if(argument0 == obj_creation.all_chapters[c].name && obj_creation.all_chapters[c].json == true){ + if(chapter_identifier == obj_creation.all_chapters[c].name && obj_creation.all_chapters[c].json == true){ obj_creation.use_chapter_object = true; chapter_id = obj_creation.all_chapters[c].id; } @@ -260,30 +260,32 @@ function scr_chapter_new(argument0) { var chapter_obj = new ChapterData(); var successfully_loaded = chapter_obj.load_from_json(chapter_id); if(!successfully_loaded){ - var issue = $"No json file exists for chapter id {chapter_id} and name {argument0}"; + var issue = $"No json file exists for chapter id {chapter_id} and name {chapter_identifier}"; // log_error(issue); scr_popup("Error Loading Chapter", issue, "debug"); return false; } global.chapter_creation_object = chapter_obj; + maxpoints=150; } #region Custom Chapter //generates custom chapter if it exists - if (is_real(argument0) && argument0 >= eCHAPTERS.CUSTOM_1 && argument0 <= eCHAPTERS.CUSTOM_10){ + if (is_real(chapter_identifier) && chapter_identifier >= eCHAPTERS.CUSTOM_1 && chapter_identifier <= eCHAPTERS.CUSTOM_10){ obj_creation.use_chapter_object = true; var chapter_obj = new ChapterData(); - var successfully_loaded = chapter_obj.load_from_json(argument0, true); + var successfully_loaded = chapter_obj.load_from_json(chapter_identifier, true); if(!successfully_loaded){ - var issue = $"No json file exists for chapter id {argument0} and name {argument0}"; + var issue = $"No json file exists for chapter id {chapter_identifier} and name {chapter_identifier}"; log_error(issue); scr_popup("Error Loading Chapter", issue, "debug"); return false; } global.chapter_creation_object = chapter_obj; + maxpoints=100; } #endregion @@ -301,10 +303,6 @@ function scr_chapter_new(argument0) { global.chapter_icon.name = chapter_object.icon_name; obj_creation.fleet_type = chapter_object.fleet_type; - obj_creation.strength = chapter_object.strength; - obj_creation.purity = chapter_object.purity; - obj_creation.stability = chapter_object.stability; - obj_creation.cooperation = chapter_object.cooperation; obj_creation.homeworld_exists = chapter_object.homeworld_exists; obj_creation.homeworld = chapter_object.homeworld; @@ -321,8 +319,6 @@ function scr_chapter_new(argument0) { obj_creation.buttons.home_planets.current_selection = chapter_object.home_planets ??1; obj_creation.aspirant_trial = trial_map(chapter_object.aspirant_trial); - obj_creation.adv = chapter_object.advantages; - obj_creation.dis = chapter_object.disadvantages; obj_creation.buttons.culture_styles.set(chapter_object.culture_styles); obj_creation.full_liveries = chapter_object.full_liveries; @@ -498,37 +494,41 @@ function scr_chapter_new(argument0) { if(struct_exists(chapter_object, "custom_advisors")){ obj_creation.custom_advisors = chapter_object.custom_advisors; } - - - - - points = chapter_object.points; - maxpoints=chapter_object.points; - - } - - - - - - - for(var a = 0; a < array_length(adv); a++){ - for(var k = 0; k < array_length(obj_creation.all_advantages); k++){ - if(adv[a]!="" && obj_creation.all_advantages[k].name=adv[a]){ - adv_num[a] = k; + // Validate and clamp trait values to sane ranges (defaulting if missing/invalid) + obj_creation.strength = clamp(is_real(chapter_object.strength) ? chapter_object.strength : 5, 0, 10); + obj_creation.purity = clamp(is_real(chapter_object.purity) ? chapter_object.purity : 5, 0, 10); + obj_creation.stability = clamp(is_real(chapter_object.stability) ? chapter_object.stability : 50, 0, 100); + obj_creation.cooperation = clamp(is_real(chapter_object.cooperation) ? chapter_object.cooperation : 5, 0, 10); + points = 0; + + points += (obj_creation.strength-5)*10; + points += (obj_creation.purity-5)*10; + points += (obj_creation.stability-50); + points += (obj_creation.cooperation-5)*10; + + var _open_adv = 0; + for (var i=0;i