519 lines
13 KiB
C
519 lines
13 KiB
C
#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
|