/datum/action_group /// The hud we're owned by var/datum/hud/owner /// The action buttons we're managing var/list/atom/movable/screen/movable/action_button/actions /// The initial vertical offset of our action buttons var/north_offset = 0 /// The initial horizontal offset of our action buttons var/west_offset = 0 /// The pixel vertical offset of our action buttons var/pixel_north_offset = 0 /// The pixel horizontal offset of our action buttons var/pixel_horiz_offset = 0 /// Max amount of buttons we can have per row /// Indexes at 1 var/column_max = 0 /// How far "ahead" of the first row we start. Lets us "scroll" our rows /// Indexes at 1 var/row_offset = 0 /// How many rows of actions we can have at max before we just stop hiding /// Indexes at 1 var/max_rows = INFINITY /// The screen location we go by var/location /// Our landing screen object var/atom/movable/screen/action_landing/landing /datum/action_group/New(datum/hud/owner) ..() actions = list() src.owner = owner /datum/action_group/Destroy() owner = null QDEL_NULL(landing) QDEL_LIST_CONTENTS(actions) return ..() /datum/action_group/proc/insert_action(atom/movable/screen/action, index) if(action in actions) if(actions[index] == action) return actions -= action // Don't dupe, come on if(!index) index = length(actions) + 1 index = min(length(actions) + 1, index) actions.Insert(index, action) refresh_actions() /datum/action_group/proc/remove_action(atom/movable/screen/action) actions -= action refresh_actions() /datum/action_group/proc/refresh_actions() // We don't use size() here because landings are not canon var/total_rows = ROUND_UP(length(actions) / column_max) total_rows -= max_rows // Lets get the amount of rows we're off from our max row_offset = clamp(row_offset, 0, total_rows) // You're not allowed to offset so far that we have a row of blank space var/button_number = 0 for(var/atom/movable/screen/button in actions) var/postion = ButtonNumberToScreenCoords(button_number) button.screen_loc = postion button_number++ if(landing) var/postion = ButtonNumberToScreenCoords(button_number, landing = TRUE) // Need a good way to count buttons off screen, but allow this to display in the right place if it's being placed with no concern for dropdown landing.screen_loc = postion button_number++ /// Accepts a number represeting our position in the group, indexes at 0 to make the math nicer /datum/action_group/proc/ButtonNumberToScreenCoords(number, landing = FALSE) var/row = round(number / column_max) row -= row_offset // If you're less then 0, you don't get to render, this lets us "scroll" rows ya feel? if(row < 0) return null // Could use >= here, but I think it's worth noting that the two start at different places, since row is based on number here if(row > max_rows - 1) if(!landing) // If you're not a landing, go away please. thx return null // We always want to render landings, even if their action button can't be displayed. // So we set a row equal to the max amount of rows + 1. Willing to overrun that max slightly to properly display the landing spot row = max_rows // Remembering that max_rows indexes at 1, and row indexes at 0 // We're going to need to set our column to match the first item in the last row, so let's set number properly now number = row * column_max var/visual_row = row + north_offset var/coord_row = visual_row ? "-[visual_row]" : "+0" var/visual_column = west_offset + number % column_max var/coord_col = "+[visual_column]" var/coord_col_offset = pixel_horiz_offset + 2 * (number % column_max + 1) var/coord_row_offset = pixel_north_offset + 2 * (1 * row) return "WEST[coord_col]:[coord_col_offset],NORTH[coord_row]:-[coord_row_offset]" /datum/action_group/proc/check_against_view() var/owner_view = owner?.mymob?.client?.view if(!owner_view) return // Unlikey as it is, we may have been changed. Want to start from our target position and fail down column_max = initial(column_max) // Convert our viewer's view var into a workable offset var/list/view_size = view_to_pixels(owner_view) // We're primarially concerned about width here, if someone makes us 1x2000 I wish them a swift and watery death var/furthest_screen_loc = ButtonNumberToScreenCoords(column_max - 1) var/list/offsets = screen_loc_to_offset(furthest_screen_loc, owner_view) if(offsets[1] > world.icon_size && offsets[1] < view_size[1] && offsets[2] > world.icon_size && offsets[2] < view_size[2]) // We're all good return for(column_max in column_max - 1 to 1 step -1) // Yes I could do this by unwrapping ButtonNumberToScreenCoords, but I don't feel like it var/tested_screen_loc = ButtonNumberToScreenCoords(column_max) offsets = screen_loc_to_offset(tested_screen_loc, owner_view) // We've found a valid max length, pack it in if(offsets[1] > world.icon_size && offsets[1] < view_size[1] && offsets[2] > world.icon_size && offsets[2] < view_size[2]) break // Use our newly resized column max refresh_actions() /// Returns the amount of objects we're storing at the moment /datum/action_group/proc/size() var/amount = length(actions) if(landing) amount += 1 return amount /datum/action_group/proc/index_of(atom/movable/screen/get_location) return actions.Find(get_location) /// Generates a landing object that can be dropped on to join this group /datum/action_group/proc/generate_landing() if(landing) return landing = new() landing.set_owner(src) refresh_actions() /// Clears any landing objects we may currently have /datum/action_group/proc/clear_landing() QDEL_NULL(landing) /datum/action_group/proc/update_landing() if(!landing) return landing.refresh_owner() /datum/action_group/proc/scroll(amount) row_offset += amount refresh_actions() /datum/action_group/palette north_offset = 2 column_max = 3 max_rows = 3 location = SCRN_OBJ_IN_PALETTE /datum/action_group/palette/proc/on_scroll(atom/movable/screen/movable/action_button/button, delta_x, delta_y, location, control, params) SIGNAL_HANDLER // COMSIG_ACTION_SCROLLED if(delta_y > 0) owner.palette_actions.scroll(1) else if(delta_y < 0) owner.palette_actions.scroll(-1) /datum/action_group/palette/insert_action(atom/movable/screen/action, index) . = ..() var/atom/movable/screen/button_palette/palette = owner.toggle_palette RegisterSignal(action, COMSIG_ACTION_SCROLLED, PROC_REF(on_scroll)) palette.play_item_added() /datum/action_group/palette/remove_action(atom/movable/screen/action) . = ..() var/atom/movable/screen/button_palette/palette = owner.toggle_palette UnregisterSignal(action, COMSIG_ACTION_SCROLLED) palette.play_item_removed() if(!length(actions)) palette.set_expanded(FALSE) /datum/action_group/palette/refresh_actions() var/atom/movable/screen/button_palette/palette = owner.toggle_palette var/atom/movable/screen/palette_scroll/scroll_down = owner.palette_down var/atom/movable/screen/palette_scroll/scroll_up = owner.palette_up var/actions_above = round((owner.listed_actions.size() - 1) / owner.listed_actions.column_max) north_offset = initial(north_offset) + actions_above palette.screen_loc = ui_action_palette_offset(actions_above) var/action_count = length(owner?.mymob?.actions) var/our_row_count = round((length(actions) - 1) / column_max) if(!action_count) palette.screen_loc = null if(palette.expanded && action_count && our_row_count >= max_rows) scroll_down.screen_loc = ui_palette_scroll_offset(actions_above) scroll_up.screen_loc = ui_palette_scroll_offset(actions_above) else scroll_down.screen_loc = null scroll_up.screen_loc = null return ..() /datum/action_group/palette/ButtonNumberToScreenCoords(number, landing) var/atom/movable/screen/button_palette/palette = owner.toggle_palette if(palette.expanded) return ..() if(!landing) return null // We only render the landing in this case, so we force it to be the second item displayed (Second rather then first since it looks nicer) // Remember the number var indexes at 0 return ..(1 + (row_offset * column_max), landing) /datum/action_group/listed pixel_horiz_offset = 4 pixel_north_offset = 6 column_max = 10 location = SCRN_OBJ_IN_LIST /datum/action_group/listed/refresh_actions() . = ..() owner?.palette_actions.refresh_actions() // We effect them, so we gotta refresh em /datum/action_group/listed/cult north_offset = 11 pixel_north_offset = 2 west_offset = 6 pixel_horiz_offset = -29 column_max = 4 location = SCRN_OBJ_CULT_LIST max_rows = 2