#include "base/all.h" #include "string_chunk.h" #ifndef GAMESHARED_H #define GAMESHARED_H ///// #define some game-tunable constants #define STARTING_DOWN_PAYMENT (10000) #define SHIP_DETAIL_COUNT (8) #define STAR_SYSTEM_COUNT (6) #define MAP_WIDTH (28) #define MAP_HEIGHT (12) #define MAX_PLANETS (3) #define MAX_PASSENGER_JOB_OFFERS (12) #define MAX_PASSENGER_JOB_PEOPLE (4) #define MAX_PASSENGER_JOB_PRICE (10000) #define MAX_PASSENGER_BERTHS (8) #define AUCTION_COMMODITY_CUTOFF (7) #define AUCTION_PRICE_START_MULTIPLE (12) typedef enum ShipType { ShipSparrow, ShipDart, ShipHaulerPrimeZ1, ShipNX400, ShipType_Count, } ShipType; global str SHIP_TYPE_STRINGS[] = { "Sparrow", "Dart", "Hauler-Prime Z-1", "NX-400", }; typedef struct ShipTemplate { ShipType type; u8 drive_efficiency; u8 life_support_efficiency; u16 vacuum_cargo_slots; u16 climate_cargo_slots; u16 passenger_berths; 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 = 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 = 2500, .cu_m_o2 = 500, }, { .type = ShipDart, .drive_efficiency = 7, .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 = 1500, }, { .type = ShipHaulerPrimeZ1, .drive_efficiency = 5, .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 = 275000, .cu_m_fuel = 2500, .cu_m_o2 = 800, }, { .type = ShipNX400, .drive_efficiency = 6, .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 = 250000, .cu_m_fuel = 2000, .cu_m_o2 = 3000, }, }; global FieldDescriptor SHIP_FIELDS[SHIP_DETAIL_COUNT] = { { "Type", FieldTypeEnum, offsetof(ShipTemplate, type), 16, (str*)&SHIP_TYPE_STRINGS }, { "Cost", FieldTypeU32, offsetof(ShipTemplate, base_cost), 8 }, { "Cargo", FieldTypeU16, offsetof(ShipTemplate, vacuum_cargo_slots), 7 }, { "PassengerBerths", FieldTypeU16, offsetof(ShipTemplate, passenger_berths), 16 }, { "DriveEff", FieldTypeU8, offsetof(ShipTemplate, drive_efficiency), 8 }, { "LifeSupp", FieldTypeU8, offsetof(ShipTemplate, life_support_efficiency), 8 }, { "FuelTank", FieldTypeU32, offsetof(ShipTemplate, cu_m_fuel), 8 }, { "O2 Tank", FieldTypeU32, offsetof(ShipTemplate, cu_m_o2), 8 }, }; typedef enum CommodityType { CommodityHydrogenFuel, CommodityOxygen, CommodityWater, CommodityFood, CommodityRawTextiles, CommodityOre, CommodityPlastics, CommoditySemiConductors, CommodityMetals, CommodityGlass, CommodityHandTools, CommodityElectronics, Commodity_Count, } CommodityType; global str COMMODITY_STRINGS[Commodity_Count] = { "Hydrogen Fuel", "Oxygen", "Water", "Food", "Raw Textiles", "Ore", "Plastics", "Semi-conductors", "Metals", "Glass", "Hand Tools", "Electronics", }; typedef enum StorageUnit { StorageUnitKg, StorageUnitContainer, StorageUnitAtmoContainer, StorageUnit_Count, } StorageUnit; global str STORAGE_UNIT_STRINGS[StorageUnit_Count] = { "kg", "cont", "atmo" }; typedef struct Commodity { CommodityType type; StorageUnit unit; str name; u32 price; u32 qty; u32 consumption; } Commodity; global Commodity COMMODITIES[Commodity_Count] = { { .type = CommodityHydrogenFuel, .unit = StorageUnitKg, .name = "Hydrogen Fuel", .price = 10, .qty = 10000, .consumption = 100, }, { .type = CommodityOxygen, .unit = StorageUnitKg, .name = "Oxygen", .price = 5, .qty = 5000, .consumption = 80, }, { .type = CommodityWater, .unit = StorageUnitContainer, .name = "Water", .price = 100, .qty = 100, .consumption = 10, }, { .type = CommodityFood, .unit = StorageUnitContainer, .name = "Food", .price = 200, .qty = 70, .consumption = 10, }, { .type = CommodityRawTextiles, .unit = StorageUnitContainer, .name = "Raw Textiles", .price = 400, .qty = 40, .consumption = 3, }, { .type = CommodityOre, .unit = StorageUnitContainer, .name = "Ore", .price = 600, .qty = 100, .consumption = 8, }, { .type = CommodityPlastics, .unit = StorageUnitContainer, .name = "Plastics", .price = 800, .qty = 30, .consumption = 5, }, { .type = CommoditySemiConductors, .unit = StorageUnitContainer, .name = "Semi-conductors", .price = 1000, .qty = 40, .consumption = 3, }, { .type = CommodityMetals, .unit = StorageUnitContainer, .name = "Metals", .price = 2000, .qty = 30, .consumption = 3, }, { .type = CommodityGlass, .unit = StorageUnitContainer, .name = "Glass", .price = 4000, .qty = 30, .consumption = 3, }, { .type = CommodityHandTools, .unit = StorageUnitContainer, .name = "Hand Tools", .price = 5000, .qty = 30, .consumption = 3, }, { .type = CommodityElectronics, .unit = StorageUnitContainer, .name = "Electronics", .price = 9000, .qty = 30, .consumption = 3, }, }; typedef struct MarketCommodity { CommodityType type; StorageUnit unit; u32 bid; u32 ask; u32 qty; u32 owned; } MarketCommodity; global FieldDescriptor MARKET_COMMODITY_FIELDS[Commodity_Count] = { { "Commodity", FieldTypeEnum, offsetof(MarketCommodity, type), 44, (str*)&COMMODITY_STRINGS }, { "Unit", FieldTypeEnum, offsetof(MarketCommodity, unit), 6, (str*)&STORAGE_UNIT_STRINGS }, { "#", FieldTypeU32, offsetof(MarketCommodity, qty), 6 }, { "Bid", FieldTypeU32, offsetof(MarketCommodity, bid), 6 }, { "Ask", FieldTypeU32, offsetof(MarketCommodity, ask), 6 }, { "Owned", FieldTypeU32, offsetof(MarketCommodity, owned), 6 }, }; typedef struct Passenger { u8 goal_system_idx; u8 people; 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; u8 goal_system_idx; 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; u8 system_idx; u8 drive_efficiency; u8 life_support_efficiency; u16 vacuum_cargo_slots; u16 climate_cargo_slots; u16 passenger_berths; u16 passenger_amenities_flags; u16 smugglers_hold_cu_m; u32 base_cost; u32 remaining_mortgage; f32 interest_rate; u32 cu_m_fuel; u32 cu_m_o2; u32 credits; u64 id; u32 commodities[Commodity_Count]; Passenger passengers[MAX_PASSENGER_BERTHS]; } PlayerShip; typedef enum PlanetType { PlanetTypeNull, PlanetTypeEarth, PlanetTypeGas, PlanetTypeMoon, PlanetTypeAsteroid, PlanetTypeStation, PlanetType_Count, } PlanetType; typedef struct Planet { PlanetType type; u32 commodities[Commodity_Count]; u32 production[Commodity_Count]; } Planet; typedef struct Auction { CommodityType type; u8 qty; u32 price; u32 started_at; u32 finished_at; } Auction; typedef struct StarSystem { bool changed; u32 x; u32 y; u32 crime; u32 idx; str name; Planet planets[MAX_PLANETS]; PassengerJobOffer offers[MAX_PASSENGER_JOB_OFFERS]; Auction auction; } StarSystem; global str STAR_NAMES[STAR_SYSTEM_COUNT] = { "Vega", "Aldebaran", "Mining Colony 17", "Centauri Prime", "Mintaka", "Barnard's Star", /* "Betelgeuse", "Antares", "Tau Ceti", "Capella", "Castor", "Deneb", "Epsilon Eridani", "Fomalhaut", "Gliese 581", "Hadar", "Izar", "Achernar", "Kepler-186", "Lacaille 8760", "Lalande 21185", "Arcturus", "Canopus", "Mirach", "Polaris", "Pollux", "Procyon", "Proxima", "Regulus", "Rigel", "Ross 154", "Sirius", "Spica", "Trappist-1", "Wolf 359", "Zeta Reticuli", "Alnitak", "Bellatrix", "Denebola", "Eltanin", */ }; typedef enum AuctionBidResult { AuctionBidResultPurchased, AuctionBidResultNotEnoughMoney, AuctionBidResultNotEnoughCargoSpace, AuctionBidResultAuctionAlreadyFinished, AuctionBidResult_Count } AuctionBidResult; typedef enum Direction { DirectionInvalid, North, South, East, West, Up, Down, Direction_Count } Direction; typedef enum CommandType { CommandInvalid, CommandKeepAlive, CommandLogin, CommandCreateCharacter, CommandTransact, CommandReadyStatus, CommandSetDestination, CommandPayMortgage, CommandAcceptPassengerJob, CommandBuyAuction, CommandType_Count, } CommandType; static const char* command_type_strings[CommandType_Count] = { "Invalid", "KeepAlive", "Login", "CreateCharacter", "ReadyStatus", "SetDestination", "PayMortgage", "AcceptPassengerJob", "BuyAuction", }; typedef enum Message { MessageInvalid, MessageCharacterId, MessageBadPw, MessageNewAccountCreated, MessageStarPositions, MessageSystemCommodities, MessagePlayerDetails, MessageTransactionResult, MessageTurnTick, MessageGameOver, MessagePayoffResult, MessageSystemPassengers, MessageJobAcceptResult, MessageJobComplete, MessageNotAlive, MessageAuctionDetails, MessageAuctionBidResult, Message_Count, } Message; static const char* MESSAGE_STRINGS[] = { "Invalid", "CharacterId", "BadPw", "NewAccountCreated", "StarPositions", "SystemCommodities", "PlayerDetails", "TransactionResult", "TurnTick", "GameOver", "PayoffResult", "SystemPassengers", "JobAcceptResult", "NotAlive", "AuctionDetails", "AuctionBidResult", }; ///// shared helper functions fn u32 fuelCostForTravel(u32 drive_efficiency, Pos2 current, Pos2 dest) { u32 x_distance = Max(current.x, dest.x) - Min(current.x, dest.x); u32 y_distance = Max(current.y, dest.y) - Min(current.y, dest.y); u32 fuel_per_dist = 100 - drive_efficiency*5; return (x_distance + y_distance) * fuel_per_dist; } fn u32 usedVacuumCargoSlots(PlayerShip ship) { u32 used_cargo = 0; for (u32 i = 0; i < Commodity_Count; i++) { if (COMMODITIES[i].unit == StorageUnitContainer) { used_cargo += ship.commodities[i]; } } return used_cargo; } fn f32 priceForCommodity(CommodityType type, u32 quantity, bool bid) { f32 result = 0.0; Commodity details = COMMODITIES[type]; f32 max = details.price * 3; f32 min = (f32)details.price / 3.0; if (quantity == 0) { return max; } result = ((f32)details.price) * ((f32)details.qty / (f32)quantity); // the bid/ask spread if (max < result) { result = max; } if (min > result) { result = min; } // the bid/ask spread if (bid) { result = result * 0.97; } else { result = result * 1.03; } return result; } fn f32 calcInterestRate(u32 cost, u32 down_payment) { return 4.0; //f32 mortgage_rate = 10.0 *(1.0 - (3.0*(f32)down_payment / ((f32)cost/3.0))); //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 u32 shipTotalPassengers(PlayerShip ship) { u32 count = 0; for (u32 i = 0; i < MAX_PASSENGER_BERTHS; i++) { if (ship.passengers[i].people > 0) { count += ship.passengers[i].people; } } return count; } fn u32 shipUsedPassengerBerths(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 used; } fn u32 shipAvailablePassengerBerths(PlayerShip ship) { return (u32)ship.passenger_berths - shipUsedPassengerBerths(ship); } fn u32 oxyCostForTravel(PlayerShip* ship, Pos2 current, Pos2 dest) { u32 x_distance = Max(current.x, dest.x) - Min(current.x, dest.x); u32 y_distance = Max(current.y, dest.y) - Min(current.y, dest.y); u32 oxy_per_person_per_dist = 15 - ship->life_support_efficiency; // +1 because the pilot uses oxy as well return (x_distance + y_distance) * oxy_per_person_per_dist * (shipTotalPassengers(*ship) + 1); } #endif //GAMESHARED_H