interim changes

isolation
Dominik Madarász 2022-09-28 05:29:32 +00:00 committed by GitHub
parent 0c95f1148a
commit 7aadeed518
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 1878 additions and 307 deletions

View File

@ -3,6 +3,7 @@ file(GLOB PKT_SRCS src/packets/*.h src/packets/*.c)
add_library(eco2d-foundation STATIC
src/core/game.c
src/core/camera.c
src/core/rules_default.c
src/platform/signal_handling.c
src/platform/profiler.c
@ -13,6 +14,7 @@ add_library(eco2d-foundation STATIC
src/ents/vehicle.c
src/ents/storage.c
src/ents/device.c
src/ents/furnace.c
src/pkt/packet.c
@ -30,10 +32,8 @@ add_library(eco2d-foundation STATIC
src/world/world_view.c
src/world/entity_view.c
src/world/prediction.c
src/world/worldgen/worldgen_test.c
src/gen/assets.c
src/gen/texgen.c
src/ecs/systems.c
src/ecs/components.c

View File

@ -0,0 +1,31 @@
#pragma once
#include "platform/system.h"
typedef struct {
float phy_walk_drag;
uint64_t demo_npc_move_speed;
uint64_t demo_npc_steer_speed;
float furnace_cook_time;
float item_pick_radius;
float item_merger_radius;
float item_attract_radius;
float item_attract_force;
float item_container_reach_radius;
float item_drop_pickup_time;
float item_drop_merger_time;
float plr_move_speed;
float plr_move_speed_mult;
float vehicle_force;
float vehicle_accel;
float vehicle_decel;
float vehicle_steer;
float vehicle_steer_compensation;
float vehicle_steer_revert;
float vehicle_power;
float vehicle_brake_force;
float veh_enter_radius;
} game_rulesdef;
extern game_rulesdef game_rules;
void rules_setup();

View File

@ -0,0 +1,26 @@
#include "core/rules.h"
game_rulesdef game_rules = (game_rulesdef){
.phy_walk_drag = 4.23f,
.demo_npc_move_speed = 500,
.demo_npc_steer_speed = 300,
.furnace_cook_time = 5.0f,
.item_pick_radius = 25.0f,
.item_merger_radius = 75.0f,
.item_attract_radius = 75.0f,
.item_attract_force = .98f,
.item_container_reach_radius = 105.0f,
.item_drop_pickup_time = 2.5f,
.item_drop_merger_time = 6.5f,
.plr_move_speed = 800.0f,
.plr_move_speed_mult = 1.5f,
.vehicle_force = 240.8f,
.vehicle_accel = 0.032f,
.vehicle_decel = 0.28f,
.vehicle_steer = 35.89f,
.vehicle_steer_compensation = 4.0f,
.vehicle_steer_revert = 6.0941816f,
.vehicle_power = 97.89f,
.vehicle_brake_force = 0.84f,
.veh_enter_radius = 45.0f,
};

View File

@ -138,6 +138,7 @@ static debug_item items[] = {
{ .kind = DITEM_BUTTON, .name = "spawn icemaker item", .on_click = ActSpawnIcemaker },
{ .kind = DITEM_BUTTON, .name = "spawn chest", .on_click = ActSpawnChest },
{ .kind = DITEM_BUTTON, .name = "spawn belt", .on_click = ActSpawnBelt },
{ .kind = DITEM_BUTTON, .name = "spawn furnace", .on_click = ActSpawnFurnace },
{
.kind = DITEM_LIST,
.name = "demo npcs",

View File

@ -60,6 +60,17 @@ ActSpawnBelt(void) {
debug_replay_special_action(RPKIND_SPAWN_BELT);
}
void
ActSpawnFurnace(void) {
ecs_entity_t e = item_spawn(ASSET_FURNACE, 32);
ecs_entity_t plr = camera_get().ent_id;
Position const* origin = ecs_get(world_ecs(), plr, Position);
Position * dest = ecs_get_mut(world_ecs(), e, Position);
*dest = *origin;
entity_set_position(e, dest->x, dest->y);
}
void
ActSpawnCirclingDriver(void) {
ecs_entity_t plr = camera_get().ent_id;

View File

@ -11,9 +11,10 @@ ECS_COMPONENT_DECLARE(Health);
ECS_COMPONENT_DECLARE(Classify);
ECS_COMPONENT_DECLARE(Vehicle);
ECS_COMPONENT_DECLARE(IsInVehicle);
ECS_COMPONENT_DECLARE(ItemDrop);
ECS_COMPONENT_DECLARE(Item);
ECS_COMPONENT_DECLARE(Inventory);
ECS_COMPONENT_DECLARE(ItemContainer);
ECS_COMPONENT_DECLARE(Furnace);
ECS_COMPONENT_DECLARE(Device);
ECS_COMPONENT_DECLARE(DemoNPC);
ECS_COMPONENT_DECLARE(StreamInfo);
@ -32,9 +33,10 @@ void ComponentsImport(ecs_world_t *ecs) {
ECS_COMPONENT_DEFINE(ecs, Classify);
ECS_COMPONENT_DEFINE(ecs, Vehicle);
ECS_COMPONENT_DEFINE(ecs, IsInVehicle);
ECS_COMPONENT_DEFINE(ecs, ItemDrop);
ECS_COMPONENT_DEFINE(ecs, Item);
ECS_COMPONENT_DEFINE(ecs, Inventory);
ECS_COMPONENT_DEFINE(ecs, ItemContainer);
ECS_COMPONENT_DEFINE(ecs, Furnace);
ECS_COMPONENT_DEFINE(ecs, Device);
ECS_COMPONENT_DEFINE(ecs, DemoNPC);
ECS_COMPONENT_DEFINE(ecs, StreamInfo);

View File

@ -100,17 +100,35 @@ typedef struct {
uint16_t kind;
uint32_t quantity;
float merger_time;
} ItemDrop;
} Item;
typedef struct {
ItemDrop items[ITEMS_CONTAINER_SIZE];
uint64_t ent;
} ItemSlot;
typedef struct {
ItemSlot items[ITEMS_CONTAINER_SIZE];
float pickup_time;
} Inventory;
typedef struct {
ItemDrop items[ITEMS_CONTAINER_SIZE];
ItemSlot items[ITEMS_CONTAINER_SIZE];
} ItemContainer;
typedef struct {
ItemSlot processed_item;
float cook_time;
float burn_time;
} Furnace;
// typedef struct {
// float burn_time;
// } Fuel;
// typedef struct {
// asset_id converted_kind;
// } FuelTank;
typedef struct {
uint16_t asset;
} Device;
@ -133,9 +151,10 @@ extern ECS_COMPONENT_DECLARE(Health);
extern ECS_COMPONENT_DECLARE(Classify);
extern ECS_COMPONENT_DECLARE(Vehicle);
extern ECS_COMPONENT_DECLARE(IsInVehicle);
extern ECS_COMPONENT_DECLARE(ItemDrop);
extern ECS_COMPONENT_DECLARE(Item);
extern ECS_COMPONENT_DECLARE(Inventory);
extern ECS_COMPONENT_DECLARE(ItemContainer);
extern ECS_COMPONENT_DECLARE(Furnace);
extern ECS_COMPONENT_DECLARE(Device);
extern ECS_COMPONENT_DECLARE(DemoNPC);
extern ECS_COMPONENT_DECLARE(StreamInfo);

View File

@ -1,16 +1,12 @@
#define DEMO_NPC_MOVE_SPEED 500
#define DEMO_NPC_STEER_SPEED 300
#include <math.h>
void DemoNPCMoveAround(ecs_iter_t *it) {
Velocity *v = ecs_field(it, Velocity, 1);
for (int i = 0; i < it->count; i++) {
float d = zpl_quake_rsqrt(v[i].x*v[i].x + v[i].y*v[i].y);
v[i].x += (v[i].x*d*DEMO_NPC_MOVE_SPEED*safe_dt(it) + zpl_cos(zpl_to_radians((float)(rand()%360)))*DEMO_NPC_STEER_SPEED*safe_dt(it));
v[i].y += (v[i].y*d*DEMO_NPC_MOVE_SPEED*safe_dt(it) + zpl_sin(zpl_to_radians((float)(rand()%360)))*DEMO_NPC_STEER_SPEED*safe_dt(it));
v[i].x += (v[i].x*d*game_rules.demo_npc_move_speed*safe_dt(it) + zpl_cos(zpl_to_radians((float)(rand()%360)))*game_rules.demo_npc_steer_speed*safe_dt(it));
v[i].y += (v[i].y*d*game_rules.demo_npc_move_speed*safe_dt(it) + zpl_sin(zpl_to_radians((float)(rand()%360)))*game_rules.demo_npc_steer_speed*safe_dt(it));
entity_wake(it->entities[i]);
}
}

View File

@ -0,0 +1,38 @@
void FurnaceCook(ecs_iter_t *it) {
ItemContainer *storage = ecs_field(it, ItemContainer, 1);
Furnace *furnace = ecs_field(it, Furnace, 2);
Position *p = ecs_field(it, Position, 3);
for (int i = 0; i < it->count; i++) {
for (int j = 0; j < ITEMS_CONTAINER_SIZE; j++) {
if (storage[i].items[j].kind == ASSET_COAL) {
// furnace[i].fuel_level += itemdb_get(storage[i].items[j].uuid, ITEMDB_FUEL_LEVEL);
// furnace[i].fuel_level = zpl_clamp(furnace[i].fuel_level + 0.8f, 0.0f, 1.0f);
storage[i].items[j].quantity--;
if (storage[i].items[j].quantity == 0) {
storage[i].items[j] = (ItemSlot){0};
}
continue;
}
// if (furnace[i].fuel_level <= 0.0f) continue;
if (furnace[i].cook_time < game_time()) {
if (furnace[i].processed_item.kind > 0) {
uint64_t e = item_spawn(furnace[i].processed_item.kind, 1);
entity_set_position(e, p[i].x, p[i].y);
furnace[i].processed_item.kind = 0;
} else {
if (storage[i].items[j].kind == ASSET_DEMO_ICEMAKER) {
furnace[i].processed_item.kind = ASSET_BELT;
storage[i].items[j].quantity--;
if (storage[i].items[j].quantity == 0) {
storage[i].items[j] = (ItemSlot){0};
}
furnace[i].cook_time = game_time() + game_rules.furnace_cook_time;
}
}
}
}
}
}

View File

@ -1,70 +1,65 @@
#include "ents/items.h"
#define ITEM_PICK_RADIUS 25.0f
#define ITEM_MERGER_RADIUS 75.0f
#define ITEM_ATTRACT_RADIUS 75.0f
#define ITEM_ATTRACT_FORCE .98f
#define ITEM_CONTAINER_REACH_RADIUS 105.0f
void PickItem(ecs_iter_t *it) {
Position *p = ecs_field(it, Position, 2);
Inventory *inv = ecs_field(it, Inventory, 3);
for (int i = 0; i < it->count; i++) {
if (inv[i].pickup_time > game_time()) continue;
size_t ents_count;
int64_t *ents = world_chunk_query_entities(it->entities[i], &ents_count, 2);
for (size_t j = 0; j < ents_count; j++) {
ItemDrop *drop = 0;
if ((drop = ecs_get_mut_if(it->world, ents[j], ItemDrop))) {
Item *drop = 0;
if ((drop = ecs_get_mut_if(it->world, ents[j], Item))) {
Position *p2 = ecs_get_mut(it->world, ents[j], Position);
Velocity *v2 = ecs_get_mut(it->world, ents[j], Velocity);
float dx = p2->x - p[i].x;
float dy = p2->y - p[i].y;
float range = zpl_sqrt(dx*dx + dy*dy);
if (range <= ITEM_PICK_RADIUS) {
if (range <= game_rules.item_pick_radius) {
uint16_t drop_id = item_find(drop->kind);
for (size_t k = 0; k < ITEMS_INVENTORY_SIZE; k += 1) {
ItemDrop *item = &inv[i].items[k];
ItemSlot *item_slot = &inv[i].items[k];
Item *item = item_get_data(item_slot->ent);
uint16_t item_id = item_find(item->kind);
if (item_id != ASSET_INVALID && (item->quantity == 0 || (item->quantity != 0 && item->kind == drop->kind)) && item->quantity < item_max_quantity(drop_id)) {
if (item_id != ASSET_INVALID && (item->quantity == 0 || (item->quantity != 0 && item->kind == drop->kind)) && item->quantity < item_max_quantity(drop_id)
&& (item_slot->ent == 0 || item_slot->ent == ents[j])) {
uint32_t picked_count = zpl_max(0, drop->quantity);
picked_count = zpl_clamp(picked_count, 0, item_max_quantity(drop_id) - item->quantity);
item_slot->ent = ents[j];
item->quantity += picked_count;
drop->quantity -= picked_count;
item->kind = drop->kind;
entity_wake(ents[j]);
if (drop->quantity == 0)
item_despawn(ents[j]);
item_show(ents[j], false);
item_slot->ent = ents[j];
break;
}
}
} else if (range <= ITEM_ATTRACT_RADIUS) {
v2->x = (p[i].x - p2->x) * ITEM_ATTRACT_FORCE;
v2->y = (p[i].y - p2->y) * ITEM_ATTRACT_FORCE;
} else if (range <= game_rules.item_attract_radius) {
v2->x = (p[i].x - p2->x) * game_rules.item_attract_force;
v2->y = (p[i].y - p2->y) * game_rules.item_attract_force;
}
}
}
}
}
#define ITEM_DROP_PICKUP_TIME 2.5f
#define ITEM_DROP_MERGER_TIME 6.5f
void DropItem(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1);
Position *p = ecs_field(it, Position, 2);
Inventory *inv = ecs_field(it, Inventory, 3);
for (int i = 0; i < it->count; i++) {
if (!in[i].drop) continue;
ItemDrop *items = inv[i].items;
ItemSlot *items = inv[i].items;
if (in[i].storage_action){
if (world_entity_valid(in[i].storage_ent)){
ItemContainer *ic = 0;
@ -77,42 +72,44 @@ void DropItem(ecs_iter_t *it) {
continue;
}
}
ItemDrop *item = &items[in[i].storage_action ? in[i].storage_selected_item : in[i].selected_item];
if (item->quantity <= 0)
ItemSlot *item_slot = &items[in[i].storage_action ? in[i].storage_selected_item : in[i].selected_item];
Item *item = item_get_data(item_slot->ent);
if (!item || item->quantity <= 0)
continue;
uint32_t dropped_count = item->quantity;
if (in[i].sprint) {
dropped_count /= 2;
} else if (in[i].ctrl) {
dropped_count = dropped_count > 0 ? 1 : 0;
}
if (dropped_count == 0)
continue;
ecs_entity_t te = item_spawn(item->kind, dropped_count);
ecs_entity_t te = item_slot->ent;
item_show(item_slot->ent, true);
item->quantity -= dropped_count;
ItemDrop *d = ecs_get_mut(world_ecs(), te, ItemDrop);
*d = (ItemDrop){
.kind = item->kind,
.quantity = dropped_count,
.merger_time = game_time() + ITEM_DROP_MERGER_TIME,
.merger_time = game_time() + game_rules.item_drop_merger_time,
};
Position *ipos = ecs_get_mut(it->world, te, Position);
*ipos = p[i];
Velocity *v = ecs_get_mut(it->world, te, Velocity);
v->x = in[i].mx * 800.0f;
v->y = in[i].my * 800.0f;
inv[i].pickup_time = game_time() + ITEM_DROP_PICKUP_TIME;
inv[i].pickup_time = game_time() + game_rules.item_drop_pickup_time;
in[i].drop = false;
if (item->quantity == 0) {
item->kind = 0;
}
@ -122,28 +119,28 @@ void DropItem(ecs_iter_t *it) {
void MergeItems(ecs_iter_t *it) {
Position *p = ecs_field(it, Position, 1);
ItemDrop *id = ecs_field(it, ItemDrop, 2);
for (int i = 0; i < it->count; i += 1) {
ItemDrop *item = &id[i];
if (item->merger_time < game_time())
continue;
size_t ents_count;
int64_t *ents = world_chunk_query_entities(it->entities[i], &ents_count, 1);
for (size_t j = 0; j < ents_count; j++) {
ItemDrop *drop = 0;
if ((drop = ecs_get_mut_if(it->world, ents[j], ItemDrop))) {
if (drop->kind != item->kind || (ecs_entity_t)ents[j] == it->entities[i] || drop->quantity == 0 || item->quantity == 0)
continue;
Position const* p2 = ecs_get(it->world, ents[j], Position);
float dx = p2->x - (p[i].x);
float dy = p2->y - (p[i].y);
float range = zpl_sqrt(dx*dx + dy*dy);
if (range <= ITEM_MERGER_RADIUS) {
if (range <= game_rules.item_merger_radius) {
drop->quantity += item->quantity;
item_despawn(it->entities[i]);
break;
@ -156,12 +153,12 @@ void MergeItems(ecs_iter_t *it) {
void SwapItems(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1);
Inventory *inv = ecs_field(it, Inventory, 2);
for (int i = 0; i < it->count; i++) {
if (!in[i].swap) continue;
ItemDrop *items = inv[i].items;
if (in[i].storage_action){
if (world_entity_valid(in[i].storage_ent)){
ItemContainer *ic = 0;
@ -174,13 +171,13 @@ void SwapItems(ecs_iter_t *it) {
continue;
}
}
ItemDrop *to = 0;
ItemDrop *from = 0;
if (in[i].swap_storage){
in[i].swap_storage = false;
if (in[i].storage_action){
from = &inv[i].items[in[i].swap_from];
to = &items[in[i].swap_to];
@ -201,11 +198,11 @@ void SwapItems(ecs_iter_t *it) {
from = &items[in[i].swap_from];
to = &items[in[i].swap_to];
}
ZPL_ASSERT(from && to);
uint16_t to_id = item_find(to->kind);
if (to == from) {
// NOTE(zaklaus): do nothing
} else if (to->kind == from->kind && to->quantity > 0) {
@ -218,7 +215,7 @@ void SwapItems(ecs_iter_t *it) {
swapped_count = zpl_clamp(swapped_count, 0, item_max_quantity(to_id) - to->quantity);
to->quantity += swapped_count;
from->quantity -= swapped_count;
if (swapped_count == 0) {
ItemDrop tmp = *to;
*to = *from;
@ -238,7 +235,7 @@ void SwapItems(ecs_iter_t *it) {
*to = *from;
*from = tmp;
}
in[i].swap = false;
}
}
@ -247,24 +244,24 @@ void UseItem(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1);
Position *p = ecs_field(it, Position, 2);
Inventory *inv = ecs_field(it, Inventory, 3);
for (int i = 0; i < it->count; i++) {
if (!in[i].use && !in[i].num_placements) continue;
if (in[i].storage_action){
continue;
}
ItemDrop *item = &inv[i].items[in[i].selected_item];
uint16_t item_id = 0;
item_usage usage = UKIND_DELETE;
if (!in[i].deletion_mode){
item_id = item_find(item->kind);
usage = item_get_usage(item_id);
if (!item || item->quantity <= 0) continue;
}
if (!in[i].use && usage == UKIND_DELETE){
for (size_t j = 0; j < in[i].num_placements; j++) {
world_chunk_destroy_block(in[i].placements_x[j], in[i].placements_y[j], true);
@ -289,25 +286,25 @@ void UseItem(ecs_iter_t *it) {
// NOTE(zaklaus): ensure we pick the first variant
ofs = 1;
}
for (size_t j = 0; j < in[i].num_placements; j++) {
Position pos = {.x = in[i].placements_x[j], .y = in[i].placements_y[j]};
item_use(it->world, item, pos, ofs);
}
in[i].num_placements = 0;
}
entity_wake(it->entities[i]);
}
}
void InspectContainers(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1);
for (int i = 0; i < it->count; ++i) {
if (!in[i].pick) continue;
if ((in[i].sel_ent && ecs_get(it->world, in[i].sel_ent, ItemContainer)) || !in[i].sel_ent)
in[i].storage_ent = in[i].sel_ent;
}
@ -316,21 +313,21 @@ void InspectContainers(ecs_iter_t *it) {
void HarvestIntoContainers(ecs_iter_t *it) {
ItemContainer *in = ecs_field(it, ItemContainer, 1);
Position *p = ecs_field(it, Position, 2);
for (int i = 0; i < it->count; ++i) {
// NOTE(zaklaus): find any item
size_t ents_count;
int64_t *ents = world_chunk_query_entities(it->entities[i], &ents_count, 0);
for (size_t j = 0; j < ents_count; j++) {
ItemDrop *drop = 0;
if ((drop = ecs_get_mut_if(it->world, ents[j], ItemDrop))) {
const Position *p2 = ecs_get(it->world, ents[j], Position);
float dx = p2->x - p[i].x;
float dy = p2->y - p[i].y;
float range = zpl_sqrt(dx*dx + dy*dy);
if (range <= ITEM_PICK_RADIUS) {
if (range <= game_rules.item_pick_radius) {
uint16_t drop_id = item_find(drop->kind);
for (size_t k = 0; k < ITEMS_CONTAINER_SIZE; k += 1) {
ItemDrop *item = &in->items[k];
@ -343,7 +340,7 @@ void HarvestIntoContainers(ecs_iter_t *it) {
item->kind = drop->kind;
entity_wake(ents[j]);
entity_wake(it->entities[i]);
if (drop->quantity == 0)
item_despawn(ents[j]);
}

View File

@ -1,8 +1,5 @@
#include "ents/entity.h"
#define PLR_MOVE_SPEED 800.0f
#define PLR_MOVE_SPEED_MULT 1.5f
void MovementImpulse(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1);
Velocity *v = ecs_field(it, Velocity, 2);
@ -11,7 +8,7 @@ void MovementImpulse(ecs_iter_t *it) {
for (int i = 0; i < it->count; i++) {
world_block_lookup lookup = world_block_from_realpos(p[i].x, p[i].y);
float drag = zpl_clamp(blocks_get_drag(lookup.bid), 0.0f, 1.0f);
float speed = PLR_MOVE_SPEED * (in[i].sprint ? PLR_MOVE_SPEED_MULT : 1.0f);
float speed = game_rules.plr_move_speed * (in[i].sprint ? game_rules.plr_move_speed_mult : 1.0f);
v[i].x += in[i].x*speed*drag*safe_dt(it);
v[i].y -= in[i].y*speed*drag*safe_dt(it);

View File

@ -1,8 +1,6 @@
#include "debug/debug_draw.h"
#include "ents/entity.h"
#define VEH_ENTER_RADIUS 45.0f
void LeaveVehicle(ecs_iter_t *it) {
Input *in = ecs_field(it, Input, 1);
IsInVehicle *vehp = ecs_field(it, IsInVehicle, 2);
@ -58,7 +56,7 @@ void EnterVehicle(ecs_iter_t *it) {
float dx = p2->x - p[i].x;
float dy = p2->y - p[i].y;
float range = zpl_sqrt(dx*dx + dy*dy);
if (range <= VEH_ENTER_RADIUS) {
if (range <= game_rules.veh_enter_radius) {
for (int k = 0; k < 4; k++) {
if (veh->seats[k] != 0) continue;
@ -78,15 +76,6 @@ void EnterVehicle(ecs_iter_t *it) {
}
}
#define VEHICLE_FORCE 240.8f
#define VEHICLE_ACCEL 0.032f
#define VEHICLE_DECEL 0.28f
#define VEHICLE_STEER 35.89f
#define VEHICLE_STEER_COMPENSATION 4.0f
#define VEHICLE_STEER_REVERT 6.0941816f
#define VEHICLE_POWER 97.89f
#define VEHICLE_BRAKE_FORCE 0.84f
void VehicleHandling(ecs_iter_t *it) {
Vehicle *veh = ecs_field(it, Vehicle, 1);
Position *p = ecs_field(it, Position, 2);
@ -108,16 +97,16 @@ void VehicleHandling(ecs_iter_t *it) {
if (j == 0) {
Input const* in = ecs_get(it->world, pe, Input);
car->force += zpl_lerp(0.0f, in->y * VEHICLE_FORCE, (zpl_sign(in->y) == zpl_sign(car->force) ? 1.0f : 3.0f) * VEHICLE_ACCEL*safe_dt(it));
car->force += zpl_lerp(0.0f, in->y * game_rules.vehicle_force, (zpl_sign(in->y) == zpl_sign(car->force) ? 1.0f : 3.0f) * game_rules.vehicle_accel*safe_dt(it));
if (in->sprint) {
car->force = zpl_lerp(car->force, 0.0f, VEHICLE_BRAKE_FORCE*safe_dt(it));
car->force = zpl_lerp(car->force, 0.0f, game_rules.vehicle_brake_force*safe_dt(it));
if (zpl_abs(car->force) < 5.5f)
car->force = 0.0f;
}
float steer_mod = (1 - zpl_abs(car->force) / car->speed) + VEHICLE_STEER_COMPENSATION * safe_dt(it);
car->steer = zpl_lerp(car->steer, 0.0f, safe_dt(it)*VEHICLE_STEER_REVERT);
car->steer += (in->x * VEHICLE_STEER * steer_mod)*safe_dt(it);
float steer_mod = (1 - zpl_abs(car->force) / car->speed) + game_rules.vehicle_steer_compensation * safe_dt(it);
car->steer = zpl_lerp(car->steer, 0.0f, safe_dt(it)*game_rules.vehicle_steer_revert);
car->steer += (in->x * game_rules.vehicle_steer * steer_mod)*safe_dt(it);
car->steer = zpl_clamp(car->steer, -60.0f, 60.0f);
}
}
@ -134,10 +123,10 @@ void VehicleHandling(ecs_iter_t *it) {
world_block_lookup lookup = world_block_from_realpos(p[i].x, p[i].y);
float drag = zpl_clamp(blocks_get_drag(lookup.bid), 0.0f, 1.0f);
bk_x += car->force * drag * zpl_cos(car->heading) * safe_dt(it)*VEHICLE_POWER;
bk_y += car->force * drag * zpl_sin(car->heading) * safe_dt(it)*VEHICLE_POWER;
fr_x += car->force * drag * zpl_cos(car->heading + zpl_to_radians(car->steer)) * safe_dt(it)*VEHICLE_POWER;
fr_y += car->force * drag * zpl_sin(car->heading + zpl_to_radians(car->steer)) * safe_dt(it)*VEHICLE_POWER;
bk_x += car->force * drag * zpl_cos(car->heading) * safe_dt(it)*game_rules.vehicle_power;
bk_y += car->force * drag * zpl_sin(car->heading) * safe_dt(it)*game_rules.vehicle_power;
fr_x += car->force * drag * zpl_cos(car->heading + zpl_to_radians(car->steer)) * safe_dt(it)*game_rules.vehicle_power;
fr_y += car->force * drag * zpl_sin(car->heading + zpl_to_radians(car->steer)) * safe_dt(it)*game_rules.vehicle_power;
v[i].x += ((fr_x + bk_x) / 2.0f - p[i].x);
v[i].y += ((fr_y + bk_y) / 2.0f - p[i].y);

View File

@ -6,6 +6,7 @@
#include "platform/profiler.h"
#include "debug/debug_draw.h"
#include "core/game.h"
#include "core/rules.h"
#define PHY_BLOCK_COLLISION 1
#define PHY_WALK_DRAG 4.23f
@ -15,6 +16,7 @@
#include "modules/system_demo.c"
#include "modules/system_vehicle.c"
#include "modules/system_items.c"
#include "modules/system_furnace.c"
static inline float physics_correction(float x, float vx, float bounce) {
float r = (((zpl_max(0.0f, (WORLD_BLOCK_SIZE/2.0f) - zpl_abs(x))*zpl_sign(x)))*(WORLD_BLOCK_SIZE/2.0f));
@ -216,6 +218,7 @@ void SystemsImport(ecs_world_t *ecs) {
ECS_SYSTEM(ecs, UseItem, EcsPostUpdate, components.Input, components.Position, components.Inventory, !components.IsInVehicle);
ECS_SYSTEM(ecs, InspectContainers, EcsPostUpdate, components.Input, !components.IsInVehicle);
ECS_SYSTEM(ecs, HarvestIntoContainers, EcsPostUpdate, components.ItemContainer, components.Position);
ECS_SYSTEM(ecs, FurnaceCook, EcsPostUpdate, components.ItemContainer, components.Furnace, components.Position);
ECS_SYSTEM(ecs, ResetActivators, EcsPostUpdate, components.Input);

View File

@ -1,11 +1,13 @@
// NOTE(zaklaus): access to spawners
#include "ents/storage.h"
#include "ents/furnace.h"
static struct {
asset_id id;
uint64_t (*proc)();
} entity_spawnlist[] = {
{ .id = ASSET_CHEST, .proc = storage_spawn }
{ .id = ASSET_CHEST, .proc = storage_spawn },
{ .id = ASSET_FURNACE, .proc = furnace_spawn }
};
#define MAX_ENTITY_SPAWNDEFS ((sizeof(entity_spawnlist))/(sizeof(entity_spawnlist[0])))

View File

@ -0,0 +1,23 @@
#include "ents/furnace.h"
#include "ents/device.h"
#include "ents/entity.h"
#include "world/entity_view.h"
#include "world/world.h"
#include "ecs/components.h"
uint64_t furnace_spawn(void) {
ecs_entity_t e = device_spawn(ASSET_FURNACE);
ItemContainer *storage = ecs_get_mut(world_ecs(), e, ItemContainer);
*storage = (ItemContainer){0};
Furnace *furnace = ecs_get_mut(world_ecs(), e, Furnace);
*furnace = (Furnace){0};
furnace->burn_time = 69.0f;
return (uint64_t)e;
}
void furnace_despawn(uint64_t ent_id) {
entity_despawn(ent_id);
}

View File

@ -0,0 +1,7 @@
#pragma once
#include "platform/system.h"
uint64_t furnace_spawn(void);
void furnace_despawn(uint64_t id);

View File

@ -24,11 +24,16 @@ static inline asset_id item_fix_kind(asset_id id) {
return items[item_find(id)].kind;
}
void item_show(uint64_t ent, bool show) {
Classify *c = ecs_get_mut(world_ecs(), ent, Classify);
c->id = show ? EKIND_ITEM : EKIND_SERVER;
}
uint64_t item_spawn(asset_id kind, uint32_t qty) {
ecs_entity_t e = entity_spawn(EKIND_ITEM);
ItemDrop *d = ecs_get_mut(world_ecs(), e, ItemDrop);
*d = (ItemDrop){
Item *d = ecs_get_mut(world_ecs(), e, Item);
*d = (Item){
.kind = item_fix_kind(kind),
.quantity = qty,
.merger_time = 0,
@ -45,11 +50,16 @@ item_id item_find(asset_id kind) {
return ASSET_INVALID;
}
void item_use(ecs_world_t *ecs, ItemDrop *it, Position p, uint64_t udata) {
Item *item_get_data(uint64_t ent) {
return ecs_get_mut(world_ecs(), ent, Item);
}
void item_use(ecs_world_t *ecs, ItemSlot *it, Position p, uint64_t udata) {
(void)ecs;
uint16_t it_id = item_find(it->kind);
Item *d = item_get_data(it->ent);
uint16_t it_id = d->kind;
item_desc *desc = &items[it_id];
if (it->quantity <= 0) return;
if (it->ent == 0) return;
switch (item_get_usage(it_id)) {
case UKIND_HOLD: /* NOOP */ break;
case UKIND_PLACE:{
@ -61,7 +71,7 @@ void item_use(ecs_world_t *ecs, ItemDrop *it, Position p, uint64_t udata) {
// NOTE(zaklaus): If we replace the same item, refund 1 qty and let it replace it
if (item_asset_id == it_id) {
it->quantity++;
d->quantity++;
} else {
return;
}
@ -71,7 +81,7 @@ void item_use(ecs_world_t *ecs, ItemDrop *it, Position p, uint64_t udata) {
return;
}
world_chunk_replace_block(l.chunk_id, l.id, blocks_find(desc->place.kind + (asset_id)udata));
it->quantity--;
d->quantity--;
}break;
case UKIND_PLACE_ITEM:{
@ -88,7 +98,7 @@ void item_use(ecs_world_t *ecs, ItemDrop *it, Position p, uint64_t udata) {
ZPL_ASSERT(world_entity_valid(e));
entity_set_position(e, p.x, p.y);
it->quantity--;
d->quantity--;
}break;

View File

@ -21,6 +21,7 @@ typedef struct {
asset_id kind;
item_usage usage;
uint32_t max_quantity;
bool unique;
// NOTE(zaklaus): usage data
union {
@ -42,12 +43,15 @@ typedef struct {
typedef uint16_t item_id;
// NOTE(zaklaus): item drops
void item_show(uint64_t ent, bool show);
uint64_t item_spawn(asset_id kind, uint32_t qty);
void item_despawn(uint64_t id);
// NOTE(zaklaus): items
item_id item_find(asset_id kind);
void item_use(ecs_world_t *ecs, ItemDrop *it, Position p, uint64_t udata);
void item_use(ecs_world_t *ecs, ItemSlot *it, Position p, uint64_t udata);
Item *item_get_data(uint64_t ent);
uint32_t item_max_quantity(item_id id);
item_usage item_get_usage(item_id id);

View File

@ -8,12 +8,13 @@ static item_desc items[] = {
ITEM_SELF(ASSET_FENCE, 64),
ITEM_SELF(ASSET_WOOD, 64),
ITEM_HOLD(ASSET_TREE, 64),
ITEM_SELF_DIR(ASSET_BELT, 999),
ITEM_PROXY(ASSET_BELT_LEFT, ASSET_BELT),
ITEM_PROXY(ASSET_BELT_RIGHT, ASSET_BELT),
ITEM_PROXY(ASSET_BELT_UP, ASSET_BELT),
ITEM_PROXY(ASSET_BELT_DOWN, ASSET_BELT),
ITEM_ENT(ASSET_CHEST, 32, ASSET_CHEST),
ITEM_ENT(ASSET_FURNACE, 32, ASSET_FURNACE),
};

View File

@ -8,15 +8,16 @@ typedef enum {
ASSET_EMPTY,
ASSET_BLANK,
ASSET_BUILDMODE_HIGHLIGHT,
// NOTE(zaklaus): entities
ASSET_PLAYER,
ASSET_THING,
ASSET_CHEST,
ASSET_FURNACE,
// NOTE(zaklaus): items
ASSET_DEMO_ICEMAKER,
// NOTE(zaklaus): blocks
ASSET_FENCE,
ASSET_DEV,
@ -30,13 +31,16 @@ typedef enum {
ASSET_HOLE,
ASSET_WOOD,
ASSET_TREE,
ASSET_COAL,
ASSET_IRON_ORE,
ASSET_IRON_INGOT,
ASSET_BELT,
ASSET_BELT_LEFT,
ASSET_BELT_RIGHT,
ASSET_BELT_UP,
ASSET_BELT_DOWN,
MAX_ASSETS = 1024,
} asset_id;
@ -44,7 +48,7 @@ typedef enum {
AKIND_TEXTURE,
AKIND_ANIM,
AKIND_SOUND,
FORCE_AKIND_UINT8 = UINT8_MAX
} asset_kind;

View File

@ -16,6 +16,7 @@ static asset assets[] = {
ASSET_TEX(ASSET_BUILDMODE_HIGHLIGHT),
ASSET_TEX(ASSET_DEMO_ICEMAKER),
ASSET_TEX(ASSET_CHEST),
ASSET_TEX(ASSET_FURNACE),
// NOTE(zaklaus): blocks
ASSET_TEX(ASSET_FENCE),

View File

@ -1,2 +0,0 @@
// NOTE(zaklaus): contains collection of packed images used by texgen
// TODO(zaklaus): we will use files for now

View File

@ -0,0 +1,13 @@
#pragma once
#include "platform/system.h"
#include "world/entity_view.h"
#include "raylib.h"
void renderer_draw(void);
void renderer_init(void);
void renderer_shutdown(void);
void renderer_debug_draw(void);
float renderer_zoom_get(void);
void renderer_draw_single(float x, float y, asset_id id, Color color);
void renderer_bake_chunk(uint64_t key, entity_view * data);
void renderer_switch(int kind);

View File

@ -6,7 +6,7 @@
#include "world/entity_view.h"
#include "debug/debug_replay.h"
#include "ents/items.h"
#include "world/worldgen/worldgen.h"
#include "world/worldgen.h"
#include "platform/platform.h"
#include "platform/profiler.h"
#include "core/game.h"
@ -249,7 +249,7 @@ void world_init_mapping(void) {
static inline
void world_generate_instance(void) {
int32_t world_build_status = worldgen_test(&world);
int32_t world_build_status = worldgen_build(&world);
ZPL_ASSERT(world_build_status >= 0);
for (int i = 0; i < zpl_square(world.dim); ++i) {

View File

@ -1,4 +1,4 @@
#pragma once
#include "platform/system.h"
int32_t worldgen_test(void *world);
int32_t worldgen_build(void *world);

View File

@ -1,2 +1,3 @@
add_subdirectory(sandbox)
add_subdirectory(minimal)
add_subdirectory(minimal_3d)

View File

@ -1,6 +1,9 @@
add_executable(minimal
src/main.c
src/platform.c
src/worldgen.c
src/texgen.c
src/rules.c
)
target_compile_definitions(minimal PRIVATE CLIENT)

View File

@ -29,12 +29,9 @@ static uint16_t screenHeight = 768;
static float target_zoom = 0.6f;
static bool request_shutdown;
#define GFX_KIND 2
#include "renderers/renderer_bridge.c"
#include "renderer.c"
// NOTE(zaklaus): add-ins
#include "gui/build_mode.c"
#include "gui/inventory.c"
bool inv_is_open = false;
void platform_init() {
SetTraceLogLevel(LOG_ERROR);
@ -51,25 +48,9 @@ void platform_init() {
screenWidth = (uint16_t)GetScreenWidth();
screenHeight = (uint16_t)GetScreenHeight();
#endif
// ToggleFullscreen();
// SetTargetFPS(60.0);
renderer_init();
}
inline static
void display_conn_status() {
if (game_is_networked()) {
if (network_client_is_connected()) {
DrawText("Connection: online", 5, 5, 12, GREEN);
} else {
DrawText("Connection: offline", 5, 5, 12, RED);
}
} else {
DrawText("Connection: single-player", 5, 5, 12, BLUE);
}
}
void platform_shutdown() {
renderer_shutdown();
CloseWindow();
@ -79,54 +60,16 @@ uint8_t platform_is_running() {
return !WindowShouldClose();
}
void platform_get_block_realpos(float *x, float *y){
camera cam = camera_get();
Vector2 mpos = GetMousePosition();
entity_view *e = game_world_view_active_get_entity(cam.ent_id);
if (!e) return;
float zoom = renderer_zoom_get();
mpos.x -= screenWidth/2.0f;
mpos.y -= screenHeight/2.0f;
cam.x += mpos.x*(1.0f/zoom);
cam.y += mpos.y*(1.0f/zoom);
cam.x = ((int32_t)cam.x / (int32_t)(WORLD_BLOCK_SIZE)) * WORLD_BLOCK_SIZE;
cam.y = ((int32_t)cam.y / (int32_t)(WORLD_BLOCK_SIZE)) * WORLD_BLOCK_SIZE;
cam.x += WORLD_BLOCK_SIZE/2.0f;
cam.y += WORLD_BLOCK_SIZE/2.0f;
if (x) *x = (float)cam.x;
if (y) *y = (float)cam.y;
}
static game_keystate_data last_input_data = {0};
static pkt_send_blockpos last_blockpos_data = {0};
inline static
void platform_input_update_input_frame(game_keystate_data data) {
float mx = 0, my = 0;
platform_get_block_realpos(&mx, &my);
if (mx != last_blockpos_data.mx || my != last_blockpos_data.my){
last_blockpos_data.mx = mx;
last_blockpos_data.my = my;
game_action_send_blockpos(mx, my);
}
// NOTE(zaklaus): Test if there are any changes
if (data.x != last_input_data.x) goto send_data;
if (data.y != last_input_data.y) goto send_data;
if (data.use != last_input_data.use) goto send_data;
if (data.sprint != last_input_data.sprint) goto send_data;
if (data.ctrl != last_input_data.ctrl) goto send_data;
if (data.pick != last_input_data.pick) goto send_data;
if (data.storage_action != last_input_data.storage_action) goto send_data;
if (data.selected_item != last_input_data.selected_item) goto send_data;
if (data.drop != last_input_data.drop) goto send_data;
if (data.swap != last_input_data.swap) goto send_data;
if (data.swap_from != last_input_data.swap_from) goto send_data;
if (data.swap_to != last_input_data.swap_to) goto send_data;
if (data.placement_num != last_input_data.placement_num) goto send_data;
if (data.deletion_mode != last_input_data.deletion_mode) goto send_data;
if (zpl_memcompare(data.placements, last_input_data.placements, zpl_size_of(data.placements))) goto send_data;
return;
send_data:
@ -153,7 +96,6 @@ void platform_input() {
use = IsKeyPressed(KEY_SPACE);
sprint = IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT);
ctrl = IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL);
drop = IsKeyPressed(KEY_G) || player_inv.drop_item || storage_inv.drop_item;
// NOTE(zaklaus): NEW! mouse movement
Vector2 mouse_pos = GetMousePosition();
@ -168,12 +110,6 @@ void platform_input() {
y = -mouse_pos.y;
}
inv_keystate *inv = (inv_is_storage_action) ? &storage_inv : &player_inv;
inv_keystate *inv2 = (!inv_is_storage_action) ? &storage_inv : &player_inv;
// NOTE(zaklaus): don't perform picking if we manipulate our inventories
pick = (inv_is_inside||inv->item_is_held||inv2->item_is_held) ? false : IsMouseButtonDown(MOUSE_LEFT_BUTTON);
game_keystate_data in_data = {
.x = x,
.y = y,
@ -182,73 +118,10 @@ void platform_input() {
.use = use,
.sprint = sprint,
.ctrl = ctrl,
.pick = pick,
.drop = drop,
.storage_action = inv_is_storage_action,
.selected_item = player_inv.selected_item,
.storage_selected_item = storage_inv.selected_item,
.swap = inv->swap,
.swap_storage = inv_swap_storage,
.swap_from = inv->swap_from,
.swap_to = inv->swap_to,
.deletion_mode = build_is_deletion_mode,
};
if (build_submit_placements) {
in_data.placement_num = build_num_placements;
zpl_memcopy(in_data.placements, build_placements, build_num_placements*zpl_size_of(item_placement));
}
platform_input_update_input_frame(in_data);
}
// NOTE(zaklaus): cycle through viewers
{
if (IsKeyPressed(KEY_Q)) {
game_world_view_cycle_active(-1);
}
else if (IsKeyPressed(KEY_E)) {
game_world_view_cycle_active(1);
}
}
// NOTE(zaklaus): switch render modes
{
if (IsKeyPressed(KEY_O)) {
renderer_switch(1-gfx_kind);
}
}
// NOTE(zaklaus): toggle debug drawing
#ifndef ECO2D_PROD
{
if (IsKeyPressed(KEY_T)) {
debug_draw_enable(!debug_draw_state());
}
}
#endif
}
void draw_selected_item() {
camera cam = camera_get();
entity_view *oe = game_world_view_active_get_entity(cam.ent_id);
if (oe) {
// NOTE(zaklaus): sel item
entity_view *e = game_world_view_active_get_entity(oe->sel_ent);
if (e && e->kind == EKIND_DEVICE) {
renderer_draw_single(e->x, e->y, ASSET_BLANK, ColorAlpha(RED, 0.4f));
}else{
// NOTE(zaklaus): hover item
entity_view *e = game_world_view_active_get_entity(oe->pick_ent);
if (e && e->kind == EKIND_DEVICE) {
renderer_draw_single(e->x, e->y, ASSET_BLANK, ColorAlpha(RED, 0.1f));
}
}
}
}
void platform_render() {
@ -276,16 +149,7 @@ void platform_render() {
{
profile (PROF_RENDER) {
renderer_draw();
draw_selected_item();
}
renderer_debug_draw();
{
// NOTE(zaklaus): add-ins
buildmode_draw();
inventory_draw();
}
display_conn_status();
debug_draw();
}
EndDrawing();

View File

@ -0,0 +1,204 @@
static Camera2D render_camera;
static float zoom_overlay_tran = 0.0f;
#define CAM_OVERLAY_ZOOM_LEVEL 0.15f
#define ALPHA(x) ColorAlpha(x, data->tran_time)
float zpl_lerp(float,float,float);
float zpl_to_degrees(float);
void DrawNametag(const char* name, uint64_t key, entity_view *data, float x, float y) {
float size = 16.f;
float font_size = lerp(4.0f, 32.0f, 0.5f/(float)render_camera.zoom);
float font_spacing = 1.1f;
float title_bg_offset = 4;
float fixed_title_offset = 8.f;
float health = (data->hp / data->max_hp);
const char *title = TextFormat("%s %llu", name, key);
float title_w = MeasureTextEco(title, font_size, font_spacing);
DrawRectangleEco(x-title_w/2.f-title_bg_offset/2.f, y-size-font_size-fixed_title_offset, title_w+title_bg_offset, font_size, ColorAlpha(BLACK, data->tran_time));
DrawRectangleEco(x-title_w/2.f-title_bg_offset/2.f, y-size-fixed_title_offset, title_w*health+title_bg_offset, font_size*0.2f, ColorAlpha(RED, data->tran_time));
DrawTextEco(title, x-title_w/2.f, y-size-font_size-fixed_title_offset, font_size, ColorAlpha(RAYWHITE, data->tran_time), font_spacing);
}
void DEBUG_draw_ground(uint64_t key, entity_view * data) {
switch (data->kind) {
case EKIND_CHUNK: {
world_view *view = game_world_view_get_active();
float size = (float)(view->chunk_size * WORLD_BLOCK_SIZE);
float offset = 0.0;
float x = data->x * size + offset;
float y = data->y * size + offset;
RenderTexture2D tex = GetChunkTexture(key);
float scale = (size)/(float)(tex.texture.width);
tex.texture.width *= (int32_t)scale;
tex.texture.height *= (int32_t)scale;
DrawTextureRec(tex.texture, (Rectangle){0, 0, size, -size}, (Vector2){x, y}, ColorAlpha(WHITE, data->tran_time));
for (size_t ty = 0; ty < view->chunk_size; ty++) {
for (size_t tx = 0; tx < view->chunk_size; tx++) {
block_id blk_id = data->outer_blocks[(ty*view->chunk_size)+tx];
if (blk_id != 0) {
DrawTextureRec(GetBlockImage(blk_id), ASSET_SRC_RECT(), (Vector2){x+tx*WORLD_BLOCK_SIZE, y+ty*WORLD_BLOCK_SIZE}, ColorAlpha(WHITE, data->tran_time));
}
}
}
}break;
default:break;
}
}
void DEBUG_draw_overlay(uint64_t key, entity_view * data) {
switch (data->kind) {
case EKIND_CHUNK: {
world_view *view = game_world_view_get_active();
float size = (float)(view->chunk_size * WORLD_BLOCK_SIZE);
float offset = 0.0;
float x = data->x * size + offset;
float y = data->y * size + offset;
DrawRectangleEco(x, y, size-offset, size-offset, ColorAlpha(ColorFromHSV((float)data->color, 0.13f, 0.89f), data->tran_time*zoom_overlay_tran*0.75f));
DrawTextEco(TextFormat("%d %d", (int)data->x, (int)data->y), x+15.0f, y+15.0f, 200 , ColorAlpha(BLACK, data->tran_time*zoom_overlay_tran), 0.0);
}break;
default:break;
}
}
extern bool inv_is_open;
void DEBUG_draw_entities(uint64_t key, entity_view * data) {
float size = 16.f;
switch (data->kind) {
case EKIND_DEMO_NPC: {
float x = data->x;
float y = data->y;
DrawNametag("Demo", key, data, x, y);
DrawCircleEco(x, y, size, ColorAlpha(BLUE, data->tran_time));
}break;
case EKIND_PLAYER: {
float x = data->x;
float y = data->y;
float health = (data->hp / data->max_hp);
DrawNametag("Player", key, data, x, y);
DrawCircleEco(x, y, size, ColorAlpha(YELLOW, data->tran_time));
if (data->has_items && !data->inside_vehicle) {
float ix = data->x;
float iy = data->y;
if (data->items[data->selected_item].quantity > 0) {
asset_id it_kind = data->items[data->selected_item].kind;
uint32_t qty = data->items[data->selected_item].quantity;
DrawTexturePro(GetSpriteTexture2D(assets_find(it_kind)), ASSET_SRC_RECT(), ((Rectangle){ix, iy, 32, 32}), (Vector2){0.5f,0.5f}, 0.0f, ALPHA(WHITE));
if (!inv_is_open)
DrawTextEco(zpl_bprintf("%d", qty), x+24, y+24, 8, RAYWHITE, 0.0f);
}
}
}break;
case EKIND_MACRO_BOT: {
float x = data->x;
float y = data->y;
DrawNametag("Bot", key, data, x, y);
}break;
case EKIND_ITEM: {
float x = data->x - 32.f;
float y = data->y - 32.f;
DrawTexturePro(GetSpriteTexture2D(assets_find(data->asset)), ASSET_SRC_RECT(), ASSET_DST_RECT(x,y), (Vector2){0.5f,0.5f}, 0.0f, ALPHA(WHITE));
DrawTextEco(zpl_bprintf("%d", data->quantity), x, y, 10, ALPHA(RAYWHITE), 0.0f);
}break;
default:break;
}
}
void DEBUG_draw_entities_low(uint64_t key, entity_view * data) {
(void)key;
switch (data->kind) {
case EKIND_VEHICLE: {
float x = data->x;
float y = data->y;
float const w = 80;
float const h = 50;
DrawRectanglePro((Rectangle){x,y,w,h}, (Vector2){w/2.0f,h/2.0f}, zpl_to_degrees(data->heading), ColorAlpha(RED, data->tran_time));
}break;
case EKIND_DEVICE:{
float x = data->x - 32.f;
float y = data->y - 32.f;
DrawTexturePro(GetSpriteTexture2D(assets_find(data->asset)), ASSET_SRC_RECT(), ASSET_DST_RECT(x,y), (Vector2){0.5f,0.5f}, 0.0f, ALPHA(WHITE));
}break;
default:break;
}
}
void renderer_draw(void) {
render_camera.offset = (Vector2){(float)(screenWidth >> 1), (float)(screenHeight >> 1)};
render_camera.zoom = zpl_lerp(render_camera.zoom, target_zoom, GetFrameTime()*2.9978f);
camera_update();
camera game_camera = camera_get();
render_camera.target = (Vector2){(float)game_camera.x, (float)game_camera.y};
zoom_overlay_tran = zpl_lerp(zoom_overlay_tran, (target_zoom <= CAM_OVERLAY_ZOOM_LEVEL) ? 1.0f : 0.0f, GetFrameTime()*2.0f);
ClearBackground(GetColor(0x222034));
BeginMode2D(render_camera);
game_world_view_active_entity_map(DEBUG_draw_ground);
game_world_view_active_entity_map(DEBUG_draw_entities_low);
game_world_view_active_entity_map(DEBUG_draw_entities);
if (zoom_overlay_tran > 0.02f) {
game_world_view_active_entity_map(DEBUG_draw_overlay);
}
EndMode2D();
}
float renderer_zoom_get(void) {
return render_camera.zoom;
}
void renderer_init(void) {
render_camera.target = (Vector2){0.0f,0.0f};
render_camera.offset = (Vector2){(float)(screenWidth >> 1), (float)(screenHeight >> 1)};
render_camera.rotation = 0.0f;
render_camera.zoom = 1.5f;
// NOTE(zaklaus): Paint the screen before we load the game
// TODO(zaklaus): Render a cool loading screen background maybe? :wink: :wink:
BeginDrawing();
ClearBackground(GetColor(0x222034));
char const *loading_text = "zpl.eco2d is loading...";
int text_w = MeasureText(loading_text, 120);
DrawText(loading_text, GetScreenWidth()-text_w-15, GetScreenHeight()-135, 120, RAYWHITE);
EndDrawing();
blocks_setup();
assets_setup();
}
void renderer_shutdown(void) {
blocks_destroy();
assets_destroy();
}
void renderer_debug_draw(void) {
}
void renderer_draw_single(float x, float y, asset_id id, Color color) {
BeginMode2D(render_camera);
x -= 32.0f;
y -= 32.0f;
DrawTexturePro(GetSpriteTexture2D(assets_find(id)), ASSET_SRC_RECT(), ASSET_DST_RECT(x,y), (Vector2){0.5f,0.5f}, 0.0f, color);
EndMode2D();
}

View File

@ -0,0 +1,5 @@
#include "core/rules.h"
void rules_setup() {
}

View File

@ -1,6 +1,5 @@
#include "gen/texgen.h"
#include "world/world.h"
#include "texgen_data.c"
#include "zpl.h"
static inline

View File

@ -160,7 +160,7 @@ static void world_fill_mountain(uint32_t x, uint32_t y) {
#define RAND_RANGE(x,y) (x + (int)rand()%(y-(x)))
#define RAND_RANGEF(x,y) ((float)RAND_RANGE(x,y))
int32_t worldgen_test(world_data *wld) {
int32_t worldgen_build(world_data *wld) {
// TODO(zaklaus): pass world as an arg instead
world = wld;

View File

@ -0,0 +1,14 @@
add_executable(minimal-3d
src/main.c
src/platform.c
src/worldgen.c
src/texgen.c
src/rules.c
)
target_compile_definitions(minimal-3d PRIVATE CLIENT)
include_directories(src ../../foundation/src ../../../art/gen)
target_link_libraries(minimal-3d eco2d-foundation)
target_compile_options(minimal-3d PRIVATE -Werror -Wall -Wextra -Wno-unused-function -Wno-unknown-pragmas -Wno-unused-variable -Wno-unused-parameter)
link_system_libs(minimal-3d)

View File

@ -0,0 +1,120 @@
#define ZPL_IMPL
#include "zpl.h"
#include "platform/system.h"
#include "core/game.h"
#include "ents/entity.h"
#include "world/entity_view.h"
#include "utils/options.h"
#include "platform/signal_handling.h"
#include "platform/profiler.h"
#include "flecs/flecs.h"
#include "flecs/flecs_os_api_stdcpp.h"
#include "ecs/components.h"
#include "ecs/systems.h"
#if defined(PLATFORM_WEB)
#include <emscripten/emscripten.h>
void UpdateDrawFrame(void);
#endif
#define DEFAULT_WORLD_SEED 302097
#define DEFAULT_CHUNK_SIZE 16 /* amount of blocks within a chunk (single axis) */
#define DEFAULT_WORLD_SIZE 32 /* amount of chunks within a world (single axis) */
int main(int argc, char** argv) {
zpl_opts opts={0};
zpl_opts_init(&opts, zpl_heap(), argv[0]);
zpl_opts_add(&opts, "?", "help", "the HELP section", ZPL_OPTS_FLAG);
zpl_opts_add(&opts, "v", "viewer-only", "run viewer-only client", ZPL_OPTS_FLAG);
zpl_opts_add(&opts, "d", "server-only", "run dedicated server", ZPL_OPTS_FLAG);
zpl_opts_add(&opts, "p", "preview-map", "draw world preview", ZPL_OPTS_FLAG);
zpl_opts_add(&opts, "s", "seed", "world seed", ZPL_OPTS_INT);
zpl_opts_add(&opts, "r", "random-seed", "generate random world seed", ZPL_OPTS_FLAG);
//zpl_opts_add(&opts, "cs", "chunk-size", "amount of blocks within a chunk (single axis)", ZPL_OPTS_INT);
zpl_opts_add(&opts, "ws", "world-size", "amount of chunks within a world (single axis)", ZPL_OPTS_INT);
zpl_opts_add(&opts, "ip", "host", "host IP address", ZPL_OPTS_STRING);
zpl_opts_add(&opts, "port", "port", "port number", ZPL_OPTS_INT);
uint32_t ok = zpl_opts_compile(&opts, argc, argv);
if (!ok) {
zpl_opts_print_errors(&opts);
zpl_opts_print_help(&opts);
return -1;
}
int8_t is_viewer_only = zpl_opts_has_arg(&opts, "viewer-only");
int8_t is_server_only = zpl_opts_has_arg(&opts, "server-only");
int32_t seed = (int32_t)zpl_opts_integer(&opts, "seed", DEFAULT_WORLD_SEED);
uint16_t world_size = (uint16_t)zpl_opts_integer(&opts, "world-size", DEFAULT_WORLD_SIZE);
uint16_t chunk_size = DEFAULT_CHUNK_SIZE; //zpl_opts_integer(&opts, "chunk-size", DEFAULT_CHUNK_SIZE);
zpl_string host = zpl_opts_string(&opts, "host", NULL);
uint16_t port = (uint16_t)zpl_opts_integer(&opts, "port", 0);
game_kind play_mode = GAMEKIND_SINGLE;
if (is_viewer_only) play_mode = GAMEKIND_CLIENT;
if (is_server_only) play_mode = GAMEKIND_HEADLESS;
if (zpl_opts_has_arg(&opts, "random-seed")) {
zpl_random rnd={0};
zpl_random_init(&rnd);
seed = zpl_random_gen_u32(&rnd);
zpl_printf("Seed: %u\n", seed);
}
if (zpl_opts_has_arg(&opts, "preview-map")) {
generate_minimap(seed, WORLD_BLOCK_SIZE, chunk_size, world_size);
return 0;
}
sighandler_register();
game_init(host, port, play_mode, 1, seed, chunk_size, world_size, 0);
#if !defined(PLATFORM_WEB)
while (game_is_running()) {
reset_cached_time();
profile (PROF_MAIN_LOOP) {
game_input();
game_update();
game_render();
}
profiler_collate();
}
#else
emscripten_set_main_loop(UpdateDrawFrame, 0, 1);
#endif
game_shutdown();
sighandler_unregister();
zpl_string_free(host);
zpl_opts_free(&opts);
return 0;
}
#if defined(PLATFORM_WEB)
void UpdateDrawFrame(void) {
reset_cached_time();
profile (PROF_MAIN_LOOP) {
game_input();
game_update();
game_render();
}
profiler_collate();
}
#endif
static float temp_time = 0.0f;
float get_cached_time(void) {
return temp_time;
}
void reset_cached_time(void) {
temp_time = zpl_time_rel();
}

View File

@ -0,0 +1,171 @@
#include "platform/platform.h"
#include "raylib.h"
#include "raymath.h"
#include "net/network.h"
#include "core/game.h"
#include "world/entity_view.h"
#include "world/prediction.h"
#include "core/camera.h"
#include "math.h"
#include "world/blocks.h"
#include "gen/assets.h"
#include "platform/profiler.h"
#include "debug/debug_ui.h"
#include "utils/raylib_helpers.h"
#if defined(PLATFORM_WEB)
#include <emscripten.h>
EM_JS(int, canvas_get_width, (), {
return canvas.width;
});
EM_JS(int, canvas_get_height, (), {
return canvas.height;
});
#endif
static uint16_t screenWidth = 1024;
static uint16_t screenHeight = 768;
static float target_zoom = 0.6f;
static bool request_shutdown;
#include "renderer.c"
bool inv_is_open = false;
void platform_init() {
SetTraceLogLevel(LOG_ERROR);
#if defined(PLATFORM_WEB)
screenWidth = (uint16_t)canvas_get_width();
screenHeight = (uint16_t)canvas_get_height();
#endif
InitWindow(screenWidth, screenHeight, "eco2d");
SetWindowState(/*FLAG_WINDOW_UNDECORATED|*/FLAG_WINDOW_MAXIMIZED|FLAG_WINDOW_RESIZABLE|FLAG_MSAA_4X_HINT);
#if !defined(PLATFORM_WEB)
screenWidth = (uint16_t)GetScreenWidth();
screenHeight = (uint16_t)GetScreenHeight();
#endif
renderer_init();
}
void platform_shutdown() {
renderer_shutdown();
CloseWindow();
}
uint8_t platform_is_running() {
return !WindowShouldClose();
}
static game_keystate_data last_input_data = {0};
inline static
void platform_input_update_input_frame(game_keystate_data data) {
// NOTE(zaklaus): Test if there are any changes
if (data.x != last_input_data.x) goto send_data;
if (data.y != last_input_data.y) goto send_data;
if (data.use != last_input_data.use) goto send_data;
if (data.sprint != last_input_data.sprint) goto send_data;
if (data.ctrl != last_input_data.ctrl) goto send_data;
return;
send_data:
last_input_data = data;
game_action_send_keystate(&data);
}
void platform_input() {
float mouse_z = (GetMouseWheelMove()*0.5f);
if (mouse_z != 0.0f) {
target_zoom = zpl_clamp(target_zoom+mouse_z, 0.1f, 11.0f);
}
// NOTE(zaklaus): keystate handling
{
float x=0.0f, y=0.0f;
uint8_t use, sprint, drop, ctrl, pick;
if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) x += 1.0f;
if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) x -= 1.0f;
if (IsKeyDown(KEY_UP) || IsKeyDown(KEY_W)) y += 1.0f;
if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S)) y -= 1.0f;
use = IsKeyPressed(KEY_SPACE);
sprint = IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT);
ctrl = IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL);
// NOTE(zaklaus): NEW! mouse movement
Vector2 mouse_pos = GetMousePosition();
mouse_pos.x /= screenWidth;
mouse_pos.y /= screenHeight;
mouse_pos.x -= 0.5f;
mouse_pos.y -= 0.5f;
mouse_pos = Vector2Normalize(mouse_pos);
if (game_get_kind() == GAMEKIND_SINGLE && IsMouseButtonDown(MOUSE_MIDDLE_BUTTON)) {
x = mouse_pos.x;
y = -mouse_pos.y;
}
game_keystate_data in_data = {
.x = x,
.y = y,
.mx = mouse_pos.x,
.my = mouse_pos.y,
.use = use,
.sprint = sprint,
.ctrl = ctrl,
};
platform_input_update_input_frame(in_data);
}
}
void platform_render() {
#if !defined(PLATFORM_WEB)
screenWidth = (uint16_t)GetScreenWidth();
screenHeight = (uint16_t)GetScreenHeight();
#else
uint16_t newScreenWidth = (uint16_t)canvas_get_width();
uint16_t newScreenHeight = (uint16_t)canvas_get_height();
if (newScreenWidth != screenWidth || newScreenHeight != screenHeight) {
screenWidth = newScreenWidth;
screenHeight = newScreenHeight;
SetWindowSize(screenWidth, screenHeight);
}
#endif
profile(PROF_ENTITY_LERP) {
game_world_view_active_entity_map(lerp_entity_positions);
game_world_view_active_entity_map(do_entity_fadeinout);
}
assets_frame();
BeginDrawing();
{
profile (PROF_RENDER) {
renderer_draw();
}
}
EndDrawing();
if (request_shutdown) {
CloseWindow();
}
}
float platform_frametime() {
return GetFrameTime();
}
float platform_zoom_get(void) {
return target_zoom;
}
void platform_request_close(void) {
request_shutdown = true;
}

View File

@ -1,10 +1,10 @@
static Camera3D render_camera_3d;
static Camera3D render_camera;
static float cam_zoom = 1.5f;
float zpl_lerp(float,float,float);
float zpl_to_degrees(float);
void DEBUG_draw_ground_3d(uint64_t key, entity_view * data) {
void DEBUG_draw_ground(uint64_t key, entity_view * data) {
switch (data->kind) {
case EKIND_CHUNK: {
world_view *view = game_world_view_get_active();
@ -46,7 +46,7 @@ void DEBUG_draw_ground_3d(uint64_t key, entity_view * data) {
}
}
void DEBUG_draw_entities_3d(uint64_t key, entity_view * data) {
void DEBUG_draw_entities(uint64_t key, entity_view * data) {
(void)key;
uint16_t size = 16;
uint16_t ground_offset = 30;
@ -87,33 +87,33 @@ void DEBUG_draw_entities_3d(uint64_t key, entity_view * data) {
}
}
void renderer_draw_3d(void) {
void renderer_draw(void) {
cam_zoom = zpl_min(zpl_lerp(cam_zoom, target_zoom, GetFrameTime()*2.18f), 9.98f);
camera_update();
camera game_camera_3d = camera_get();
camera game_camera = camera_get();
#if 1
render_camera_3d.position = (Vector3){(float)game_camera_3d.x, 260.0f*(10.0f-cam_zoom), (float)game_camera_3d.y+50.0f*(10.0f-cam_zoom/2.0f)};
render_camera_3d.target = (Vector3){(float)game_camera_3d.x, 0.0f, (float)game_camera_3d.y};
render_camera.position = (Vector3){(float)game_camera.x, 260.0f*(10.0f-cam_zoom), (float)game_camera.y+50.0f*(10.0f-cam_zoom/2.0f)};
render_camera.target = (Vector3){(float)game_camera.x, 0.0f, (float)game_camera.y};
#else
UpdateCamera(&render_camera_3d);
UpdateCamera(&render_camera);
#endif
ClearBackground(GetColor(0x222034));
BeginMode3D(render_camera_3d);
game_world_view_active_entity_map(DEBUG_draw_ground_3d);
game_world_view_active_entity_map(DEBUG_draw_entities_3d);
BeginMode3D(render_camera);
game_world_view_active_entity_map(DEBUG_draw_ground);
game_world_view_active_entity_map(DEBUG_draw_entities);
EndMode3D();
}
void renderer_init_3d(void) {
render_camera_3d.up = (Vector3){0.0f,0.0f,-1.0f};
render_camera_3d.fovy = 45.f;
render_camera_3d.projection = CAMERA_PERSPECTIVE;
void renderer_init(void) {
render_camera.up = (Vector3){0.0f,0.0f,-1.0f};
render_camera.fovy = 45.f;
render_camera.projection = CAMERA_PERSPECTIVE;
#if 0
SetCameraMode(render_camera_3d, CAMERA_ORBITAL);
render_camera_3d.position = (Vector3){10,10,10};
render_camera_3d.target = (Vector3){0};
SetCameraMode(render_camera, CAMERA_ORBITAL);
render_camera.position = (Vector3){10,10,10};
render_camera.target = (Vector3){0};
#endif
// NOTE(zaklaus): Paint the screen before we load the game
@ -131,23 +131,23 @@ void renderer_init_3d(void) {
assets_setup();
}
void renderer_shutdown_3d(void) {
void renderer_shutdown(void) {
blocks_destroy();
assets_destroy();
}
void renderer_debug_draw_3d(void) {
void renderer_debug_draw(void) {
}
float renderer_zoom_get_3d(void) {
float renderer_zoom_get(void) {
return cam_zoom;
}
void renderer_draw_single_3d(float x, float y, asset_id id, Color color) {
void renderer_draw_single(float x, float y, asset_id id, Color color) {
(void)color;
BeginMode3D(render_camera_3d);
BeginMode3D(render_camera);
EcoDrawCube((Vector3){x, 15.0f, y}, 16, 16, 16, 0.0f, PINK);
EndMode3D();
}

View File

@ -0,0 +1,5 @@
#include "core/rules.h"
void rules_setup() {
}

View File

@ -0,0 +1,76 @@
#include "gen/texgen.h"
#include "world/world.h"
#include "zpl.h"
static inline
Texture2D LoadTexEco(const char *name) {
static char filename[128];
zpl_snprintf(filename, 128, "art/gen/%s.png", name);
return LoadTexture(filename);
}
static inline
Image LoadImageEco(const char *name) {
static char filename[128];
zpl_snprintf(filename, 128, "art/gen/%s.png", name);
return LoadImage(filename);
}
static inline
Texture2D Image2TexEco(Image image) {
Texture2D tex = LoadTextureFromImage(image);
UnloadImage(image);
return tex;
}
static inline
Texture2D GenColorEco(Color color) {
Image img = GenImageColor(1, 1, color);
return Image2TexEco(img);
}
Texture2D texgen_build_anim(asset_id id, int64_t counter) {
(void)counter;
switch (id) {
case ASSET_WATER: {
Image img = LoadImageEco("water");
ImageColorBrightness(&img, zpl_abs((counter % 64 - 32)*2));
return Image2TexEco(img);
}break;
default: return GenColorEco(PINK); break;
}
}
Texture2D texgen_build_sprite(asset_id id) {
switch (id) {
case ASSET_BLANK: return GenColorEco(WHITE); break;
case ASSET_BUILDMODE_HIGHLIGHT: return GenColorEco(WHITE); break;
// NOTE(zaklaus): items
case ASSET_DEMO_ICEMAKER: return LoadTexEco("demo_icemaker");
// NOTE(zaklaus): blocks
case ASSET_FENCE: return LoadTexEco("fence");
case ASSET_GROUND: return LoadTexEco("grass");
case ASSET_DIRT: return LoadTexEco("dirt");
case ASSET_WALL: return LoadTexEco("asphalt");
case ASSET_HILL_SNOW:
case ASSET_HILL: return LoadTexEco("rock");
case ASSET_LAVA: return LoadTexEco("lava");
case ASSET_WOOD: return LoadTexEco("wood");
case ASSET_TREE: return LoadTexEco("tree");
// case ASSET_WATER: return LoadTexEco("water");
case ASSET_BELT:
case ASSET_BELT_RIGHT: return LoadTexEco("belt_right");
case ASSET_BELT_LEFT: return LoadTexEco("belt_left");
case ASSET_BELT_UP: return LoadTexEco("belt_up");
case ASSET_BELT_DOWN: return LoadTexEco("belt_down");
// NOTE(zaklaus): devices
case ASSET_CHEST: return LoadTexEco("chest");
default: return GenColorEco(PINK); break;
}
}

View File

@ -0,0 +1,276 @@
#include "zpl.h"
#include <math.h>
#include <stdlib.h>
#include "world/world.h"
#include "world/blocks.h"
#include "world/perlin.h"
#include "ecs/components.h"
#include "ents/entity.h"
#include "ents/vehicle.h"
#include "ents/items.h"
#include "world/blocks_info.h"
#define WORLD_BLOCK_OBSERVER(name) block_id name(block_id *data, block_id id, uint32_t block_idx)
typedef WORLD_BLOCK_OBSERVER(world_block_observer_proc);
#define WORLD_PERLIN_FREQ 100
#define WORLD_PERLIN_OCTAVES 1
#define BLOCK_INVALID 0xF
block_id worldgen_biome_find(uint32_t biome, uint32_t kind) {
asset_id asset = ASSET_INVALID;
switch (biome) {
case BLOCK_BIOME_DEV: {
switch (kind) {
case BLOCK_KIND_GROUND: asset = ASSET_GROUND; break;
case BLOCK_KIND_DIRT: asset = ASSET_DIRT; break;
case BLOCK_KIND_WALL: asset = ASSET_WALL; break;
case BLOCK_KIND_HILL_SNOW:
case BLOCK_KIND_HILL: asset = ASSET_HILL; break;
case BLOCK_KIND_WATER: asset = ASSET_WATER; break;
case BLOCK_KIND_LAVA: asset = ASSET_LAVA; break;
}
}
}
return blocks_find(asset);
}
static world_data *world;
static void world_fill_rect(block_id *data, block_id id, uint32_t x, uint32_t y, uint32_t w, uint32_t h, world_block_observer_proc *proc) {
for (uint32_t cy=y; cy<y+h; cy++) {
for (uint32_t cx=x; cx<x+w; cx++) {
if (cx < 0 || cx >= world->dim) continue;
if (cy < 0 || cy >= world->dim) continue;
uint32_t i = (cy*world->dim) + cx;
if (proc) {
block_id new_id = (*proc)(data, id, i);
if (new_id != BLOCK_INVALID) {
id = new_id;
}
else continue;
}
data[i] = id;
}
}
}
static void world_fill_circle(block_id *data, block_id id, uint32_t x, uint32_t y, uint32_t w, uint32_t h, world_block_observer_proc *proc) {
for (uint32_t cy=y; cy<y+h; cy++) {
for (uint32_t cx=x; cx<x+w; cx++) {
if (cx < 0 || cx >= world->dim) continue;
if (cy < 0 || cy >= world->dim) continue;
uint32_t i = (cy*world->dim) + cx;
if (proc) {
block_id new_id = (*proc)(data, id, i);
if (new_id != BLOCK_INVALID) {
id = new_id;
}
else continue;
}
data[i] = id;
}
}
}
static void world_fill_rect_anchor(block_id *data, block_id id, uint32_t x, uint32_t y, uint32_t w, uint32_t h, float ax, float ay, world_block_observer_proc *proc) {
uint32_t w2 = (uint32_t)floorf(w*ax);
uint32_t h2 = (uint32_t)floorf(h*ay);
world_fill_rect(data, id, x-w2, y-h2, w, h, proc);
}
static WORLD_BLOCK_OBSERVER(shaper) {
uint32_t kind = id;
uint32_t old_kind = data[block_idx];
if (kind == BLOCK_KIND_WALL && kind == old_kind) {
return worldgen_biome_find(BLOCK_BIOME_DEV, BLOCK_KIND_HILL);
}
if (kind == BLOCK_KIND_HILL && kind == old_kind) {
return worldgen_biome_find(BLOCK_BIOME_DEV, BLOCK_KIND_HILL_SNOW);
}
return id;
}
static block_id world_perlin_cond_offset(uint32_t block_idx, double chance, uint32_t ofx, uint32_t ofy) {
uint32_t x = block_idx % world->dim + ofx;
uint32_t y = block_idx / world->dim + ofy;
return perlin_fbm(world->seed, x, y, WORLD_PERLIN_FREQ, WORLD_PERLIN_OCTAVES) < chance;
}
static block_id world_perlin_cond(uint32_t block_idx, double chance) {
return world_perlin_cond_offset(block_idx, chance, 0, 0);
}
#if 1
static WORLD_BLOCK_OBSERVER(shaper_noise80) {
return world_perlin_cond(block_idx, 0.80) ? shaper(data, id, block_idx) : BLOCK_INVALID;
}
static WORLD_BLOCK_OBSERVER(shaper_noise50) {
return world_perlin_cond(block_idx, 0.50) ? shaper(data, id, block_idx) : BLOCK_INVALID;
}
static WORLD_BLOCK_OBSERVER(shaper_noise33) {
return world_perlin_cond(block_idx, 0.33) ? shaper(data, id, block_idx) : BLOCK_INVALID;
}
static WORLD_BLOCK_OBSERVER(shaper_noise05) {
return world_perlin_cond(block_idx, 0.05) ? shaper(data, id, block_idx) : BLOCK_INVALID;
}
static WORLD_BLOCK_OBSERVER(shaper_noise05b) {
return world_perlin_cond_offset(block_idx, 0.05, 32, 0) ? shaper(data, id, block_idx) : BLOCK_INVALID;
}
static WORLD_BLOCK_OBSERVER(shaper_noise01b) {
return world_perlin_cond_offset(block_idx, 0.01, 32, 0) ? shaper(data, id, block_idx) : BLOCK_INVALID;
}
#else
static WORLD_BLOCK_OBSERVER(shaper_noise80) {
return rand()%10 < 8 ? shaper(id, block_idx) : BLOCK_INVALID;
}
static WORLD_BLOCK_OBSERVER(shaper_noise50) {
return rand()%10 < 5 ? shaper(id, block_idx) : BLOCK_INVALID;
}
static WORLD_BLOCK_OBSERVER(shaper_noise33) {
return rand()%10 < 3 ? shaper(id, block_idx) : BLOCK_INVALID;
}
#endif
#if 0
static void world_fill_mountain(uint32_t x, uint32_t y) {
}
#endif
#define RAND_RANGE(x,y) (x + (int)rand()%(y-(x)))
#define RAND_RANGEF(x,y) ((float)RAND_RANGE(x,y))
int32_t worldgen_build(world_data *wld) {
// TODO(zaklaus): pass world as an arg instead
world = wld;
// TODO: perform world gen
// atm, we will fill the world with ground and surround it by walls
block_id wall_id = worldgen_biome_find(BLOCK_BIOME_DEV, BLOCK_KIND_WALL);
block_id grnd_id = worldgen_biome_find(BLOCK_BIOME_DEV, BLOCK_KIND_GROUND);
block_id dirt_id = worldgen_biome_find(BLOCK_BIOME_DEV, BLOCK_KIND_DIRT);
block_id watr_id = worldgen_biome_find(BLOCK_BIOME_DEV, BLOCK_KIND_WATER);
block_id lava_id = worldgen_biome_find(BLOCK_BIOME_DEV, BLOCK_KIND_LAVA);
block_id tree_id = blocks_find(ASSET_TREE);
srand(world->seed);
// walls
world_fill_rect(world->data, wall_id, 0, 0, world->dim, world->dim, NULL);
// ground
world_fill_rect(world->data, grnd_id, 1, 1, world->dim-2, world->dim-2, NULL);
world_fill_rect(world->data, dirt_id, 1, 1, world->dim-2, world->dim-2, shaper_noise05);
world_fill_rect(world->outer_data, tree_id, 1, 1, world->dim-2, world->dim-2, shaper_noise01b);
// water
#if 1
for (int i=0; i<RAND_RANGE(58, 92); i++) {
world_fill_rect_anchor(world->data, watr_id, RAND_RANGE(0, world->dim), RAND_RANGE(0, world->dim), 4+RAND_RANGE(0,3), 4+RAND_RANGE(0,3), 0.5f, 0.5f, shaper_noise80);
}
#endif
// ice rink
#if 0
world_fill_rect_anchor(world->data, watr_id, 450, 125, 10, 10, 0.0f, 0.0f, NULL);
#endif
// lava
#if 1
for (int i=0; i<RAND_RANGE(48, 62); i++) {
world_fill_rect_anchor(world->data, lava_id, RAND_RANGE(0, world->dim), RAND_RANGE(0, world->dim), 4+RAND_RANGE(0,3), 4+RAND_RANGE(0,3), 0.5f, 0.5f, shaper_noise80);
}
#endif
// hills
#if 1
const uint32_t HILLS_SIZE = 21;
for (int i=0; i<RAND_RANGE(8, 124); i++) {
world_fill_rect_anchor(world->data, wall_id, RAND_RANGE(0, world->dim), RAND_RANGE(0, world->dim), RAND_RANGE(0,HILLS_SIZE), RAND_RANGE(0,HILLS_SIZE), 0.5f, 0.5f, shaper_noise50);
}
#endif
// vehicles
#if 1
for (int i=0; i<RAND_RANGE(258, 1124); i++) {
uint64_t e = vehicle_spawn();
Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
dest->y = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
entity_set_position(e, dest->x, dest->y);
}
#endif
// items
#if 1
for (int i=0; i<RAND_RANGE(328, 164); i++) {
uint64_t e = item_spawn(ASSET_DEMO_ICEMAKER, 32);
Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
dest->y = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
entity_set_position(e, dest->x, dest->y);
}
for (int i=0; i<RAND_RANGE(328, 164); i++) {
uint64_t e = item_spawn(ASSET_FENCE, 64);
Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
dest->y = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
entity_set_position(e, dest->x, dest->y);
}
for (int i=0; i<RAND_RANGE(328, 164); i++) {
uint64_t e = item_spawn(ASSET_WOOD, 64);
Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
dest->y = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
entity_set_position(e, dest->x, dest->y);
}
for (int i=0; i<RAND_RANGE(128, 564); i++) {
uint64_t e = item_spawn(ASSET_BELT, 999);
Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
dest->y = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
entity_set_position(e, dest->x, dest->y);
}
for (int i=0; i<RAND_RANGE(128, 964); i++) {
uint64_t e = item_spawn(ASSET_CHEST, 4);
Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
dest->y = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
entity_set_position(e, dest->x, dest->y);
}
#endif
return WORLD_ERROR_NONE;
}

View File

@ -1,6 +1,9 @@
add_executable(eco2d
src/main.c
src/platform.c
src/worldgen.c
src/texgen.c
src/rules.c
)
target_compile_definitions(eco2d PRIVATE CLIENT)

View File

@ -0,0 +1,300 @@
#include "platform/platform.h"
#include "raylib.h"
#include "raymath.h"
#include "net/network.h"
#include "core/game.h"
#include "world/entity_view.h"
#include "world/prediction.h"
#include "core/camera.h"
#include "math.h"
#include "world/blocks.h"
#include "gen/assets.h"
#include "platform/profiler.h"
#include "debug/debug_ui.h"
#include "debug/debug_draw.h"
#include "utils/raylib_helpers.h"
#if defined(PLATFORM_WEB)
#include <emscripten.h>
EM_JS(int, canvas_get_width, (), {
return canvas.width;
});
EM_JS(int, canvas_get_height, (), {
return canvas.height;
});
#endif
static uint16_t screenWidth = 1024;
static uint16_t screenHeight = 768;
static float target_zoom = 0.6f;
static bool request_shutdown;
#include "renderer.c"
// NOTE(zaklaus): add-ins
#include "gui/build_mode.c"
#include "gui/inventory.c"
void platform_init() {
SetTraceLogLevel(LOG_ERROR);
#if defined(PLATFORM_WEB)
screenWidth = (uint16_t)canvas_get_width();
screenHeight = (uint16_t)canvas_get_height();
#endif
InitWindow(screenWidth, screenHeight, "eco2d");
SetWindowState(/*FLAG_WINDOW_UNDECORATED|*/FLAG_WINDOW_MAXIMIZED|FLAG_WINDOW_RESIZABLE|FLAG_MSAA_4X_HINT);
#if !defined(PLATFORM_WEB)
screenWidth = (uint16_t)GetScreenWidth();
screenHeight = (uint16_t)GetScreenHeight();
#endif
// ToggleFullscreen();
// SetTargetFPS(60.0);
renderer_init();
}
inline static
void display_conn_status() {
if (game_is_networked()) {
if (network_client_is_connected()) {
DrawText("Connection: online", 5, 5, 12, GREEN);
} else {
DrawText("Connection: offline", 5, 5, 12, RED);
}
} else {
DrawText("Connection: single-player", 5, 5, 12, BLUE);
}
}
void platform_shutdown() {
renderer_shutdown();
CloseWindow();
}
uint8_t platform_is_running() {
return !WindowShouldClose();
}
void platform_get_block_realpos(float *x, float *y){
camera cam = camera_get();
Vector2 mpos = GetMousePosition();
entity_view *e = game_world_view_active_get_entity(cam.ent_id);
if (!e) return;
float zoom = renderer_zoom_get();
mpos.x -= screenWidth/2.0f;
mpos.y -= screenHeight/2.0f;
cam.x += mpos.x*(1.0f/zoom);
cam.y += mpos.y*(1.0f/zoom);
cam.x = ((int32_t)cam.x / (int32_t)(WORLD_BLOCK_SIZE)) * WORLD_BLOCK_SIZE;
cam.y = ((int32_t)cam.y / (int32_t)(WORLD_BLOCK_SIZE)) * WORLD_BLOCK_SIZE;
cam.x += WORLD_BLOCK_SIZE/2.0f;
cam.y += WORLD_BLOCK_SIZE/2.0f;
if (x) *x = (float)cam.x;
if (y) *y = (float)cam.y;
}
static game_keystate_data last_input_data = {0};
static pkt_send_blockpos last_blockpos_data = {0};
inline static
void platform_input_update_input_frame(game_keystate_data data) {
float mx = 0, my = 0;
platform_get_block_realpos(&mx, &my);
if (mx != last_blockpos_data.mx || my != last_blockpos_data.my){
last_blockpos_data.mx = mx;
last_blockpos_data.my = my;
game_action_send_blockpos(mx, my);
}
// NOTE(zaklaus): Test if there are any changes
if (data.x != last_input_data.x) goto send_data;
if (data.y != last_input_data.y) goto send_data;
if (data.use != last_input_data.use) goto send_data;
if (data.sprint != last_input_data.sprint) goto send_data;
if (data.ctrl != last_input_data.ctrl) goto send_data;
if (data.pick != last_input_data.pick) goto send_data;
if (data.storage_action != last_input_data.storage_action) goto send_data;
if (data.selected_item != last_input_data.selected_item) goto send_data;
if (data.drop != last_input_data.drop) goto send_data;
if (data.swap != last_input_data.swap) goto send_data;
if (data.swap_from != last_input_data.swap_from) goto send_data;
if (data.swap_to != last_input_data.swap_to) goto send_data;
if (data.placement_num != last_input_data.placement_num) goto send_data;
if (data.deletion_mode != last_input_data.deletion_mode) goto send_data;
if (zpl_memcompare(data.placements, last_input_data.placements, zpl_size_of(data.placements))) goto send_data;
return;
send_data:
last_input_data = data;
game_action_send_keystate(&data);
}
void platform_input() {
float mouse_z = (GetMouseWheelMove()*0.5f);
if (mouse_z != 0.0f) {
target_zoom = zpl_clamp(target_zoom+mouse_z, 0.1f, 11.0f);
}
// NOTE(zaklaus): keystate handling
{
float x=0.0f, y=0.0f;
uint8_t use, sprint, drop, ctrl, pick;
if (IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D)) x += 1.0f;
if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A)) x -= 1.0f;
if (IsKeyDown(KEY_UP) || IsKeyDown(KEY_W)) y += 1.0f;
if (IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S)) y -= 1.0f;
use = IsKeyPressed(KEY_SPACE);
sprint = IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT);
ctrl = IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL);
drop = IsKeyPressed(KEY_G) || player_inv.drop_item || storage_inv.drop_item;
// NOTE(zaklaus): NEW! mouse movement
Vector2 mouse_pos = GetMousePosition();
mouse_pos.x /= screenWidth;
mouse_pos.y /= screenHeight;
mouse_pos.x -= 0.5f;
mouse_pos.y -= 0.5f;
mouse_pos = Vector2Normalize(mouse_pos);
if (game_get_kind() == GAMEKIND_SINGLE && IsMouseButtonDown(MOUSE_MIDDLE_BUTTON)) {
x = mouse_pos.x;
y = -mouse_pos.y;
}
inv_keystate *inv = (inv_is_storage_action) ? &storage_inv : &player_inv;
inv_keystate *inv2 = (!inv_is_storage_action) ? &storage_inv : &player_inv;
// NOTE(zaklaus): don't perform picking if we manipulate our inventories
pick = (inv_is_inside||inv->item_is_held||inv2->item_is_held) ? false : IsMouseButtonDown(MOUSE_LEFT_BUTTON);
game_keystate_data in_data = {
.x = x,
.y = y,
.mx = mouse_pos.x,
.my = mouse_pos.y,
.use = use,
.sprint = sprint,
.ctrl = ctrl,
.pick = pick,
.drop = drop,
.storage_action = inv_is_storage_action,
.selected_item = player_inv.selected_item,
.storage_selected_item = storage_inv.selected_item,
.swap = inv->swap,
.swap_storage = inv_swap_storage,
.swap_from = inv->swap_from,
.swap_to = inv->swap_to,
.deletion_mode = build_is_deletion_mode,
};
if (build_submit_placements) {
in_data.placement_num = build_num_placements;
zpl_memcopy(in_data.placements, build_placements, build_num_placements*zpl_size_of(item_placement));
}
platform_input_update_input_frame(in_data);
}
// NOTE(zaklaus): cycle through viewers
{
if (IsKeyPressed(KEY_Q)) {
game_world_view_cycle_active(-1);
}
else if (IsKeyPressed(KEY_E)) {
game_world_view_cycle_active(1);
}
}
// NOTE(zaklaus): toggle debug drawing
#ifndef ECO2D_PROD
{
if (IsKeyPressed(KEY_T)) {
debug_draw_enable(!debug_draw_state());
}
}
#endif
}
void draw_selected_item() {
camera cam = camera_get();
entity_view *oe = game_world_view_active_get_entity(cam.ent_id);
if (oe) {
// NOTE(zaklaus): sel item
entity_view *e = game_world_view_active_get_entity(oe->sel_ent);
if (e && e->kind == EKIND_DEVICE) {
renderer_draw_single(e->x, e->y, ASSET_BLANK, ColorAlpha(RED, 0.4f));
}else{
// NOTE(zaklaus): hover item
entity_view *e = game_world_view_active_get_entity(oe->pick_ent);
if (e && e->kind == EKIND_DEVICE) {
renderer_draw_single(e->x, e->y, ASSET_BLANK, ColorAlpha(RED, 0.1f));
}
}
}
}
void platform_render() {
#if !defined(PLATFORM_WEB)
screenWidth = (uint16_t)GetScreenWidth();
screenHeight = (uint16_t)GetScreenHeight();
#else
uint16_t newScreenWidth = (uint16_t)canvas_get_width();
uint16_t newScreenHeight = (uint16_t)canvas_get_height();
if (newScreenWidth != screenWidth || newScreenHeight != screenHeight) {
screenWidth = newScreenWidth;
screenHeight = newScreenHeight;
SetWindowSize(screenWidth, screenHeight);
}
#endif
profile(PROF_ENTITY_LERP) {
game_world_view_active_entity_map(lerp_entity_positions);
game_world_view_active_entity_map(do_entity_fadeinout);
}
assets_frame();
BeginDrawing();
{
profile (PROF_RENDER) {
renderer_draw();
draw_selected_item();
}
renderer_debug_draw();
{
// NOTE(zaklaus): add-ins
buildmode_draw();
inventory_draw();
}
display_conn_status();
debug_draw();
}
EndDrawing();
if (request_shutdown) {
CloseWindow();
}
}
float platform_frametime() {
return GetFrameTime();
}
float platform_zoom_get(void) {
return target_zoom;
}
void platform_request_close(void) {
request_shutdown = true;
}

View File

@ -136,7 +136,7 @@ void DEBUG_draw_entities_low(uint64_t key, entity_view * data) {
}
}
void renderer_draw_v0(void) {
void renderer_draw(void) {
render_camera.offset = (Vector2){(float)(screenWidth >> 1), (float)(screenHeight >> 1)};
render_camera.zoom = zpl_lerp(render_camera.zoom, target_zoom, GetFrameTime()*2.9978f);
camera_update();
@ -158,11 +158,11 @@ void renderer_draw_v0(void) {
EndMode2D();
}
float renderer_zoom_get_v0(void) {
float renderer_zoom_get(void) {
return render_camera.zoom;
}
void renderer_init_v0(void) {
void renderer_init(void) {
render_camera.target = (Vector2){0.0f,0.0f};
render_camera.offset = (Vector2){(float)(screenWidth >> 1), (float)(screenHeight >> 1)};
render_camera.rotation = 0.0f;
@ -183,12 +183,12 @@ void renderer_init_v0(void) {
assets_setup();
}
void renderer_shutdown_v0(void) {
void renderer_shutdown(void) {
blocks_destroy();
assets_destroy();
}
void renderer_debug_draw_v0(void) {
void renderer_debug_draw(void) {
BeginMode2D(render_camera);
debug_draw_queue *que = debug_draw_samples();
@ -228,7 +228,7 @@ void renderer_debug_draw_v0(void) {
EndMode2D();
}
void renderer_draw_single_v0(float x, float y, asset_id id, Color color) {
void renderer_draw_single(float x, float y, asset_id id, Color color) {
BeginMode2D(render_camera);
x -= 32.0f;

View File

@ -0,0 +1,5 @@
#include "core/rules.h"
void rules_setup() {
}

View File

@ -0,0 +1,76 @@
#include "gen/texgen.h"
#include "world/world.h"
#include "zpl.h"
static inline
Texture2D LoadTexEco(const char *name) {
static char filename[128];
zpl_snprintf(filename, 128, "art/gen/%s.png", name);
return LoadTexture(filename);
}
static inline
Image LoadImageEco(const char *name) {
static char filename[128];
zpl_snprintf(filename, 128, "art/gen/%s.png", name);
return LoadImage(filename);
}
static inline
Texture2D Image2TexEco(Image image) {
Texture2D tex = LoadTextureFromImage(image);
UnloadImage(image);
return tex;
}
static inline
Texture2D GenColorEco(Color color) {
Image img = GenImageColor(1, 1, color);
return Image2TexEco(img);
}
Texture2D texgen_build_anim(asset_id id, int64_t counter) {
(void)counter;
switch (id) {
case ASSET_WATER: {
Image img = LoadImageEco("water");
ImageColorBrightness(&img, zpl_abs((counter % 64 - 32)*2));
return Image2TexEco(img);
}break;
default: return GenColorEco(PINK); break;
}
}
Texture2D texgen_build_sprite(asset_id id) {
switch (id) {
case ASSET_BLANK: return GenColorEco(WHITE); break;
case ASSET_BUILDMODE_HIGHLIGHT: return GenColorEco(WHITE); break;
// NOTE(zaklaus): items
case ASSET_DEMO_ICEMAKER: return LoadTexEco("demo_icemaker");
// NOTE(zaklaus): blocks
case ASSET_FENCE: return LoadTexEco("fence");
case ASSET_GROUND: return LoadTexEco("grass");
case ASSET_DIRT: return LoadTexEco("dirt");
case ASSET_WALL: return LoadTexEco("asphalt");
case ASSET_HILL_SNOW:
case ASSET_HILL: return LoadTexEco("rock");
case ASSET_LAVA: return LoadTexEco("lava");
case ASSET_WOOD: return LoadTexEco("wood");
case ASSET_TREE: return LoadTexEco("tree");
// case ASSET_WATER: return LoadTexEco("water");
case ASSET_BELT:
case ASSET_BELT_RIGHT: return LoadTexEco("belt_right");
case ASSET_BELT_LEFT: return LoadTexEco("belt_left");
case ASSET_BELT_UP: return LoadTexEco("belt_up");
case ASSET_BELT_DOWN: return LoadTexEco("belt_down");
// NOTE(zaklaus): devices
case ASSET_CHEST: return LoadTexEco("chest");
default: return GenColorEco(PINK); break;
}
}

View File

@ -0,0 +1,276 @@
#include "zpl.h"
#include <math.h>
#include <stdlib.h>
#include "world/world.h"
#include "world/blocks.h"
#include "world/perlin.h"
#include "ecs/components.h"
#include "ents/entity.h"
#include "ents/vehicle.h"
#include "ents/items.h"
#include "world/blocks_info.h"
#define WORLD_BLOCK_OBSERVER(name) block_id name(block_id *data, block_id id, uint32_t block_idx)
typedef WORLD_BLOCK_OBSERVER(world_block_observer_proc);
#define WORLD_PERLIN_FREQ 100
#define WORLD_PERLIN_OCTAVES 1
#define BLOCK_INVALID 0xF
block_id worldgen_biome_find(uint32_t biome, uint32_t kind) {
asset_id asset = ASSET_INVALID;
switch (biome) {
case BLOCK_BIOME_DEV: {
switch (kind) {
case BLOCK_KIND_GROUND: asset = ASSET_GROUND; break;
case BLOCK_KIND_DIRT: asset = ASSET_DIRT; break;
case BLOCK_KIND_WALL: asset = ASSET_WALL; break;
case BLOCK_KIND_HILL_SNOW:
case BLOCK_KIND_HILL: asset = ASSET_HILL; break;
case BLOCK_KIND_WATER: asset = ASSET_WATER; break;
case BLOCK_KIND_LAVA: asset = ASSET_LAVA; break;
}
}
}
return blocks_find(asset);
}
static world_data *world;
static void world_fill_rect(block_id *data, block_id id, uint32_t x, uint32_t y, uint32_t w, uint32_t h, world_block_observer_proc *proc) {
for (uint32_t cy=y; cy<y+h; cy++) {
for (uint32_t cx=x; cx<x+w; cx++) {
if (cx < 0 || cx >= world->dim) continue;
if (cy < 0 || cy >= world->dim) continue;
uint32_t i = (cy*world->dim) + cx;
if (proc) {
block_id new_id = (*proc)(data, id, i);
if (new_id != BLOCK_INVALID) {
id = new_id;
}
else continue;
}
data[i] = id;
}
}
}
static void world_fill_circle(block_id *data, block_id id, uint32_t x, uint32_t y, uint32_t w, uint32_t h, world_block_observer_proc *proc) {
for (uint32_t cy=y; cy<y+h; cy++) {
for (uint32_t cx=x; cx<x+w; cx++) {
if (cx < 0 || cx >= world->dim) continue;
if (cy < 0 || cy >= world->dim) continue;
uint32_t i = (cy*world->dim) + cx;
if (proc) {
block_id new_id = (*proc)(data, id, i);
if (new_id != BLOCK_INVALID) {
id = new_id;
}
else continue;
}
data[i] = id;
}
}
}
static void world_fill_rect_anchor(block_id *data, block_id id, uint32_t x, uint32_t y, uint32_t w, uint32_t h, float ax, float ay, world_block_observer_proc *proc) {
uint32_t w2 = (uint32_t)floorf(w*ax);
uint32_t h2 = (uint32_t)floorf(h*ay);
world_fill_rect(data, id, x-w2, y-h2, w, h, proc);
}
static WORLD_BLOCK_OBSERVER(shaper) {
uint32_t kind = id;
uint32_t old_kind = data[block_idx];
if (kind == BLOCK_KIND_WALL && kind == old_kind) {
return worldgen_biome_find(BLOCK_BIOME_DEV, BLOCK_KIND_HILL);
}
if (kind == BLOCK_KIND_HILL && kind == old_kind) {
return worldgen_biome_find(BLOCK_BIOME_DEV, BLOCK_KIND_HILL_SNOW);
}
return id;
}
static block_id world_perlin_cond_offset(uint32_t block_idx, double chance, uint32_t ofx, uint32_t ofy) {
uint32_t x = block_idx % world->dim + ofx;
uint32_t y = block_idx / world->dim + ofy;
return perlin_fbm(world->seed, x, y, WORLD_PERLIN_FREQ, WORLD_PERLIN_OCTAVES) < chance;
}
static block_id world_perlin_cond(uint32_t block_idx, double chance) {
return world_perlin_cond_offset(block_idx, chance, 0, 0);
}
#if 1
static WORLD_BLOCK_OBSERVER(shaper_noise80) {
return world_perlin_cond(block_idx, 0.80) ? shaper(data, id, block_idx) : BLOCK_INVALID;
}
static WORLD_BLOCK_OBSERVER(shaper_noise50) {
return world_perlin_cond(block_idx, 0.50) ? shaper(data, id, block_idx) : BLOCK_INVALID;
}
static WORLD_BLOCK_OBSERVER(shaper_noise33) {
return world_perlin_cond(block_idx, 0.33) ? shaper(data, id, block_idx) : BLOCK_INVALID;
}
static WORLD_BLOCK_OBSERVER(shaper_noise05) {
return world_perlin_cond(block_idx, 0.05) ? shaper(data, id, block_idx) : BLOCK_INVALID;
}
static WORLD_BLOCK_OBSERVER(shaper_noise05b) {
return world_perlin_cond_offset(block_idx, 0.05, 32, 0) ? shaper(data, id, block_idx) : BLOCK_INVALID;
}
static WORLD_BLOCK_OBSERVER(shaper_noise01b) {
return world_perlin_cond_offset(block_idx, 0.01, 32, 0) ? shaper(data, id, block_idx) : BLOCK_INVALID;
}
#else
static WORLD_BLOCK_OBSERVER(shaper_noise80) {
return rand()%10 < 8 ? shaper(id, block_idx) : BLOCK_INVALID;
}
static WORLD_BLOCK_OBSERVER(shaper_noise50) {
return rand()%10 < 5 ? shaper(id, block_idx) : BLOCK_INVALID;
}
static WORLD_BLOCK_OBSERVER(shaper_noise33) {
return rand()%10 < 3 ? shaper(id, block_idx) : BLOCK_INVALID;
}
#endif
#if 0
static void world_fill_mountain(uint32_t x, uint32_t y) {
}
#endif
#define RAND_RANGE(x,y) (x + (int)rand()%(y-(x)))
#define RAND_RANGEF(x,y) ((float)RAND_RANGE(x,y))
int32_t worldgen_build(world_data *wld) {
// TODO(zaklaus): pass world as an arg instead
world = wld;
// TODO: perform world gen
// atm, we will fill the world with ground and surround it by walls
block_id wall_id = worldgen_biome_find(BLOCK_BIOME_DEV, BLOCK_KIND_WALL);
block_id grnd_id = worldgen_biome_find(BLOCK_BIOME_DEV, BLOCK_KIND_GROUND);
block_id dirt_id = worldgen_biome_find(BLOCK_BIOME_DEV, BLOCK_KIND_DIRT);
block_id watr_id = worldgen_biome_find(BLOCK_BIOME_DEV, BLOCK_KIND_WATER);
block_id lava_id = worldgen_biome_find(BLOCK_BIOME_DEV, BLOCK_KIND_LAVA);
block_id tree_id = blocks_find(ASSET_TREE);
srand(world->seed);
// walls
world_fill_rect(world->data, wall_id, 0, 0, world->dim, world->dim, NULL);
// ground
world_fill_rect(world->data, grnd_id, 1, 1, world->dim-2, world->dim-2, NULL);
world_fill_rect(world->data, dirt_id, 1, 1, world->dim-2, world->dim-2, shaper_noise05);
world_fill_rect(world->outer_data, tree_id, 1, 1, world->dim-2, world->dim-2, shaper_noise01b);
// water
#if 1
for (int i=0; i<RAND_RANGE(58, 92); i++) {
world_fill_rect_anchor(world->data, watr_id, RAND_RANGE(0, world->dim), RAND_RANGE(0, world->dim), 4+RAND_RANGE(0,3), 4+RAND_RANGE(0,3), 0.5f, 0.5f, shaper_noise80);
}
#endif
// ice rink
#if 0
world_fill_rect_anchor(world->data, watr_id, 450, 125, 10, 10, 0.0f, 0.0f, NULL);
#endif
// lava
#if 1
for (int i=0; i<RAND_RANGE(48, 62); i++) {
world_fill_rect_anchor(world->data, lava_id, RAND_RANGE(0, world->dim), RAND_RANGE(0, world->dim), 4+RAND_RANGE(0,3), 4+RAND_RANGE(0,3), 0.5f, 0.5f, shaper_noise80);
}
#endif
// hills
#if 1
const uint32_t HILLS_SIZE = 21;
for (int i=0; i<RAND_RANGE(8, 124); i++) {
world_fill_rect_anchor(world->data, wall_id, RAND_RANGE(0, world->dim), RAND_RANGE(0, world->dim), RAND_RANGE(0,HILLS_SIZE), RAND_RANGE(0,HILLS_SIZE), 0.5f, 0.5f, shaper_noise50);
}
#endif
// vehicles
#if 1
for (int i=0; i<RAND_RANGE(258, 1124); i++) {
uint64_t e = vehicle_spawn();
Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
dest->y = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
entity_set_position(e, dest->x, dest->y);
}
#endif
// items
#if 1
for (int i=0; i<RAND_RANGE(328, 164); i++) {
uint64_t e = item_spawn(ASSET_DEMO_ICEMAKER, 32);
Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
dest->y = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
entity_set_position(e, dest->x, dest->y);
}
for (int i=0; i<RAND_RANGE(328, 164); i++) {
uint64_t e = item_spawn(ASSET_FENCE, 64);
Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
dest->y = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
entity_set_position(e, dest->x, dest->y);
}
for (int i=0; i<RAND_RANGE(328, 164); i++) {
uint64_t e = item_spawn(ASSET_WOOD, 64);
Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
dest->y = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
entity_set_position(e, dest->x, dest->y);
}
for (int i=0; i<RAND_RANGE(128, 564); i++) {
uint64_t e = item_spawn(ASSET_BELT, 999);
Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
dest->y = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
entity_set_position(e, dest->x, dest->y);
}
for (int i=0; i<RAND_RANGE(128, 964); i++) {
uint64_t e = item_spawn(ASSET_CHEST, 4);
Position *dest = ecs_get_mut(world_ecs(), e, Position);
dest->x = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
dest->y = RAND_RANGEF(0, world->dim*WORLD_BLOCK_SIZE);
entity_set_position(e, dest->x, dest->y);
}
#endif
return WORLD_ERROR_NONE;
}