can display a list of passenger job offers
This commit is contained in:
@@ -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
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user