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; 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) { fn void* arenaAllocArraySized(Arena* arena, u64 elem_size, u64 count) {
return arenaAlloc(arena, elem_size * count); return arenaAlloc(arena, elem_size * count);
} }
+123 -46
View File
@@ -33,7 +33,8 @@ typedef enum Tab {
TabChat, TabChat,
TabMap, TabMap,
TabShip, TabShip,
TabStation, TabMarket,
TabPassengers,
Tab_Count, Tab_Count,
} Tab; } Tab;
@@ -44,14 +45,22 @@ typedef enum ShipTabStates {
ShipTabStates_Count, ShipTabStates_Count,
} ShipTabStates; } ShipTabStates;
typedef enum StationTabStates { typedef enum MarketTabStates {
StationTabStateTable, MarketTabStateTable,
StationTabStateComparisonTable, MarketTabStateComparisonTable,
StationTabStateTransact, MarketTabStateTransact,
StationTabStateResult, MarketTabStateResult,
StationTabStateLoading, MarketTabStateLoading,
StationTabStates_Count, MarketTabStates_Count,
} StationTabStates; } MarketTabStates;
typedef enum PassengersTabStates {
PassengersTabStateTable,
PassengersTabStateBookModal,
PassengersTabStateLoading,
PassengersTabStateResult,
PassengersTabStates_Count,
} PassengersTabStates;
typedef enum Screen { typedef enum Screen {
ScreenLogin, ScreenLogin,
@@ -98,7 +107,7 @@ typedef struct ParsedServerMessage {
StarSystem sys; StarSystem sys;
PlayerShip ship; PlayerShip ship;
TransactionResult tx_result; TransactionResult tx_result;
//u64 ids[PARSED_IDS_LEN]; PassengerJobOffer offers[MAX_PASSENGER_JOB_OFFERS];
} ParsedServerMessage; } ParsedServerMessage;
typedef struct ParsedServerMessageThreadQueue { typedef struct ParsedServerMessageThreadQueue {
@@ -149,7 +158,7 @@ typedef struct GameState {
StarSystem map[STAR_SYSTEM_COUNT]; StarSystem map[STAR_SYSTEM_COUNT];
Pos2 pos; Pos2 pos;
u8 destination_sys_idx; u8 destination_sys_idx;
StationTabStates station_tab_state; MarketTabStates market_tab_state;
ShipTabStates ship_tab_states; ShipTabStates ship_tab_states;
TransactionResult tx_result; TransactionResult tx_result;
u64 turn_tick_started_on; u64 turn_tick_started_on;
@@ -164,7 +173,7 @@ global u8 system_message_index = 0;
global GameState state = {0}; global GameState state = {0};
global OutgoingMessageQueue* network_send_queue = {0}; global OutgoingMessageQueue* network_send_queue = {0};
global ParsedServerMessageThreadQueue* network_recv_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] = { global FieldDescriptor COMPARE_COMMODITY_FIELDS[STAR_SYSTEM_COUNT] = {
{ "System", FieldTypeString, offsetof(CompareCommodity, system_name), 16 }, { "System", FieldTypeString, offsetof(CompareCommodity, system_name), 16 },
{ "Bid", FieldTypeU32, offsetof(CompareCommodity, bid), 6 }, { "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) { fn void resetTabRow(Tab tab) {
if (state.menu.selected_index == TabStation) { if (state.menu.selected_index == TabMarket) {
state.row.len = Commodity_Count; state.row.len = Commodity_Count;
state.row.selected_index = 0; state.row.selected_index = 0;
} else if (state.menu.selected_index == TabShip) { } else if (state.menu.selected_index == TabShip) {
state.ship_tab_states = ShipTabStateMain; state.ship_tab_states = ShipTabStateMain;
state.row.len = 2; state.row.len = 2;
state.row.selected_index = 0; 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: { case MessageGameOver: {
parsed.byte = message[msg_pos++]; parsed.byte = message[msg_pos++];
} break; } 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: { case MessageTransactionResult: {
parsed.tx_result.buying = message[msg_pos++]; parsed.tx_result.buying = message[msg_pos++];
parsed.tx_result.qty = readU32FromBufferLE(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; 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) { fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count) {
GameState* state = (GameState*) s; GameState* state = (GameState*) s;
state->loop_count = loop_count; 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) { while (next_net_msg != NULL) {
msg_iters += 0; msg_iters += 0;
switch (msg.type) { 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: { case MessageGameOver: {
state->screen = state->me.id == msg.byte ? ScreenVictory : ScreenDefeat; state->screen = state->me.id == msg.byte ? ScreenVictory : ScreenDefeat;
sprintf(sbuf,"got winner id: %d", msg.byte); 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; state->turn_tick_started_on = loop_count;
} break; } break;
case MessageTransactionResult: { case MessageTransactionResult: {
state->station_tab_state = StationTabStateResult; state->market_tab_state = MarketTabStateResult;
MemoryCopyStruct(&state->tx_result, &msg.tx_result); MemoryCopyStruct(&state->tx_result, &msg.tx_result);
} break; } break;
case MessagePlayerDetails: { 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); renderStrToBuffer(tui->frame_buffer, modal_outline.x+2, y_off, "Loading...", screen_dimensions);
} }
} break; } break;
case TabStation: { case TabMarket: {
if (input_buffer[0] == 'q' || user_pressed_esc) { if (input_buffer[0] == 'q' || user_pressed_esc) {
if (state->station_tab_state == StationTabStateTable) { if (state->market_tab_state == MarketTabStateTable) {
should_quit = true; should_quit = true;
} else { } else {
state->station_tab_state = StationTabStateTable; state->market_tab_state = MarketTabStateTable;
} }
} }
u32 line = box.y + 1; u32 line = box.y + 1;
// draw a nifty status-line // draw a nifty status-line
MemoryZero(sbuf, SBUFLEN); drawStatusLine(tui, sbuf, &state->me, box.x+2, line);
sprintf(sbuf, " $: %d ", state->me.credits); line++;
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);
MemoryZero(sbuf, SBUFLEN); MemoryZero(sbuf, SBUFLEN);
sprintf(sbuf, "Welcome to %s", curr.name); 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, .qty = selected_qty,
.owned = state->me.commodities[c.type], .owned = state->me.commodities[c.type],
}; };
if (state->station_tab_state == StationTabStateComparisonTable) { if (state->market_tab_state == MarketTabStateComparisonTable) {
info.rows = STAR_SYSTEM_COUNT; info.rows = STAR_SYSTEM_COUNT;
info.cols = 4; info.cols = 4;
CompareCommodity rows[STAR_SYSTEM_COUNT] = { 0 }; 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, .height = 15,
.width = 50, .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 // draw the modal outline
clearBox(tui, modal_outline); clearBox(tui, modal_outline);
drawAnsiBox(tui->frame_buffer, modal_outline, tui->screen_dimensions, false); 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; u32 y_off = modal_outline.y+2;
str cname = COMMODITIES[state->row.selected_index].name; str cname = COMMODITIES[state->row.selected_index].name;
switch (state->station_tab_state) { switch (state->market_tab_state) {
case StationTabStateTable: { case MarketTabStateTable: {
moveRowUpDown(user_pressed_up, user_pressed_down); moveRowUpDown(user_pressed_up, user_pressed_down);
if (user_pressed_enter) { if (user_pressed_enter) {
state->station_tab_state = StationTabStateTransact; state->market_tab_state = MarketTabStateTransact;
state->modal_choice.len = 2; state->modal_choice.len = 2;
state->modal_choice.selected_index = 0; state->modal_choice.selected_index = 0;
} else if (user_pressed_space) { } else if (user_pressed_space) {
state->station_tab_state = StationTabStateComparisonTable; state->market_tab_state = MarketTabStateComparisonTable;
} }
} break; } break;
case StationTabStateComparisonTable: { case MarketTabStateComparisonTable: {
if (user_pressed_backspace) { if (user_pressed_backspace) {
state->station_tab_state = StationTabStateTable; state->market_tab_state = MarketTabStateTable;
} }
// TODO // TODO
} break; } break;
case StationTabStateTransact: { case MarketTabStateTransact: {
if (user_pressed_right) { if (user_pressed_right) {
state->modal_choice.selected_index = 1; state->modal_choice.selected_index = 1;
} else if (user_pressed_left) { } 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); outgoingMessageQueuePush(network_send_queue, &msg);
state->station_tab_state = StationTabStateLoading; state->market_tab_state = MarketTabStateLoading;
} }
} break; } break;
case StationTabStateResult: { case MarketTabStateResult: {
if (state->tx_result.buying) { if (state->tx_result.buying) {
renderStrToBuffer(tui->frame_buffer, modal_outline.x+((modal_outline.width-10)/2), y_off++, "Purchased!", screen_dimensions); renderStrToBuffer(tui->frame_buffer, modal_outline.x+((modal_outline.width-10)/2), y_off++, "Purchased!", screen_dimensions);
} else { } 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); renderStrToBuffer(tui->frame_buffer, exit_x, y_off++, "EXIT", screen_dimensions);
if (user_pressed_enter) { if (user_pressed_enter) {
state->station_tab_state = StationTabStateTable; state->market_tab_state = MarketTabStateTable;
} }
} break; } break;
case StationTabStateLoading: { case MarketTabStateLoading: {
renderStrToBuffer(tui->frame_buffer, modal_outline.x+2, y_off, "Loading...", screen_dimensions); renderStrToBuffer(tui->frame_buffer, modal_outline.x+2, y_off, "Loading...", screen_dimensions);
} break; } break;
case StationTabStates_Count: assert(false && "invalid station_tab_state"); break; case MarketTabStates_Count: assert(false && "invalid market_tab_state"); break;
} }
} 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; case Tab_Count: {} break;
} }
} break; } break;
@@ -1431,7 +1496,19 @@ i32 main(i32 argc, ptr argv[]) {
printf("bad network startup"); printf("bad network startup");
exit(1); 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 // "hardcoded" keep alive message to periodically send to server
state.keep_alive_msg.address = state.client.server_address; state.keep_alive_msg.address = state.client.server_address;
+95 -1
View File
@@ -37,6 +37,8 @@
typedef struct ParsedClientCommand { typedef struct ParsedClientCommand {
CommandType type; CommandType type;
u8 byte; u8 byte;
u8 people;
u8 time_limit;
u16 sender_port; u16 sender_port;
u16 alt_port; u16 alt_port;
u32 sender_ip; u32 sender_ip;
@@ -235,6 +237,23 @@ fn u32 starSystemPlanetCount(StarSystem* sys) {
return planet_count; 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) { fn UDPMessage makeMessageSystemCommodities(StarSystem* sys) {
UDPMessage outgoing_message = {0}; UDPMessage outgoing_message = {0};
u32 planet_count = starSystemPlanetCount(sys); 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"); printf("command create character received\n");
parsed.byte = message[1]; parsed.byte = message[1];
} break; } 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 CommandInvalid:
case CommandType_Count: { case CommandType_Count: {
dbg("invalid command type"); dbg("invalid command type");
@@ -443,6 +469,13 @@ fn void* sendNetworkUpdates(void* sock) {
.items = sys_udp_msg.bytes, .items = sys_udp_msg.bytes,
.length = sys_udp_msg.bytes_len, .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); { lockMutex(&state.client_mutex); {
// WARNING the `i` starts at 1 here because state.clients.items[0] is a "null" Client // 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++) { 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 // 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 // so that the prices mostly stay up to date pretty quickly without having to track changes
sendUDPu8List(socket, &client.address, &sys_msg); sendUDPu8List(socket, &client.address, &sys_msg);
// and the passenger offers
sendUDPu8List(socket, &client.address, &sys_pass_msg);
// update all the changed systems // update all the changed systems
UDPMessage msg_data; UDPMessage msg_data;
@@ -549,6 +584,37 @@ fn void* gameLoop(void* params) {
u32 client_handle = findClientHandleBySocketAddress(&state.clients, sender); u32 client_handle = findClientHandleBySocketAddress(&state.clients, sender);
Client* client = &state.clients.items[client_handle]; Client* client = &state.clients.items[client_handle];
switch (msg.type) { 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: { case CommandPayMortgage: {
Account* account = &state.accounts[client->account_id]; Account* account = &state.accounts[client->account_id];
u32 amount_to_pay = msg.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++) { for (u32 i = sys_range.min; i < sys_range.max; i++) {
sys = &state.map[i]; sys = &state.map[i];
sys->changed = true; sys->changed = true;
// consume and produce commodities
for (u32 ii = 0; ii < Commodity_Count; ii++) { for (u32 ii = 0; ii < Commodity_Count; ii++) {
for (u32 iii = 0; iii < MAX_PLANETS; iii++) { for (u32 iii = 0; iii < MAX_PLANETS; iii++) {
if (sys->planets[iii].type != PlanetTypeNull) { 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); 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++) { for (u32 i = 0; i < STAR_SYSTEM_COUNT; i++) {
u32 planet_count = rand() % MAX_PLANETS + 1; u32 planet_count = rand() % MAX_PLANETS + 1;
for (u32 ii = 0; ii < planet_count; ii++) { for (u32 ii = 0; ii < planet_count; ii++) {
@@ -1055,6 +1138,17 @@ i32 main(i32 argc, ptr argv[]) {
break; 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 // 2. spin off sendNetworkUpdates() infinite loop thread
+73 -18
View File
@@ -11,12 +11,16 @@
#define MAP_WIDTH (36) #define MAP_WIDTH (36)
#define MAP_HEIGHT (12) #define MAP_HEIGHT (12)
#define MAX_PLANETS (3) #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 { typedef enum ShipType {
ShipSparrow, ShipSparrow,
ShipDart, ShipDart,
ShipHaulerPrimeZ1, ShipHaulerPrimeZ1,
// ShipNX400, ShipNX400,
ShipType_Count, ShipType_Count,
} ShipType; } ShipType;
@@ -24,7 +28,7 @@ global str SHIP_TYPE_STRINGS[] = {
"Sparrow", "Sparrow",
"Dart", "Dart",
"Hauler-Prime Z-1", "Hauler-Prime Z-1",
// "NX-400", "NX-400",
}; };
typedef struct ShipTemplate { typedef struct ShipTemplate {
@@ -43,33 +47,33 @@ typedef struct ShipTemplate {
global ShipTemplate SHIPS[] = { 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, .vacuum_cargo_slots = 5, .climate_cargo_slots = 0, .passenger_berths = 0,
.passenger_amenities_flags = 0, .passenger_amenities_flags = 0,
.smugglers_hold_cu_m = 1, .base_cost = 100000, .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, .type = ShipDart, .drive_efficiency = 70, .life_support_efficiency = 3,
.vacuum_cargo_slots = 10, .climate_cargo_slots = 1, .passenger_berths = 1, .vacuum_cargo_slots = 6, .climate_cargo_slots = 0, .passenger_berths = 1,
.passenger_amenities_flags = 0, .passenger_amenities_flags = 0,
.smugglers_hold_cu_m = 0, .base_cost = 150000, .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, .type = ShipHaulerPrimeZ1, .drive_efficiency = 50, .life_support_efficiency = 2,
.vacuum_cargo_slots = 100, .climate_cargo_slots = 5, .passenger_berths = 0, .vacuum_cargo_slots = 30, .climate_cargo_slots = 0, .passenger_berths = 0,
.passenger_amenities_flags = 0, .passenger_amenities_flags = 0,
.smugglers_hold_cu_m = 0, .base_cost = 250000, .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, .type = ShipNX400, .drive_efficiency = 60, .life_support_efficiency = 5,
.vacuum_cargo_slots = 5, .climate_cargo_slots = 5, .passenger_berths = 5, .vacuum_cargo_slots = 3, .climate_cargo_slots = 0, .passenger_berths = 3,
.passenger_amenities_flags = 0, .passenger_amenities_flags = 0,
.smugglers_hold_cu_m = 0, .base_cost = 350000, .smugglers_hold_cu_m = 0, .base_cost = 250000,
.cu_m_fuel = 2000, .cu_m_o2 = 1000, .cu_m_fuel = 2000, .cu_m_o2 = 3000,
},*/ },
}; };
global FieldDescriptor SHIP_FIELDS[SHIP_DETAIL_COUNT] = { global FieldDescriptor SHIP_FIELDS[SHIP_DETAIL_COUNT] = {
@@ -272,6 +276,35 @@ global FieldDescriptor MARKET_COMMODITY_FIELDS[Commodity_Count] = {
{ "Owned", FieldTypeU32, offsetof(MarketCommodity, owned), 6 }, { "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 { typedef struct PlayerShip {
ShipType type; ShipType type;
bool ready_to_depart; bool ready_to_depart;
@@ -291,6 +324,7 @@ typedef struct PlayerShip {
u32 credits; u32 credits;
u64 id; u64 id;
u32 commodities[Commodity_Count]; u32 commodities[Commodity_Count];
Passenger passengers[MAX_PASSENGER_BERTHS];
} PlayerShip; } PlayerShip;
typedef enum PlanetType { typedef enum PlanetType {
@@ -317,6 +351,7 @@ typedef struct StarSystem {
u32 idx; u32 idx;
str name; str name;
Planet planets[MAX_PLANETS]; Planet planets[MAX_PLANETS];
PassengerJobOffer offers[MAX_PASSENGER_JOB_OFFERS];
} StarSystem; } StarSystem;
global str STAR_NAMES[STAR_SYSTEM_COUNT] = { global str STAR_NAMES[STAR_SYSTEM_COUNT] = {
@@ -382,6 +417,7 @@ typedef enum CommandType {
CommandReadyStatus, CommandReadyStatus,
CommandSetDestination, CommandSetDestination,
CommandPayMortgage, CommandPayMortgage,
CommandAcceptPassengerJob,
CommandType_Count, CommandType_Count,
} CommandType; } CommandType;
@@ -393,10 +429,9 @@ static const char* command_type_strings[CommandType_Count] = {
"ReadyStatus", "ReadyStatus",
"SetDestination", "SetDestination",
"PayMortgage", "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 { typedef enum Message {
MessageInvalid, MessageInvalid,
MessageCharacterId, MessageCharacterId,
@@ -409,6 +444,7 @@ typedef enum Message {
MessageTurnTick, MessageTurnTick,
MessageGameOver, MessageGameOver,
MessagePayoffResult, MessagePayoffResult,
MessageSystemPassengers,
Message_Count, Message_Count,
} Message; } Message;
@@ -424,6 +460,7 @@ static const char* MESSAGE_STRINGS[] = {
"TurnTick", "TurnTick",
"GameOver", "GameOver",
"PayoffResult", "PayoffResult",
"SystemPassengers",
}; };
///// shared helper functions ///// shared helper functions
@@ -473,4 +510,22 @@ fn f32 calcInterestRate(u32 cost, u32 down_payment) {
return mortgage_rate; 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 #endif //GAMESHARED_H