can display a list of passenger job offers

This commit is contained in:
Tenari
2026-03-14 15:30:20 -05:00
parent 5dc79706ed
commit 09a7eb507a
4 changed files with 297 additions and 65 deletions
+6
View File
@@ -40,6 +40,12 @@ fn void* arenaAlloc(Arena* arena, u64 size) {
return memory;
}
fn void* arenaAllocZero(Arena* a, u64 size) {
void* result = arenaAlloc(a, size);
MemoryZero(result, size);
return result;
}
fn void* arenaAllocArraySized(Arena* arena, u64 elem_size, u64 count) {
return arenaAlloc(arena, elem_size * count);
}
+123 -46
View File
@@ -33,7 +33,8 @@ typedef enum Tab {
TabChat,
TabMap,
TabShip,
TabStation,
TabMarket,
TabPassengers,
Tab_Count,
} Tab;
@@ -44,14 +45,22 @@ typedef enum ShipTabStates {
ShipTabStates_Count,
} ShipTabStates;
typedef enum StationTabStates {
StationTabStateTable,
StationTabStateComparisonTable,
StationTabStateTransact,
StationTabStateResult,
StationTabStateLoading,
StationTabStates_Count,
} StationTabStates;
typedef enum MarketTabStates {
MarketTabStateTable,
MarketTabStateComparisonTable,
MarketTabStateTransact,
MarketTabStateResult,
MarketTabStateLoading,
MarketTabStates_Count,
} MarketTabStates;
typedef enum PassengersTabStates {
PassengersTabStateTable,
PassengersTabStateBookModal,
PassengersTabStateLoading,
PassengersTabStateResult,
PassengersTabStates_Count,
} PassengersTabStates;
typedef enum Screen {
ScreenLogin,
@@ -98,7 +107,7 @@ typedef struct ParsedServerMessage {
StarSystem sys;
PlayerShip ship;
TransactionResult tx_result;
//u64 ids[PARSED_IDS_LEN];
PassengerJobOffer offers[MAX_PASSENGER_JOB_OFFERS];
} ParsedServerMessage;
typedef struct ParsedServerMessageThreadQueue {
@@ -149,7 +158,7 @@ typedef struct GameState {
StarSystem map[STAR_SYSTEM_COUNT];
Pos2 pos;
u8 destination_sys_idx;
StationTabStates station_tab_state;
MarketTabStates market_tab_state;
ShipTabStates ship_tab_states;
TransactionResult tx_result;
u64 turn_tick_started_on;
@@ -164,7 +173,7 @@ global u8 system_message_index = 0;
global GameState state = {0};
global OutgoingMessageQueue* network_send_queue = {0};
global ParsedServerMessageThreadQueue* network_recv_queue = {0};
global str TAB_STRS[Tab_Count] = {"Debug", "Chat", "Map", "Ship", "Station"};
global str TAB_STRS[Tab_Count] = {"Debug", "Chat", "Map", "Ship", "Market", "Passengers"};
global FieldDescriptor COMPARE_COMMODITY_FIELDS[STAR_SYSTEM_COUNT] = {
{ "System", FieldTypeString, offsetof(CompareCommodity, system_name), 16 },
{ "Bid", FieldTypeU32, offsetof(CompareCommodity, bid), 6 },
@@ -310,13 +319,16 @@ fn void renderStaticAssetToPixelBuffer(TuiState* tui, u8* asset, u32 len, u16 x,
}
fn void resetTabRow(Tab tab) {
if (state.menu.selected_index == TabStation) {
if (state.menu.selected_index == TabMarket) {
state.row.len = Commodity_Count;
state.row.selected_index = 0;
} else if (state.menu.selected_index == TabShip) {
state.ship_tab_states = ShipTabStateMain;
state.row.len = 2;
state.row.selected_index = 0;
} else if (state.menu.selected_index == TabPassengers) {
state.row.len = 0;// TODO the number of available passenger missions
state.row.selected_index = 0;
}
}
@@ -351,6 +363,17 @@ fn void handleIncomingMessage(u8* message, u32 len, SocketAddress sender, i32 so
case MessageGameOver: {
parsed.byte = message[msg_pos++];
} break;
case MessageSystemPassengers: {
parsed.byte = message[msg_pos++];
u32 i = 0;
while (msg_pos < len) {
parsed.offers[i].goal_system_idx = message[msg_pos++];
parsed.offers[i].people = message[msg_pos++];
parsed.offers[i].time_limit = message[msg_pos++];
parsed.offers[i].offer = readU32FromBufferLE(message + msg_pos);
msg_pos += 4;
}
} break;
case MessageTransactionResult: {
parsed.tx_result.buying = message[msg_pos++];
parsed.tx_result.qty = readU32FromBufferLE(message + msg_pos);
@@ -454,6 +477,23 @@ fn i32 compareCompareCommodity(const void *a, const void *b) {
return ((CompareCommodity*)b)->bid - ((CompareCommodity*)a)->bid;
}
fn void drawStatusLine(TuiState* tui, ptr sbuf, PlayerShip* me, u32 x, u32 y) {
MemoryZero(sbuf, SBUFLEN);
sprintf(sbuf, " $%d ", me->credits);
Range1u32 range = {
.min = XYToPos(x, y, tui->screen_dimensions.width),
.max = XYToPos(x+strlen(sbuf), y, tui->screen_dimensions.width),
};
colorizeRange(tui, range, ANSI_HIGHLIGHT_YELLOW, ANSI_BLACK);
renderStrToBuffer(tui->frame_buffer, x, y, sbuf, tui->screen_dimensions);
MemoryZero(sbuf, SBUFLEN);
sprintf(sbuf, " %d / %d cargo ", usedVacuumCargoSlots(*me), me->vacuum_cargo_slots);
range.min = range.max;
range.max = range.min+strlen(sbuf);
colorizeRange(tui, range, ANSI_DULL_BLUE, ANSI_WHITE);
renderStrToBuffer(tui->frame_buffer, range.min % tui->screen_dimensions.width, y, sbuf, tui->screen_dimensions);
}
fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count) {
GameState* state = (GameState*) s;
state->loop_count = loop_count;
@@ -476,6 +516,10 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
while (next_net_msg != NULL) {
msg_iters += 0;
switch (msg.type) {
case MessageSystemPassengers: {
// put the stuff from msg into the correct state.map[i]
MemoryCopy(&state->map[msg.byte].offers, &msg.offers, sizeof(PassengerJobOffer) * MAX_PASSENGER_JOB_OFFERS);
} break;
case MessageGameOver: {
state->screen = state->me.id == msg.byte ? ScreenVictory : ScreenDefeat;
sprintf(sbuf,"got winner id: %d", msg.byte);
@@ -492,7 +536,7 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
state->turn_tick_started_on = loop_count;
} break;
case MessageTransactionResult: {
state->station_tab_state = StationTabStateResult;
state->market_tab_state = MarketTabStateResult;
MemoryCopyStruct(&state->tx_result, &msg.tx_result);
} break;
case MessagePlayerDetails: {
@@ -1026,32 +1070,20 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
renderStrToBuffer(tui->frame_buffer, modal_outline.x+2, y_off, "Loading...", screen_dimensions);
}
} break;
case TabStation: {
case TabMarket: {
if (input_buffer[0] == 'q' || user_pressed_esc) {
if (state->station_tab_state == StationTabStateTable) {
if (state->market_tab_state == MarketTabStateTable) {
should_quit = true;
} else {
state->station_tab_state = StationTabStateTable;
state->market_tab_state = MarketTabStateTable;
}
}
u32 line = box.y + 1;
// draw a nifty status-line
MemoryZero(sbuf, SBUFLEN);
sprintf(sbuf, " $: %d ", state->me.credits);
Range1u32 range = {
.min = XYToPos(box.x+2, line, screen_dimensions.width),
.max = XYToPos(box.x+2+strlen(sbuf), line, screen_dimensions.width),
};
colorizeRange(tui, range, ANSI_HIGHLIGHT_YELLOW, ANSI_BLACK);
renderStrToBuffer(tui->frame_buffer, box.x+2, line, sbuf, screen_dimensions);
MemoryZero(sbuf, SBUFLEN);
sprintf(sbuf, " %d / %d cargo ", usedVacuumCargoSlots(state->me), state->me.vacuum_cargo_slots);
range.min = range.max;
range.max = range.min+strlen(sbuf);
colorizeRange(tui, range, ANSI_DULL_BLUE, ANSI_WHITE);
renderStrToBuffer(tui->frame_buffer, range.min % screen_dimensions.width, line++, sbuf, screen_dimensions);
drawStatusLine(tui, sbuf, &state->me, box.x+2, line);
line++;
MemoryZero(sbuf, SBUFLEN);
sprintf(sbuf, "Welcome to %s", curr.name);
@@ -1072,7 +1104,7 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
.qty = selected_qty,
.owned = state->me.commodities[c.type],
};
if (state->station_tab_state == StationTabStateComparisonTable) {
if (state->market_tab_state == MarketTabStateComparisonTable) {
info.rows = STAR_SYSTEM_COUNT;
info.cols = 4;
CompareCommodity rows[STAR_SYSTEM_COUNT] = { 0 };
@@ -1117,7 +1149,7 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
.height = 15,
.width = 50,
};
if (state->station_tab_state != StationTabStateTable && state->station_tab_state != StationTabStateComparisonTable) {
if (state->market_tab_state != MarketTabStateTable && state->market_tab_state != MarketTabStateComparisonTable) {
// draw the modal outline
clearBox(tui, modal_outline);
drawAnsiBox(tui->frame_buffer, modal_outline, tui->screen_dimensions, false);
@@ -1127,24 +1159,24 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
u32 y_off = modal_outline.y+2;
str cname = COMMODITIES[state->row.selected_index].name;
switch (state->station_tab_state) {
case StationTabStateTable: {
switch (state->market_tab_state) {
case MarketTabStateTable: {
moveRowUpDown(user_pressed_up, user_pressed_down);
if (user_pressed_enter) {
state->station_tab_state = StationTabStateTransact;
state->market_tab_state = MarketTabStateTransact;
state->modal_choice.len = 2;
state->modal_choice.selected_index = 0;
} else if (user_pressed_space) {
state->station_tab_state = StationTabStateComparisonTable;
state->market_tab_state = MarketTabStateComparisonTable;
}
} break;
case StationTabStateComparisonTable: {
case MarketTabStateComparisonTable: {
if (user_pressed_backspace) {
state->station_tab_state = StationTabStateTable;
state->market_tab_state = MarketTabStateTable;
}
// TODO
} break;
case StationTabStateTransact: {
case MarketTabStateTransact: {
if (user_pressed_right) {
state->modal_choice.selected_index = 1;
} else if (user_pressed_left) {
@@ -1217,10 +1249,10 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
outgoingMessageQueuePush(network_send_queue, &msg);
state->station_tab_state = StationTabStateLoading;
state->market_tab_state = MarketTabStateLoading;
}
} break;
case StationTabStateResult: {
case MarketTabStateResult: {
if (state->tx_result.buying) {
renderStrToBuffer(tui->frame_buffer, modal_outline.x+((modal_outline.width-10)/2), y_off++, "Purchased!", screen_dimensions);
} else {
@@ -1245,16 +1277,49 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count
renderStrToBuffer(tui->frame_buffer, exit_x, y_off++, "EXIT", screen_dimensions);
if (user_pressed_enter) {
state->station_tab_state = StationTabStateTable;
state->market_tab_state = MarketTabStateTable;
}
} break;
case StationTabStateLoading: {
case MarketTabStateLoading: {
renderStrToBuffer(tui->frame_buffer, modal_outline.x+2, y_off, "Loading...", screen_dimensions);
} break;
case StationTabStates_Count: assert(false && "invalid station_tab_state"); break;
case MarketTabStates_Count: assert(false && "invalid market_tab_state"); break;
}
} break;
case TabPassengers: {
u32 line = box.y + 1;
// draw a nifty status-line
drawStatusLine(tui, sbuf, &state->me, box.x+2, line);
line++;
// the table
u32 offer_count = 0;
DisplayPassengerJobOffer rows[MAX_PASSENGER_JOB_OFFERS] = { 0 };
for (u32 i = 0; i < MAX_PASSENGER_JOB_OFFERS; i++) {
if (curr.offers[i].people > 0) {
offer_count += 1;
rows[i].destination = state->map[curr.offers[i].goal_system_idx].name;
rows[i].people = curr.offers[i].people;
rows[i].time_limit = curr.offers[i].time_limit;
rows[i].offer = curr.offers[i].offer;
}
}
TableDrawInfo info = {
.x_offset = box.x+2, .y_offset = ++line,
.rows = offer_count, .cols = 4,
};
renderTable(
tui,
info,
state->row.selected_index,
PASSENGER_JOB_OFFER_FIELDS,
1,
rows,
sizeof(DisplayPassengerJobOffer)
);
} break;
case Tab_Count: {} break;
}
} break;
@@ -1431,7 +1496,19 @@ i32 main(i32 argc, ptr argv[]) {
printf("bad network startup");
exit(1);
}
state.client = createUDPClient(7777, argc > 1 ? argv[1] : NULL);
ptr server_address = NULL;
if (argc > 1) {
server_address = argv[1];
} else {
printf("Server IP Address: ");
char input_string[16] = { 0 };
scanf("%15s", input_string);
printf("%s", input_string);
if (input_string[0] && input_string[1] && input_string[2] && input_string[3]) {
server_address = input_string;
}
}
state.client = createUDPClient(7777, server_address);
// "hardcoded" keep alive message to periodically send to server
state.keep_alive_msg.address = state.client.server_address;
+95 -1
View File
@@ -37,6 +37,8 @@
typedef struct ParsedClientCommand {
CommandType type;
u8 byte;
u8 people;
u8 time_limit;
u16 sender_port;
u16 alt_port;
u32 sender_ip;
@@ -235,6 +237,23 @@ fn u32 starSystemPlanetCount(StarSystem* sys) {
return planet_count;
}
fn UDPMessage makeMessageSystemPassengers(StarSystem* sys) {
UDPMessage outgoing_message = {0};
u32 msg_i = 0;
outgoing_message.bytes[msg_i++] = (u8)MessageSystemPassengers;
outgoing_message.bytes[msg_i++] = (u8)sys->idx;
for (u32 i = 0; i < MAX_PASSENGER_JOB_OFFERS; i++) {
if (sys->offers[i].people) {
outgoing_message.bytes[msg_i++] = sys->offers[i].goal_system_idx;
outgoing_message.bytes[msg_i++] = sys->offers[i].people;
outgoing_message.bytes[msg_i++] = sys->offers[i].time_limit;
msg_i += writeU32ToBufferLE(outgoing_message.bytes + msg_i, sys->offers[i].offer);
}
}
outgoing_message.bytes_len = msg_i;
return outgoing_message;
}
fn UDPMessage makeMessageSystemCommodities(StarSystem* sys) {
UDPMessage outgoing_message = {0};
u32 planet_count = starSystemPlanetCount(sys);
@@ -395,6 +414,13 @@ fn void handleIncomingMessage(u8* message, u32 len, SocketAddress sender, i32 so
printf("command create character received\n");
parsed.byte = message[1];
} break;
case CommandAcceptPassengerJob: {
printf("command accept passenger job received\n");
parsed.byte = message[1];
parsed.people = message[2];
parsed.time_limit = message[3];
parsed.qty = readU32FromBufferLE(message + 4);
} break;
case CommandInvalid:
case CommandType_Count: {
dbg("invalid command type");
@@ -443,6 +469,13 @@ fn void* sendNetworkUpdates(void* sock) {
.items = sys_udp_msg.bytes,
.length = sys_udp_msg.bytes_len,
};
UDPMessage sys_pass_udp_msg = { 0 };
sys_pass_udp_msg = makeMessageSystemPassengers(&state.map[send_loop % STAR_SYSTEM_COUNT]);
u8List sys_pass_msg = {
.capacity = UDP_MAX_MESSAGE_LEN,
.items = sys_pass_udp_msg.bytes,
.length = sys_pass_udp_msg.bytes_len,
};
lockMutex(&state.client_mutex); {
// WARNING the `i` starts at 1 here because state.clients.items[0] is a "null" Client
for (u32 i = 1; i < state.clients.length; i++) {
@@ -455,6 +488,8 @@ fn void* sendNetworkUpdates(void* sock) {
// every "send-frame" we send each connnected client the current prices for a different system
// so that the prices mostly stay up to date pretty quickly without having to track changes
sendUDPu8List(socket, &client.address, &sys_msg);
// and the passenger offers
sendUDPu8List(socket, &client.address, &sys_pass_msg);
// update all the changed systems
UDPMessage msg_data;
@@ -549,6 +584,37 @@ fn void* gameLoop(void* params) {
u32 client_handle = findClientHandleBySocketAddress(&state.clients, sender);
Client* client = &state.clients.items[client_handle];
switch (msg.type) {
case CommandAcceptPassengerJob: {
// move the offer out of the system and INTO the PlayerShip
Account* account = &state.accounts[client->account_id];
StarSystem* player_sys = &state.map[account->ship.system_idx];
PassengerJobOffer pjo = {
.goal_system_idx = msg.byte,
.people = msg.people,
.time_limit = msg.time_limit,
.offer = msg.qty,
};
// find the matching job, and IF the ship has room,
// "accept" it by clearing it from the system list and adding it to the ship
for (u32 i = 0; i < MAX_PASSENGER_JOB_OFFERS; i++) {
if (passengerJobEq(player_sys->offers[i], pjo)) {
if (shipAvailablePassengerBerths(account->ship) > 0) {
for (u32 ii = 0; ii < MAX_PASSENGER_BERTHS; ii++) {
if (account->ship.passengers[ii].people == 0) {
account->ship.passengers[ii].people = pjo.people;
account->ship.passengers[ii].goal_system_idx = pjo.goal_system_idx;
account->ship.passengers[ii].turns_remaining = pjo.time_limit;
account->ship.passengers[ii].reward = pjo.offer;
MemoryZero(&player_sys->offers[i], sizeof(PassengerJobOffer));
ii = MAX_PASSENGER_BERTHS;
i = MAX_PASSENGER_JOB_OFFERS;
break;
}
}
}
}
}
} break;
case CommandPayMortgage: {
Account* account = &state.accounts[client->account_id];
u32 amount_to_pay = msg.id;
@@ -819,6 +885,7 @@ fn void* gameLoop(void* params) {
for (u32 i = sys_range.min; i < sys_range.max; i++) {
sys = &state.map[i];
sys->changed = true;
// consume and produce commodities
for (u32 ii = 0; ii < Commodity_Count; ii++) {
for (u32 iii = 0; iii < MAX_PLANETS; iii++) {
if (sys->planets[iii].type != PlanetTypeNull) {
@@ -831,6 +898,22 @@ fn void* gameLoop(void* params) {
}
}
}
// potentially create a new passenger job
u32 planet_divisor = ((MAX_PLANETS - starSystemPlanetCount(sys))+1);
u32 max_passenger_job_count = 1 + ((MAX_PASSENGER_JOB_OFFERS-1) / planet_divisor);
for (u32 ii = 0; ii < max_passenger_job_count; ii++) {
if (sys->offers[ii].people == 0) {
// TODO choose a system further away
sys->offers[ii].goal_system_idx = (i+(rand() % 6)) % STAR_SYSTEM_COUNT;
sys->offers[ii].people = 1 + (rand() % (MAX_PASSENGER_JOB_PEOPLE - 1));
sys->offers[ii].time_limit = 3 + (rand() % 5);
// TODO compute based on distance between people and time_limit
sys->offers[ii].offer = 1000 + (rand() % MAX_PASSENGER_JOB_PRICE);
break;
} else { //increase the offer amount each turn
sys->offers[ii].offer = (u32)((f32)sys->offers[ii].offer * 1.05);
}
}
}
Range1u64 ship_range = LaneRange(player_count);
@@ -953,7 +1036,7 @@ i32 main(i32 argc, ptr argv[]) {
}
}
// now, build out the commodity info for the systems
// now, build out the commodity info and passenger jobs for the systems
for (u32 i = 0; i < STAR_SYSTEM_COUNT; i++) {
u32 planet_count = rand() % MAX_PLANETS + 1;
for (u32 ii = 0; ii < planet_count; ii++) {
@@ -1055,6 +1138,17 @@ i32 main(i32 argc, ptr argv[]) {
break;
}
}
u32 planet_divisor = ((MAX_PLANETS - planet_count)+1);
u32 offer_count = 1 + (rand() % (MAX_PASSENGER_JOB_OFFERS / planet_divisor));
for (u32 ii = 0; ii < offer_count; ii++) {
// TODO choose a system further away
state.map[i].offers[ii].goal_system_idx = (i+(rand() % 6)) % STAR_SYSTEM_COUNT;
state.map[i].offers[ii].people = 1 + (rand() % (MAX_PASSENGER_JOB_PEOPLE - 1));
state.map[i].offers[ii].time_limit = 3 + (rand() % 5);
// TODO compute based on distance between people and time_limit
state.map[i].offers[ii].offer = 1000 + (rand() % MAX_PASSENGER_JOB_PRICE);
}
}
// 2. spin off sendNetworkUpdates() infinite loop thread
+73 -18
View File
@@ -11,12 +11,16 @@
#define MAP_WIDTH (36)
#define MAP_HEIGHT (12)
#define MAX_PLANETS (3)
#define MAX_PASSENGER_JOB_OFFERS (16)
#define MAX_PASSENGER_JOB_PEOPLE (4)
#define MAX_PASSENGER_JOB_PRICE (10000)
#define MAX_PASSENGER_BERTHS (8)
typedef enum ShipType {
ShipSparrow,
ShipDart,
ShipHaulerPrimeZ1,
// ShipNX400,
ShipNX400,
ShipType_Count,
} ShipType;
@@ -24,7 +28,7 @@ global str SHIP_TYPE_STRINGS[] = {
"Sparrow",
"Dart",
"Hauler-Prime Z-1",
// "NX-400",
"NX-400",
};
typedef struct ShipTemplate {
@@ -43,33 +47,33 @@ typedef struct ShipTemplate {
global ShipTemplate SHIPS[] = {
{
.type = ShipSparrow, .drive_efficiency = 10, .life_support_efficiency = 2,
.type = ShipSparrow, .drive_efficiency = 10, .life_support_efficiency = 1,
.vacuum_cargo_slots = 5, .climate_cargo_slots = 0, .passenger_berths = 0,
.passenger_amenities_flags = 0,
.smugglers_hold_cu_m = 1, .base_cost = 100000,
.cu_m_fuel = 2000, .cu_m_o2 = 1000,
.cu_m_fuel = 2500, .cu_m_o2 = 1000,
},
{
.type = ShipDart, .drive_efficiency = 70, .life_support_efficiency = 4,
.vacuum_cargo_slots = 10, .climate_cargo_slots = 1, .passenger_berths = 1,
.type = ShipDart, .drive_efficiency = 70, .life_support_efficiency = 3,
.vacuum_cargo_slots = 6, .climate_cargo_slots = 0, .passenger_berths = 1,
.passenger_amenities_flags = 0,
.smugglers_hold_cu_m = 0, .base_cost = 150000,
.cu_m_fuel = 2000, .cu_m_o2 = 1000,
.cu_m_fuel = 2000, .cu_m_o2 = 2000,
},
{
.type = ShipHaulerPrimeZ1, .drive_efficiency = 50, .life_support_efficiency = 3,
.vacuum_cargo_slots = 100, .climate_cargo_slots = 5, .passenger_berths = 0,
.type = ShipHaulerPrimeZ1, .drive_efficiency = 50, .life_support_efficiency = 2,
.vacuum_cargo_slots = 30, .climate_cargo_slots = 0, .passenger_berths = 0,
.passenger_amenities_flags = 0,
.smugglers_hold_cu_m = 0, .base_cost = 250000,
.cu_m_fuel = 2000, .cu_m_o2 = 1000,
.cu_m_fuel = 2500, .cu_m_o2 = 1000,
},
/* {
.type = ShipNX400, .drive_efficiency = 6, .life_support_efficiency = 8,
.vacuum_cargo_slots = 5, .climate_cargo_slots = 5, .passenger_berths = 5,
{
.type = ShipNX400, .drive_efficiency = 60, .life_support_efficiency = 5,
.vacuum_cargo_slots = 3, .climate_cargo_slots = 0, .passenger_berths = 3,
.passenger_amenities_flags = 0,
.smugglers_hold_cu_m = 0, .base_cost = 350000,
.cu_m_fuel = 2000, .cu_m_o2 = 1000,
},*/
.smugglers_hold_cu_m = 0, .base_cost = 250000,
.cu_m_fuel = 2000, .cu_m_o2 = 3000,
},
};
global FieldDescriptor SHIP_FIELDS[SHIP_DETAIL_COUNT] = {
@@ -272,6 +276,35 @@ global FieldDescriptor MARKET_COMMODITY_FIELDS[Commodity_Count] = {
{ "Owned", FieldTypeU32, offsetof(MarketCommodity, owned), 6 },
};
typedef struct Passenger {
u8 people;
u8 goal_system_idx;
u8 turns_remaining;
u32 reward;
} Passenger;
typedef struct PassengerJobOffer {
u8 goal_system_idx;
u8 people; // 1 - 4, affects how much oxygen they consume
u8 time_limit; // # of turns until they leave your ship without paying
u32 offer;
} PassengerJobOffer;
typedef struct DisplayPassengerJobOffer {
str destination;
u8 people;
u8 time_limit;
u32 offer;
} DisplayPassengerJobOffer;
global FieldDescriptor PASSENGER_JOB_OFFER_FIELDS[] = {
{ "Destination", FieldTypeString, offsetof(DisplayPassengerJobOffer, destination), 44 },
{ "People", FieldTypeU8, offsetof(DisplayPassengerJobOffer, people), 6 },
{ "Turns", FieldTypeU8, offsetof(DisplayPassengerJobOffer, time_limit), 6 },
{ "Offer", FieldTypeU32, offsetof(DisplayPassengerJobOffer, offer), 6 },
};
typedef struct PlayerShip {
ShipType type;
bool ready_to_depart;
@@ -291,6 +324,7 @@ typedef struct PlayerShip {
u32 credits;
u64 id;
u32 commodities[Commodity_Count];
Passenger passengers[MAX_PASSENGER_BERTHS];
} PlayerShip;
typedef enum PlanetType {
@@ -317,6 +351,7 @@ typedef struct StarSystem {
u32 idx;
str name;
Planet planets[MAX_PLANETS];
PassengerJobOffer offers[MAX_PASSENGER_JOB_OFFERS];
} StarSystem;
global str STAR_NAMES[STAR_SYSTEM_COUNT] = {
@@ -382,6 +417,7 @@ typedef enum CommandType {
CommandReadyStatus,
CommandSetDestination,
CommandPayMortgage,
CommandAcceptPassengerJob,
CommandType_Count,
} CommandType;
@@ -393,10 +429,9 @@ static const char* command_type_strings[CommandType_Count] = {
"ReadyStatus",
"SetDestination",
"PayMortgage",
"AcceptPassengerJob",
};
#define ENTITY_HEADER_MESSAGE_SIZE (8+8+1+1+1)
#define ENTITY_MESSAGE_SIZE (ENTITY_HEADER_MESSAGE_SIZE+2+2+2+2+8+1+1)
typedef enum Message {
MessageInvalid,
MessageCharacterId,
@@ -409,6 +444,7 @@ typedef enum Message {
MessageTurnTick,
MessageGameOver,
MessagePayoffResult,
MessageSystemPassengers,
Message_Count,
} Message;
@@ -424,6 +460,7 @@ static const char* MESSAGE_STRINGS[] = {
"TurnTick",
"GameOver",
"PayoffResult",
"SystemPassengers",
};
///// shared helper functions
@@ -473,4 +510,22 @@ fn f32 calcInterestRate(u32 cost, u32 down_payment) {
return mortgage_rate;
}
fn bool passengerJobEq(PassengerJobOffer a, PassengerJobOffer b) {
return a.goal_system_idx == b.goal_system_idx &&
a.offer == b.offer &&
a.people == b.people &&
a.time_limit == b.time_limit;
}
fn bool shipAvailablePassengerBerths(PlayerShip ship) {
u32 used = 0;
for (u32 i = 0; i < MAX_PASSENGER_BERTHS; i++) {
if (ship.passengers[i].people > 0) {
used += 1;
}
}
assert(used <= ship.passenger_berths);
return ship.passenger_berths - used;
}
#endif //GAMESHARED_H