From 067ce881e563881bd7120daeaf85196a1f634e88 Mon Sep 17 00:00:00 2001 From: Tenari Date: Fri, 6 Feb 2026 08:02:52 -0600 Subject: [PATCH] actually rendering a table --- README.md | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/client.c | 48 +++++++++++++++++++- src/shared.h | 37 +++++++++++++++- 3 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..99f0fe5 --- /dev/null +++ b/README.md @@ -0,0 +1,122 @@ +space-trader-multiplayer-boardgame-esque: + - projector shows the star-map with everyone's positions + - players all have a laptop to control their character + - choose a starting ship + - you have 10_000 credits, and the down-payment % of the ship total value affects the interest rate you get + - Ships: + - attributes: + - cargo space + - Alcubierre Drive efficiency + - Passenger space + - Passenger amenities + - smugglers holds + - life-support system + - Sparrow [2, 1, 0, shared-bunk, 1, 0, 1, 100k] + - Dart [5, 8, 2, shared-bunk, 0, 1, 2, 150k] + - Hauler-Prime Z-1 [120, 2, 0, X, 2, 1, 4, 180k] + - NX-400 [15, 4, 8, shared-bunk, 0, 2, 3, 280k] + - CabinRun 40-X [40, 3, 45, shared-bunk, 1, 4, 6, 380k] + - IronBulk X7 [350, 3, 0, X, 3, 2, 8, 420k] + - Solaris-200 [25, 6, 24, shared-bunk, 0, 3, 4, 520k] + - Merchant's Run [180, 5, 12, shared-bunk, 1, 2, 5, 620k] + - Genesis-Long-Haul V2 [100, 4, 80, shared-bunk, 2, 6, 10, 720k] + - Titan-Class HC-3000 [800, 4, 0, X, 3, 3, 15, 890k] + - Steady Hand [420, 6, 25, shared-bunk, 1, 4, 7, 1.1m] + - Pathfinder [60, 6, 65, shared-bunk, 0, 5, 8, 1.4m] + - Ark-Class Transport A1 [250, 5, 95, shared-bunk, 3, 8, 18, 1.6m] + - Starlight [8, 9, 5, private-apartment, 0, 3, 2, 2.1m] + - Leviathan's Route [900, 7, 40, shared-bunk, 2, 6, 12, 2.3m] + - Voyager's Dream [140, 7, 100, shared-bunk, 1, 8, 12, 2.8m] + - Apex-5000 Deluxe [150, 8, 18, private-apartment, 0, 4, 6, 3.2m] + - Aurora Crown [18, 10, 35, private-apartment, 0, 4, 5, 3.8m] + - TerraFreight Luxe-900 [380, 9, 30, private-apartment, 0, 5, 8, 4.5m] + - Odyssey [320, 8, 100, shared-bunk, 2, 10, 20, 5.4m] + - Celestial Palace [55, 9, 85, private-apartment, 0, 7, 10, 6.2m] + - Sovereign Expanse [950, 10, 60, private-apartment, 1, 8, 20, 8.7m] + - Nexus-Elite-7 [120, 10, 100, private-apartment, 0, 9, 14, 9.1m] + - Infinity's Embrace [280, 10, 100, private-apartment, 1, 10, 22, 10m] + - turn-based. Everyone does their trading at the same time, and then flies to the next station + - instead of making journeys take longer, for game-sake, longer journeys just cost more fuel. + - "faster" drives are actually just more fuel efficient + - the prices of goods are supply/demand based on quantity produced/consumed at planets/stations + - the prices/goods available at a place are only visible to those who are in the station + - stations may build "reputation" prices for repeat suppliers/buyers. + - players can trade with each other while at the same station + - certain stations have shipyards, with mortgages available for trade-in/upgrading ships drives/storage capacity, etc. + - all systems have a safety rating, indicating likelyhood of piracy. High risk systems often have higher profitablilty + - AI station trading agent for haggling deal-making? + - transport contracts available at stations randomly. + - in a system, you can fly out to the asteroid belt and try to buy from miners directly, but it's a gamble on even finding any of them. + - game ends when someone pays off their ship's mortgage. + - passengers/VIP transport + - playstyles: + - Mass-Cargo hauler + - Fast-expensive-goods transport (precious metals) + - passenger transport + - commodities: + - Hydrogen (Fuel) + - Oxygen + - Water + - Food: (plants provide seed versions as well) + - Wheat + - rice + - corn + - potatoes + - beef + - chicken + - butter + - olive oil + - alcohol + - fertilizer + - cleaning supplies + - air filters + - Chemicals + - clothes + - textiles + - Electronics + - Construction parts + - 3D printer parts + - clothing fabricator + - hand tools + - machine tools + - cutlery + - robots + - plastics + - hand weapons + - batteries + - solar panels + - medicines + - optical components + - glass + - Metals (Ore-form and Purified-form): + - Lithium + - Berylium + - Magnesium + - Aluminium + - Silicon + - Scandium + - Titanium + - Vanadium + - Chromium + - Manganese + - Iron + - Cobalt + - Nickel + - Copper + - Zinc + - Selenium + - Zirconium + - Molybdenum + - Ruthenium + - Rhodium + - Palladium + - Silver + - Cadmium + - Tin + - Tungsten + - Rhenium + - Iridium + - Platinum + - Gold + - Mercury + - Lead diff --git a/src/client.c b/src/client.c index c964cbb..f20750a 100644 --- a/src/client.c +++ b/src/client.c @@ -492,7 +492,6 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count state->screen = ScreenMainGame; } } - if (state->menu.selected_index >= state->menu.len) { state->menu.selected_index = 0; } @@ -511,6 +510,52 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count // TODO: game-specific character creation stuff // 3. draw the ship choices str ship_label = "Choose your starting ship:"; + line++; + // the table + str cols[SHIP_DETAIL_COUNT] = {"Type", "Cost", "DriveEff", "LifeSupp", "V.Cargo", "C.Cargo", "Passengers", "SmugglersHold"}; + u32 table_x = 5; + u32 table_col_pad = 1; + // render the headers + u32 col_x_pos = 0; + for (u32 i = 0; i < SHIP_DETAIL_COUNT; col_x_pos += SHIP_FIELDS[i].width+table_col_pad, i++) { + renderStrToBuffer(tui->frame_buffer, table_x+col_x_pos, line, cols[i], screen_dimensions); + for (u32 ii = 0; ii < SHIP_FIELDS[i].width; ii++) { + renderUtf8CharToBuffer(tui->frame_buffer, table_x+col_x_pos+ii, line+1, "━", screen_dimensions); + } + } + line++; + line++; + // render the columns + char tmp_buffer[32] = {0}; + for (u32 i = 0; i < ShipType_Count; line++, i++) { + col_x_pos = 0; + ShipTemplate ship = SHIPS[i]; + for (u32 ii = 0; ii < SHIP_DETAIL_COUNT; col_x_pos += SHIP_FIELDS[ii].width+table_col_pad, ii++) { + FieldDescriptor details = SHIP_FIELDS[ii]; + void *field_ptr = (char *)&ship + details.offset; + switch (details.type) { + case FieldTypeEnum: { + renderStrToBuffer(tui->frame_buffer, table_x+col_x_pos, line, details.enum_vals[ship.type], screen_dimensions); + } break; + case FieldTypeU8: { + MemoryZero(tmp_buffer, 32); + sprintf(tmp_buffer, "%d", *(u8 *)field_ptr); + renderStrToBuffer(tui->frame_buffer, table_x+col_x_pos, line, tmp_buffer, screen_dimensions); + } break; + case FieldTypeU16: { + MemoryZero(tmp_buffer, 32); + sprintf(tmp_buffer, "%d", *(u16 *)field_ptr); + renderStrToBuffer(tui->frame_buffer, table_x+col_x_pos, line, tmp_buffer, screen_dimensions); + } break; + case FieldTypeU32: { + MemoryZero(tmp_buffer, 32); + sprintf(tmp_buffer, "%d", *(u32 *)field_ptr); + renderStrToBuffer(tui->frame_buffer, table_x+col_x_pos, line, tmp_buffer, screen_dimensions); + } break; + } + } + } + /* renderStrToBuffer(tui->frame_buffer, 5, ++line, ship_label, screen_dimensions); line++; renderChoiceMenu( @@ -523,6 +568,7 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count state->menu.selected_index, NULL ); + */ } break; case ScreenMainGame: { // SIMULATION diff --git a/src/shared.h b/src/shared.h index 53c0588..deed340 100644 --- a/src/shared.h +++ b/src/shared.h @@ -33,12 +33,14 @@ typedef enum ShipType { ShipNX400, ShipType_Count, } ShipType; -static const char* SHIP_TYPE_STRINGS[] = { +global str SHIP_TYPE_STRINGS[] = { "Sparrow", "Dart", "Hauler-Prime Z-1", "NX-400", }; + +#define SHIP_DETAIL_COUNT (8) typedef struct ShipTemplate { ShipType type; u8 drive_efficiency; @@ -77,6 +79,39 @@ global ShipTemplate SHIPS[] = { }, }; +typedef enum { + FieldTypeU8, + FieldTypeU16, + FieldTypeU32, + FieldTypeFloat, + FieldTypeString, + FieldTypeEnum, + FieldType_Count, +} FieldType; + +typedef struct FieldDescriptor { + str name; + FieldType type; + size_t offset; + int width; // column width for display + str* enum_vals; +} FieldDescriptor; + +// Define field metadata using offsetof +#define offsetof(st, m) ((size_t)&(((st*)0)->m)) + +// Field descriptors for Employee +global FieldDescriptor SHIP_FIELDS[SHIP_DETAIL_COUNT] = { + { "Type", FieldTypeEnum, offsetof(ShipTemplate, type), 16, (str*)&SHIP_TYPE_STRINGS }, + { "Cost", FieldTypeU32, offsetof(ShipTemplate, base_cost), 8 }, + { "DriveEff", FieldTypeU8, offsetof(ShipTemplate, drive_efficiency), 8 }, + { "LifeSupp", FieldTypeU8, offsetof(ShipTemplate, life_support_efficiency), 8 }, + { "V.Cargo", FieldTypeU16, offsetof(ShipTemplate, vacuum_cargo_slots), 7 }, + { "C.Cargo", FieldTypeU16, offsetof(ShipTemplate, climate_cargo_slots), 7 }, + { "Passengers", FieldTypeU16, offsetof(ShipTemplate, passenger_berths), 10 }, + { "SmugglersHold", FieldTypeU16, offsetof(ShipTemplate, smugglers_hold_cu_m), 13 }, +}; + typedef struct PlayerShip { ShipType type; u8 drive_efficiency;