diff --git a/src/client.c b/src/client.c index dbd1850..2f498f6 100644 --- a/src/client.c +++ b/src/client.c @@ -7,7 +7,7 @@ * MY_CONSTANT * */ #include -#include +//#include #include "base/impl.c" #include "lib/network.c" #include "render.c" @@ -101,6 +101,7 @@ typedef struct TransactionResult { typedef struct ParsedServerMessage { Message type; + u8 byte; u16 port; u16 port2; u32 ip; @@ -323,51 +324,6 @@ fn void renderPercentBar(TuiState* tui, u16 x, u16 y, u16 width, u8 ansi_color, } } -fn void renderSpeechBubble(TuiState* tui, u16 x, u16 y, u16 max_width, String message, SpeechTailDirection dir) { - Pixel* buf = tui->frame_buffer; - Dim2 screen_dimensions = tui->screen_dimensions; - // calculate dimenions - u32 height = (message.length / max_width) + 1; - u32 width = message.length; - if (message.length > max_width) { - width = max_width; - } - if (width < 12) { - width = 12; // minimum width - } - - // print the upper border - renderUtf8CharToBuffer(buf, x, y, "╭", screen_dimensions); - for (i32 i = 0; i < width+1; i++) { - renderUtf8CharToBuffer(buf, x+1+i, y, "─", screen_dimensions); - } - renderUtf8CharToBuffer(buf, x+width+1, y, "╮", screen_dimensions); - - // start printing the rows - for (u32 i = 0; i < height; i++) { - renderUtf8CharToBuffer(buf, x, y+1+i, "│", screen_dimensions); - // print the line of text - for (u32 j = 0; j < width; j++) { - u32 character_index = (i*(width-1))+j; - u16 pos = (x+1+j) + (screen_dimensions.width * (y+1+i)); - if (message.length > character_index) { - buf[pos].bytes[0] = message.bytes[character_index]; - } else { - buf[pos].bytes[0] = ' '; - } - } - renderUtf8CharToBuffer(buf, x+1+width, y+1+i, "│", screen_dimensions); - } - - // print the bottom box border - renderUtf8CharToBuffer(buf, x, y+1+height, "╰", screen_dimensions); - for (i32 i = 0; i < width+1; i++) { - renderUtf8CharToBuffer(buf, x+1+i, y+1+height, "─", screen_dimensions); - } - renderUtf8CharToBuffer(buf, x+width+1, y+1+height, "╯", screen_dimensions); - // TODO actually print the speech tail -} - fn void renderStaticAssetToPixelBuffer(TuiState* tui, u8* asset, u32 len, u16 x, u16 y) { u16 line = 0; u16 x_in_line = 0; @@ -431,6 +387,9 @@ fn void handleIncomingMessage(u8* message, u32 len, SocketAddress sender, i32 so case MessageTurnTick: case MessageNewAccountCreated: case MessageBadPw: {/*nothing to parse but the type*/} break; + case MessageGameOver: { + parsed.byte = message[msg_pos++]; + } break; case MessageTransactionResult: { parsed.tx_result.buying = message[msg_pos++]; parsed.tx_result.qty = readU32FromBufferLE(message + msg_pos); @@ -551,6 +510,11 @@ 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 MessageGameOver: { + state->screen = state->me.id == msg.byte ? ScreenVictory : ScreenDefeat; + sprintf(sbuf,"got winner id: %d", msg.byte); + addSystemMessage((u8*)sbuf); + } break; case MessagePayoffResult: { state->ship_tab_states = ShipTabStateMain; } break; @@ -732,10 +696,8 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count u32 principal_borrowed = selected_ship_cost - STARTING_DOWN_PAYMENT; f32 mortgage_rate = calcInterestRate(selected_ship_cost, STARTING_DOWN_PAYMENT); f32 r = mortgage_rate / 100.0; - f32 rm = r / 12.0; - u32 n = 12 * 4; - u32 payment = principal_borrowed * (rm*pow((1.0+rm),n))/(pow((1+rm),n) - 1); - sprintf(sbuf, "Mortgage: $%d @ %f%% = $%d minimum payment per turn", selected_ship_cost - STARTING_DOWN_PAYMENT, mortgage_rate, payment); + u32 payment = principal_borrowed * r; + sprintf(sbuf, "Mortgage: $%d @ %f%% = $%d interest after first turn", selected_ship_cost - STARTING_DOWN_PAYMENT, mortgage_rate, payment); renderStrToBuffer(tui->frame_buffer, 5, ++line, sbuf, screen_dimensions); line++; // the table @@ -1359,23 +1321,21 @@ fn bool updateAndRender(TuiState* tui, void* s, u8* input_buffer, u64 loop_count case ScreenVictory: { // SIMULATION if (user_pressed_esc || input_buffer[0] == ASCII_RETURN || input_buffer[0] == ASCII_LINE_FEED) { - state->screen = ScreenMainGame; + should_quit = true; } // RENDERING renderStrToBuffer(tui->frame_buffer, 10, 10, "You WIN!!!", screen_dimensions); + renderStrToBuffer(tui->frame_buffer, 5, 11, "You paid off your ships mortgage and are a free trader!", screen_dimensions); } break; case ScreenDefeat: { // SIMULATION if (user_pressed_esc || input_buffer[0] == ASCII_RETURN || input_buffer[0] == ASCII_LINE_FEED) { - state->me.id = 0; - state->menu.selected_index = 0; - clearServerSentState(); - state->screen = ScreenCreateCharacter; + should_quit = true; } // RENDERING - renderStrToBuffer(tui->frame_buffer, 10, 10, "You died...", screen_dimensions); + renderStrToBuffer(tui->frame_buffer, 10, 10, "You lose, forever doomed to work for the bank...", screen_dimensions); renderStrToBuffer(tui->frame_buffer, 10, 11, "[press ESC to continue]", screen_dimensions); } break; case Screen_Count: diff --git a/src/server.c b/src/server.c index 0d9549f..f66105e 100644 --- a/src/server.c +++ b/src/server.c @@ -59,17 +59,6 @@ typedef struct ParsedClientCommandThreadQueue { Cond not_full; } ParsedClientCommandThreadQueue; -typedef struct Entity { - bool changed; - u8 x; - u8 y; - u8 color; - EntityType type; - u32 misc; - u64 id; - u64 features; -} Entity; - typedef struct Account { u8 destination_sys_idx; bool changed; @@ -79,26 +68,6 @@ typedef struct Account { PlayerShip ship; } Account; -typedef struct EntityList { - u64 length; // the currently used length - u64 capacity; - Entity* items; -} EntityList; - -typedef struct EntityChunk { - u64 length; // the currently used # of entities in this chunk - u64 capacity; // the "chunk size" / space in this chunk - struct EntityChunk* next; // the next chunk - Entity* items; // the actual entities -} EntityChunk; - -typedef struct ChunkedEntityList { - u64 length; // the current number of entities - u64 chunk_size; // the # of entities per chunk - u64 chunks; // the # of chunks in this list so far - EntityChunk* first; // the first chunk of entities -} ChunkedEntityList; - typedef struct Client { u16 lan_port; i32 lan_ip; @@ -134,9 +103,7 @@ typedef struct State { ///// Global Variables global State state = { 0 }; global Arena permanent_arena = { 0 }; -global const Entity NULL_ENTITY = { 0 }; global bool debug_mode = false; -global ChunkedEntityList free_chunks = { 0, CHUNK_SIZE, 0, NULL }; ///// functionImplementations() fn ParsedClientCommandThreadQueue* newPCCThreadQueue(Arena* a) { @@ -182,44 +149,6 @@ fn ParsedClientCommand* pccThreadSafeNonblockingQueuePop(ParsedClientCommandThre return result; } -fn u64 entitySerialize(Entity current, Account* acct, u64 index, u8 bytes[]) { - // send entity header (common to all entity types) - index += writeU64ToBufferLE(bytes + index, current.id); - index += writeU64ToBufferLE(bytes + index, current.features); - bytes[index++] = current.x; - bytes[index++] = current.y; - bytes[index++] = (u8)current.type; // EntityType - - //if (CheckFlag(current.features, FeatureRegensHp)) { - // index += writeU16ToBufferLE(bytes + index, current.hp); - // index += writeU16ToBufferLE(bytes + index, current.max_hp); - //} - - // send character-specific details (color and name) - if (current.type == EntityCharacter) { - bytes[index++] = current.color; - // send name string - index += writeU64ToBufferLE(bytes + index, acct->name.length); - for (u32 j = 0; j < acct->name.length; j++) { - bytes[index++] = acct->name.bytes[j]; - } - } - return index; -} - -fn u64 entityFeaturesFromType(EntityType type) { - u64 result = 0; - switch (type) { - case EntityCharacter: { - SetFlag(result, FeatureWalksAround); - SetFlag(result, FeatureCanFight); - } break; - default: { - } break; - } - return result; -} - fn Account* findAccountByName(String name) { for (u32 i = 0; i < ACCOUNT_LEN; i++) { if (stringsEq(&state.accounts[i].name, &name)) { @@ -229,93 +158,6 @@ fn Account* findAccountByName(String name) { return NULL; } -fn u64 pushEntity(EntityChunk* chunk, Entity e) { - assert(chunk->length < chunk->capacity); - chunk->items[chunk->length] = e; - u64 result = chunk->length; - chunk->length += 1; - return result; -} - -fn bool isSpaceInChunk(EntityChunk* chunk) { - return chunk->length < chunk->capacity; -} - -fn bool allChunksFull(ChunkedEntityList list) { - return list.length >= (list.chunk_size * list.chunks); -} - -fn EntityChunk* pushNewChunk(Arena* a, ChunkedEntityList* list) { - EntityChunk* new_chunk; - if (free_chunks.length > 0) { - new_chunk = free_chunks.first; - free_chunks.length -= 1; - free_chunks.first = new_chunk->next; - } else { - new_chunk = arenaAlloc(a, sizeof(EntityChunk)); - // alloc the new chunk of entities - new_chunk->length = 0; - new_chunk->capacity = list->chunk_size; - new_chunk->next = NULL; - new_chunk->items = arenaAllocArray(a, Entity, list->chunk_size); - } - // bookeeping in the list - list->chunks += 1; - if (list->first == NULL) { - list->first = new_chunk; - } else { - EntityChunk* last = list->first; - while (last->next != NULL) { - last = last->next; - } - last->next = new_chunk; - } - return new_chunk; -} - -fn Entity* entityPtrFromChunkList(ChunkedEntityList* list, i32 index) { - Entity* result = (Entity*)&NULL_ENTITY; - i32 chunk_index = index / list->chunk_size; - EntityChunk* chunk = list->first; - for (i32 i = 0; i < chunk_index; i++) { - chunk = chunk->next; - } - result = &chunk->items[index % list->chunk_size]; - return result; -} - -fn Entity entityFromChunkList(ChunkedEntityList* list, i32 index) { - Entity result = {0}; - i32 chunk_index = index / list->chunk_size; - EntityChunk* chunk = list->first; - for (i32 i = 0; i < chunk_index; i++) { - chunk = chunk->next; - } - result = chunk->items[index % list->chunk_size]; - return result; -} - -fn bool deleteLastEntity(ChunkedEntityList* list, EntityChunk* last_chunk, EntityChunk* second_to_last_chunk) { - last_chunk->length -= 1; - list->length -= 1; - // don't delete the room's only chunk, but otherwise move the chunk to the free-list - if (last_chunk->length == 0 && last_chunk != list->first) { - list->chunks -= 1; - second_to_last_chunk->next = NULL; - free_chunks.length += 1; - EntityChunk* last_free_chunk = free_chunks.first; - if (last_free_chunk) { - while (last_free_chunk->next != NULL) { - last_free_chunk = last_free_chunk->next; - } - last_free_chunk->next = last_chunk; - } else { - free_chunks.first = last_chunk; - } - } - return true; -} - fn void exitWithErrorMessage(ptr msg) { printf("error: %s", msg); exit(1); @@ -515,8 +357,7 @@ fn void handleIncomingMessage(u8* message, u32 len, SocketAddress sender, i32 so printf("Logging in player: %s %d %s\n", MESSAGE_STRINGS[parsed.type], name_len, message + 7); } break; - case CommandKeepAlive: - break; + case CommandKeepAlive: break; case CommandPayMortgage: { parsed.id = readU64FromBufferLE(message + msg_idx); printf("paying off %lld\n", parsed.id); @@ -720,8 +561,10 @@ fn void* gameLoop(void* params) { u32 qty_traded = 0; u32 credit_value = 0; if (is_buying_from_system) { - u32 ship_space = MAX_u32; - if (COMMODITIES[msg.commodity].unit == StorageUnitContainer) { + u32 ship_space = account->ship.cu_m_fuel - account->ship.commodities[CommodityHydrogenFuel]; + if (msg.commodity == CommodityOxygen) { + ship_space = account->ship.cu_m_o2 - account->ship.commodities[CommodityOxygen]; + } else if (COMMODITIES[msg.commodity].unit == StorageUnitContainer) { ship_space = account->ship.vacuum_cargo_slots - usedVacuumCargoSlots(account->ship); } for (u32 amount_to_buy = Min(msg.qty, total_available); amount_to_buy > 0; amount_to_buy--, total_available--, ship_space--) { @@ -858,12 +701,12 @@ fn void* gameLoop(void* params) { .remaining_mortgage = template.base_cost - STARTING_DOWN_PAYMENT, .interest_rate = calcInterestRate(template.base_cost, STARTING_DOWN_PAYMENT), .credits = 10000.0, - //.cu_m_fuel; // we are just saying you can buy as much fuel as you want - //.cu_m_o2; // we are just saying you can buy as much o2 as you want + .cu_m_fuel = template.cu_m_fuel, + .cu_m_o2 = template.cu_m_o2, .id = account->id, }; - player_ship.commodities[CommodityHydrogenFuel] = 2000; - player_ship.commodities[CommodityOxygen] = 1000; + player_ship.commodities[CommodityHydrogenFuel] = player_ship.cu_m_fuel; + player_ship.commodities[CommodityOxygen] = player_ship.cu_m_o2; account->ship = player_ship; printf("ship_type=%s, client_handle=%d, acct_id=%d\n", SHIP_TYPE_STRINGS[msg.byte], client_handle, account->id); u32 starting_system_idx = rand() % STAR_SYSTEM_COUNT; @@ -933,12 +776,15 @@ fn void* gameLoop(void* params) { outgoingMessageQueuePush(state.network_send_queue, &outgoing_message); } } - return NULL; } } unlockMutex(&state.mutex); unlockMutex(&state.client_mutex); } + if (state.someone_won) { + return NULL; + } + LaneSync(); u32 player_count = 0; @@ -990,20 +836,28 @@ fn void* gameLoop(void* params) { acct->ship.ready_to_depart = false; acct->changed = true; - // look for a winner + increase the mortgage from interest + // increase the mortgage from interest if (!shipIsNull(&acct->ship)) { - if (acct->ship.remaining_mortgage == 0) { - state.winner_id = acct->id; - state.someone_won = true; - printf("someone WON!!! %d\n", i); - break; - } else { + if (acct->ship.remaining_mortgage > 0) { acct->ship.remaining_mortgage += acct->ship.remaining_mortgage * (acct->ship.interest_rate / 100.0); } } } + } - // TODO pay the mortgage payment + // look for a winner + Range1u64 ship_range = LaneRange(player_count); + for (u32 i = ship_range.min; i < ship_range.max; i++) { + Account* acct = &state.accounts[i]; + if (!shipIsNull(&acct->ship)) { + if (acct->ship.remaining_mortgage == 0) { + state.winner_id = acct->id; + state.someone_won = true; + printf("someone WON!!! %d\n", i); + acct->changed = true; + break; + } + } } // 3. scratch cleanup @@ -1209,6 +1063,9 @@ i32 main(i32 argc, ptr argv[]) { osThreadJoin(game_threads[i], MAX_u64); } + osThreadJoin(recv_thread, MAX_u64); + osThreadJoin(send_thread, MAX_u64); + return 0; } diff --git a/src/shared.h b/src/shared.h index 80954a6..0b2b5db 100644 --- a/src/shared.h +++ b/src/shared.h @@ -6,7 +6,7 @@ ///// #define some game-tunable constants #define STARTING_DOWN_PAYMENT (10000) -#define SHIP_DETAIL_COUNT (8) +#define SHIP_DETAIL_COUNT (7) #define STAR_SYSTEM_COUNT (40) #define MAP_WIDTH (36) #define MAP_HEIGHT (12) @@ -34,14 +34,14 @@ typedef enum ShipType { ShipSparrow, ShipDart, ShipHaulerPrimeZ1, - ShipNX400, +// ShipNX400, ShipType_Count, } ShipType; global str SHIP_TYPE_STRINGS[] = { "Sparrow", "Dart", "Hauler-Prime Z-1", - "NX-400", +// "NX-400", }; typedef struct ShipTemplate { @@ -54,33 +54,39 @@ typedef struct ShipTemplate { u16 passenger_amenities_flags; u16 smugglers_hold_cu_m; u32 base_cost; + u32 cu_m_fuel; + u32 cu_m_o2; } ShipTemplate; global ShipTemplate SHIPS[] = { { - .type = ShipSparrow, .drive_efficiency = 1, .life_support_efficiency = 2, + .type = ShipSparrow, .drive_efficiency = 10, .life_support_efficiency = 2, .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, }, { - .type = ShipDart, .drive_efficiency = 12, .life_support_efficiency = 4, - .vacuum_cargo_slots = 9, .climate_cargo_slots = 1, .passenger_berths = 1, + .type = ShipDart, .drive_efficiency = 70, .life_support_efficiency = 4, + .vacuum_cargo_slots = 10, .climate_cargo_slots = 1, .passenger_berths = 1, .passenger_amenities_flags = 0, .smugglers_hold_cu_m = 0, .base_cost = 150000, + .cu_m_fuel = 2000, .cu_m_o2 = 1000, }, { - .type = ShipHaulerPrimeZ1, .drive_efficiency = 3, .life_support_efficiency = 3, - .vacuum_cargo_slots = 25, .climate_cargo_slots = 5, .passenger_berths = 0, + .type = ShipHaulerPrimeZ1, .drive_efficiency = 50, .life_support_efficiency = 3, + .vacuum_cargo_slots = 100, .climate_cargo_slots = 5, .passenger_berths = 0, .passenger_amenities_flags = 0, .smugglers_hold_cu_m = 0, .base_cost = 250000, + .cu_m_fuel = 2000, .cu_m_o2 = 1000, }, - { +/* { .type = ShipNX400, .drive_efficiency = 6, .life_support_efficiency = 8, .vacuum_cargo_slots = 5, .climate_cargo_slots = 5, .passenger_berths = 5, .passenger_amenities_flags = 0, .smugglers_hold_cu_m = 0, .base_cost = 350000, - }, + .cu_m_fuel = 2000, .cu_m_o2 = 1000, + },*/ }; fn f32 calcInterestRate(u32 cost, u32 down_payment) { @@ -93,10 +99,10 @@ global FieldDescriptor SHIP_FIELDS[SHIP_DETAIL_COUNT] = { { "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 }, + { "Cargo", FieldTypeU16, offsetof(ShipTemplate, vacuum_cargo_slots), 7 }, + { "FuelTank", FieldTypeU32, offsetof(ShipTemplate, cu_m_fuel), 8 }, + { "O2 Tank", FieldTypeU32, offsetof(ShipTemplate, cu_m_o2), 8 }, +// { "SmugglersHold", FieldTypeU16, offsetof(ShipTemplate, smugglers_hold_cu_m), 13 }, }; typedef enum EquipmentType {