|
|
|
|
@ -326,29 +326,25 @@ void render_body_info_ui(SimulationState* sim, RenderState* render_state, UIStat
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Render maneuver list UI panel
|
|
|
|
|
void render_maneuver_list_ui(SimulationState* sim, RenderState* render_state, UIState* ui_state) { |
|
|
|
|
if (ui_state->selected_craft_index < 0 || ui_state->selected_craft_index >= sim->craft_count) { |
|
|
|
|
return; // No spacecraft selected
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int panel_width = MANEUVER_LIST_WIDTH; |
|
|
|
|
int panel_height = MANEUVER_LIST_HEIGHT; |
|
|
|
|
int panel_x = GetScreenWidth() - panel_width - 10; |
|
|
|
|
int panel_y = MANEUVER_LIST_Y_OFFSET; // Below info panel
|
|
|
|
|
int panel_y = MANEUVER_LIST_Y_OFFSET; |
|
|
|
|
|
|
|
|
|
Rectangle panel_bounds = { (float)panel_x, (float)panel_y, (float)panel_width, (float)panel_height }; |
|
|
|
|
Rectangle panel_bounds = {(float)panel_x, (float)panel_y, (float)panel_width, (float)panel_height}; |
|
|
|
|
|
|
|
|
|
Spacecraft* craft = &sim->spacecraft[ui_state->selected_craft_index]; |
|
|
|
|
|
|
|
|
|
// Create title with spacecraft name
|
|
|
|
|
char title[128]; |
|
|
|
|
snprintf(title, sizeof(title), "Maneuvers: %s", craft->name); |
|
|
|
|
|
|
|
|
|
// Draw window
|
|
|
|
|
GuiWindowBox(panel_bounds, title); |
|
|
|
|
|
|
|
|
|
// Count maneuvers for this spacecraft
|
|
|
|
|
int craft_maneuver_count = 0; |
|
|
|
|
for (int i = 0; i < sim->maneuver_count; i++) { |
|
|
|
|
if (sim->maneuvers[i].craft_index == ui_state->selected_craft_index) { |
|
|
|
|
@ -357,7 +353,6 @@ void render_maneuver_list_ui(SimulationState* sim, RenderState* render_state, UI
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (craft_maneuver_count == 0) { |
|
|
|
|
// No maneuvers for this spacecraft
|
|
|
|
|
Rectangle text_bounds = { |
|
|
|
|
(float)panel_x + 8, |
|
|
|
|
(float)panel_y + 25, |
|
|
|
|
@ -366,14 +361,8 @@ void render_maneuver_list_ui(SimulationState* sim, RenderState* render_state, UI
|
|
|
|
|
}; |
|
|
|
|
GuiLabel(text_bounds, "No maneuvers defined"); |
|
|
|
|
|
|
|
|
|
// Add button to open maneuver dialog
|
|
|
|
|
Rectangle dialog_btn = { |
|
|
|
|
(float)panel_x + 8, |
|
|
|
|
(float)panel_y + (float)panel_height - 28, |
|
|
|
|
(float)panel_width - 16, |
|
|
|
|
20 |
|
|
|
|
}; |
|
|
|
|
if (GuiButton(dialog_btn, "[+] Manage Maneuvers")) { |
|
|
|
|
Rectangle create_btn = {(float)panel_x + 8, (float)panel_y + (float)panel_height - 28, (float)panel_width - 16, 20}; |
|
|
|
|
if (GuiButton(create_btn, "[+] Create Maneuver")) { |
|
|
|
|
ui_state->maneuver_dialog.open = true; |
|
|
|
|
ui_state->maneuver_dialog.active_tab = MD_TAB_CREATE; |
|
|
|
|
ui_state->maneuver_dialog.craft_index = ui_state->selected_craft_index; |
|
|
|
|
@ -387,79 +376,104 @@ void render_maneuver_list_ui(SimulationState* sim, RenderState* render_state, UI
|
|
|
|
|
ui_state->maneuver_dialog.show_error = false; |
|
|
|
|
ui_state->maneuver_dialog.error_message[0] = '\0'; |
|
|
|
|
ui_state->maneuver_dialog.show_delete_confirm = false; |
|
|
|
|
ui_state->selected_maneuver_index = -1; |
|
|
|
|
} |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Create maneuver list string for raygui
|
|
|
|
|
int buffer_size = craft_maneuver_count * (64 + 10 + 1) + 1; |
|
|
|
|
char* maneuver_list_text = (char*)malloc(buffer_size); |
|
|
|
|
if (!maneuver_list_text) return; |
|
|
|
|
|
|
|
|
|
maneuver_list_text[0] = '\0'; |
|
|
|
|
int offset = 0; |
|
|
|
|
int maneuver_indices[craft_maneuver_count]; |
|
|
|
|
int maneuver_index_count = 0; |
|
|
|
|
|
|
|
|
|
for (int i = 0; i < sim->maneuver_count; i++) { |
|
|
|
|
if (sim->maneuvers[i].craft_index == ui_state->selected_craft_index) { |
|
|
|
|
maneuver_indices[maneuver_index_count++] = i; |
|
|
|
|
if (offset > 0) { |
|
|
|
|
offset += snprintf(maneuver_list_text + offset, buffer_size - offset, ";"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const char* status_char = sim->maneuvers[i].executed ? "[DONE]" : "[PENDING]"; |
|
|
|
|
offset += snprintf(maneuver_list_text + offset, buffer_size - offset, "%s %s", |
|
|
|
|
status_char, sim->maneuvers[i].name); |
|
|
|
|
status_char, sim->maneuvers[i].name); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Draw list view (inside window, leave space for title bar)
|
|
|
|
|
Rectangle list_bounds = { |
|
|
|
|
(float)panel_x + 4, |
|
|
|
|
(float)panel_y + 25, |
|
|
|
|
(float)panel_width - 8, |
|
|
|
|
(float)panel_height - 100 // Leave space for details below
|
|
|
|
|
(float)panel_height - 280 |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
int maneuver_scroll = 0; |
|
|
|
|
int maneuver_active = -1; |
|
|
|
|
GuiListView(list_bounds, maneuver_list_text, &maneuver_scroll, &maneuver_active); |
|
|
|
|
int previous_active = ui_state->maneuver_list_active; |
|
|
|
|
GuiListView(list_bounds, maneuver_list_text, &ui_state->maneuver_list_scroll, &ui_state->maneuver_list_active); |
|
|
|
|
|
|
|
|
|
// Show details for first pending or last executed maneuver
|
|
|
|
|
char details_text[512]; |
|
|
|
|
details_text[0] = '\0'; |
|
|
|
|
if (ui_state->maneuver_list_active >= 0 && ui_state->maneuver_list_active != previous_active) { |
|
|
|
|
if (ui_state->maneuver_list_active >= 0 && ui_state->maneuver_list_active < maneuver_index_count) { |
|
|
|
|
ui_state->selected_maneuver_index = maneuver_indices[ui_state->maneuver_list_active]; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (int i = 0; i < sim->maneuver_count; i++) { |
|
|
|
|
if (sim->maneuvers[i].craft_index == ui_state->selected_craft_index) { |
|
|
|
|
if (sim->maneuvers[i].executed) { |
|
|
|
|
snprintf(details_text, sizeof(details_text), |
|
|
|
|
"Last executed:\n%s\nΔv: %.1f m/s\nTime: %.1f s", |
|
|
|
|
sim->maneuvers[i].name, sim->maneuvers[i].delta_v, sim->maneuvers[i].executed_time); |
|
|
|
|
int preview_y = panel_y + panel_height - 250; |
|
|
|
|
|
|
|
|
|
if (ui_state->selected_maneuver_index >= 0 && ui_state->selected_maneuver_index < sim->maneuver_count) { |
|
|
|
|
Maneuver* selected = &sim->maneuvers[ui_state->selected_maneuver_index]; |
|
|
|
|
|
|
|
|
|
if (selected->craft_index == ui_state->selected_craft_index) { |
|
|
|
|
char selected_text[512]; |
|
|
|
|
char status_str[16]; |
|
|
|
|
snprintf(status_str, sizeof(status_str), selected->executed ? "Executed" : "Pending"); |
|
|
|
|
|
|
|
|
|
snprintf(selected_text, sizeof(selected_text), |
|
|
|
|
"Selected: %s\n\nStatus: %s\nDirection: %s\nΔv: %.1f m/s\n", |
|
|
|
|
selected->name, status_str, get_burn_direction_name(selected->direction), selected->delta_v); |
|
|
|
|
|
|
|
|
|
if (selected->trigger_type == TRIGGER_TIME) { |
|
|
|
|
snprintf(selected_text + strlen(selected_text), sizeof(selected_text) - strlen(selected_text), |
|
|
|
|
"Trigger: Time @ %.1f s", selected->trigger_value); |
|
|
|
|
} else { |
|
|
|
|
snprintf(details_text, sizeof(details_text), |
|
|
|
|
"Next pending:\n%s\nΔv: %.1f m/s", |
|
|
|
|
sim->maneuvers[i].name, sim->maneuvers[i].delta_v); |
|
|
|
|
break; // Show first pending
|
|
|
|
|
snprintf(selected_text + strlen(selected_text), sizeof(selected_text) - strlen(selected_text), |
|
|
|
|
"Trigger: True Anomaly @ %.2f rad", selected->trigger_value); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Rectangle preview_text_bounds = { |
|
|
|
|
(float)panel_x + 8, |
|
|
|
|
(float)preview_y, |
|
|
|
|
(float)panel_width - 16, |
|
|
|
|
80 |
|
|
|
|
}; |
|
|
|
|
GuiLabel(preview_text_bounds, selected_text); |
|
|
|
|
|
|
|
|
|
int orbital_preview_y = preview_y + 85; |
|
|
|
|
|
|
|
|
|
ui_state->maneuver_dialog.craft_index = ui_state->selected_craft_index; |
|
|
|
|
ui_state->maneuver_dialog.direction_active = (int)selected->direction; |
|
|
|
|
ui_state->maneuver_dialog.delta_v = selected->delta_v; |
|
|
|
|
|
|
|
|
|
update_orbital_preview(sim, ui_state); |
|
|
|
|
|
|
|
|
|
if (ui_state->maneuver_dialog.show_preview) { |
|
|
|
|
Rectangle orbital_bounds = { |
|
|
|
|
(float)panel_x + 8, |
|
|
|
|
(float)orbital_preview_y, |
|
|
|
|
(float)panel_width - 16, |
|
|
|
|
110 |
|
|
|
|
}; |
|
|
|
|
render_orbital_preview(sim, ui_state, orbital_bounds); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (details_text[0] != '\0') { |
|
|
|
|
Rectangle details_bounds = { |
|
|
|
|
(float)panel_x + 8, |
|
|
|
|
(float)panel_y + (float)panel_height - 70, |
|
|
|
|
(float)panel_width - 16, |
|
|
|
|
65 |
|
|
|
|
}; |
|
|
|
|
GuiLabel(details_bounds, details_text); |
|
|
|
|
} |
|
|
|
|
int button_row_y = panel_y + panel_height - 50; |
|
|
|
|
|
|
|
|
|
// Add button to open maneuver dialog
|
|
|
|
|
Rectangle dialog_btn = { |
|
|
|
|
(float)panel_x + 8, |
|
|
|
|
(float)panel_y + (float)panel_height - 28, |
|
|
|
|
(float)panel_width - 16, |
|
|
|
|
20 |
|
|
|
|
}; |
|
|
|
|
if (GuiButton(dialog_btn, "[+] Manage Maneuvers")) { |
|
|
|
|
int btn_width = (panel_width - 20) / 4; |
|
|
|
|
|
|
|
|
|
Rectangle create_btn = {(float)panel_x + 10, (float)button_row_y, (float)btn_width, 30}; |
|
|
|
|
if (GuiButton(create_btn, "Create")) { |
|
|
|
|
ui_state->maneuver_dialog.open = true; |
|
|
|
|
ui_state->maneuver_dialog.active_tab = MD_TAB_CREATE; |
|
|
|
|
ui_state->maneuver_dialog.craft_index = ui_state->selected_craft_index; |
|
|
|
|
@ -473,6 +487,45 @@ void render_maneuver_list_ui(SimulationState* sim, RenderState* render_state, UI
|
|
|
|
|
ui_state->maneuver_dialog.show_error = false; |
|
|
|
|
ui_state->maneuver_dialog.error_message[0] = '\0'; |
|
|
|
|
ui_state->maneuver_dialog.show_delete_confirm = false; |
|
|
|
|
ui_state->selected_maneuver_index = -1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Rectangle hohmann_btn = {(float)panel_x + 10 + btn_width, (float)button_row_y, (float)btn_width, 30}; |
|
|
|
|
if (GuiButton(hohmann_btn, "Hohmann...")) { |
|
|
|
|
ui_state->maneuver_dialog.open = true; |
|
|
|
|
ui_state->maneuver_dialog.active_tab = MD_TAB_HOHMANN; |
|
|
|
|
ui_state->maneuver_dialog.craft_index = ui_state->selected_craft_index; |
|
|
|
|
ui_state->maneuver_dialog.edit_maneuver_index = -1; |
|
|
|
|
ui_state->maneuver_dialog.name[0] = '\0'; |
|
|
|
|
ui_state->maneuver_dialog.show_preview = false; |
|
|
|
|
ui_state->maneuver_dialog.show_error = false; |
|
|
|
|
ui_state->maneuver_dialog.error_message[0] = '\0'; |
|
|
|
|
ui_state->maneuver_dialog.show_delete_confirm = false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Rectangle edit_btn = {(float)panel_x + 10 + btn_width * 2, (float)button_row_y, (float)btn_width, 30}; |
|
|
|
|
if (GuiButton(edit_btn, "Edit")) { |
|
|
|
|
if (ui_state->selected_maneuver_index >= 0 && ui_state->selected_maneuver_index < sim->maneuver_count) { |
|
|
|
|
Maneuver* selected = &sim->maneuvers[ui_state->selected_maneuver_index]; |
|
|
|
|
if (selected->craft_index == ui_state->selected_craft_index) { |
|
|
|
|
load_maneuver_into_dialog(sim, ui_state, ui_state->selected_maneuver_index); |
|
|
|
|
ui_state->maneuver_dialog.open = true; |
|
|
|
|
ui_state->maneuver_dialog.active_tab = MD_TAB_EDIT; |
|
|
|
|
ui_state->maneuver_dialog.show_delete_confirm = false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Rectangle delete_btn = {(float)panel_x + 10 + btn_width * 3, (float)button_row_y, (float)btn_width, 30}; |
|
|
|
|
if (GuiButton(delete_btn, "Delete")) { |
|
|
|
|
if (ui_state->selected_maneuver_index >= 0 && ui_state->selected_maneuver_index < sim->maneuver_count) { |
|
|
|
|
Maneuver* selected = &sim->maneuvers[ui_state->selected_maneuver_index]; |
|
|
|
|
if (selected->craft_index == ui_state->selected_craft_index) { |
|
|
|
|
remove_maneuver_by_index(sim, ui_state->selected_maneuver_index); |
|
|
|
|
ui_state->selected_maneuver_index = -1; |
|
|
|
|
ui_state->maneuver_list_active = -1; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
free(maneuver_list_text); |
|
|
|
|
|