big chungus

master
Dominik Madarász 2023-07-26 19:05:50 +02:00
parent 213710e922
commit a9efa0f01c
16 changed files with 197 additions and 21 deletions

View File

@ -26,6 +26,7 @@ void game_update();
void game_render();
void game_player_joined(uint64_t ent);
void game_player_departed(uint64_t ent);
void game_player_died(uint64_t ent);
void game_client_receive_code(pkt_send_code data);
// base methods called from games

View File

@ -106,6 +106,10 @@ typedef struct {
typedef struct { char _unused; } Dead;
typedef struct {
uint16_t timer;
} Respawn;
typedef struct {
float amt;
} HealthRegen;
@ -277,6 +281,7 @@ typedef struct {
X(ClientInfo)\
X(Health)\
X(Dead)\
X(Respawn)\
X(HealthRegen)\
X(HealDelay)\
X(Mob)\

View File

@ -121,9 +121,6 @@ uint64_t storage_spawn(void) {
uint64_t mob_spawn(void) {
ecs_entity_t e = entity_spawn(EKIND_MONSTER);
Health *hp = ecs_get_mut(world_ecs(), e, Health);
hp->max_hp = hp->hp = 100.0f;
ecs_add(world_ecs(), e, Mob);
ecs_set(world_ecs(), e, Health, { 60, 60, 0 });
ecs_set(world_ecs(), e, PhysicsBody, { .kind = PHYS_AABB, .mass = 1.0f });

View File

@ -50,6 +50,7 @@ void OnDead(ecs_iter_t *it) {
if (ci) {
pkt_notification_send(0, 0, "Someone died!", zpl_bprintf("Player %d has died!", it->entities[i]));
game_player_died(it->entities[i]);
}
if (pi) {

View File

@ -110,6 +110,14 @@ void VehicleHandling(ecs_iter_t *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);
// if (in->x != 0) {
// // Add a sideways velocity to the car. This will make the car move sideways,
// // giving the appearance of a drift. The actual amount of sideways velocity
// // will need to be fine-tuned.
// v[i].x += in->x * steer_mod * -zpl_sin(car->heading) * car->force;
// v[i].y += in->x * steer_mod * zpl_cos(car->heading) * car->force;
// }
}
}

View File

@ -320,6 +320,7 @@ void world_setup_ecs(void) {
ECS_IMPORT(world.ecs, Components);
ECS_IMPORT(world.ecs, Systems);
world.ecs_update = ecs_query_new(world.ecs, "components.ClientInfo, components.Position");
world.ecs_alive_player = ecs_query_new(world.ecs, "components.ClientInfo, components.Position, !components.Dead");
world.ecs_clientinfo = ecs_query_new(world.ecs, "components.ClientInfo");
world.ecs_layeroverriden = ecs_query_new(world.ecs, "components.StreamLayerOverride");
}
@ -531,6 +532,10 @@ ecs_query_t* world_ecs_player(void) {
return world.ecs_update;
}
ecs_query_t* world_ecs_alive_player(void) {
return world.ecs_alive_player;
}
ecs_query_t* world_ecs_clientinfo(void) {
return world.ecs_clientinfo;
}

View File

@ -52,6 +52,7 @@ typedef struct {
ecs_world_t *ecs;
ecs_world_t *ecs_stage;
ecs_query_t *ecs_update;
ecs_query_t *ecs_alive_player;
ecs_query_t *ecs_clientinfo;
ecs_query_t *ecs_layeroverriden;
ecs_entity_t *chunk_mapping;
@ -72,6 +73,7 @@ uint32_t world_buf(block_id const **ptr, uint32_t *width);
uint32_t world_seed(void);
ecs_world_t *world_ecs(void);
ecs_query_t *world_ecs_player(void);
ecs_query_t *world_ecs_alive_player(void);
ecs_query_t *world_ecs_clientinfo(void);
void world_set_stage(ecs_world_t *ecs);
librg_world *world_tracker(void);

View File

@ -21,6 +21,10 @@ void game_player_departed(uint64_t ent) {
}
void game_player_died(uint64_t ent) {
}
void game_client_receive_code(pkt_send_code data) {
}

View File

@ -22,6 +22,10 @@ void game_player_departed(uint64_t ent) {
}
void game_player_died(uint64_t ent) {
}
void game_client_receive_code(pkt_send_code data) {
}

View File

@ -19,12 +19,60 @@ static ecs_query_t *ecs_pawn_query = NULL;
#include "system_mob.c"
#include "system_weapon.c"
#define PLAYER_RESPAWN_BLAST_FORCE 1200.0f
void PlayerRespawn(ecs_iter_t *it) {
Respawn *r = ecs_field(it, Respawn, 1);
Input *in = ecs_field(it, Input, 2);
Sprite *s = ecs_field(it, Sprite, 3);
Position *p = ecs_field(it, Position, 4);
Health *h = ecs_field(it, Health, 5);
for (int i = 0; i < it->count; i++) {
if (r[i].timer > 0) {
TICK_VAR(r[i].timer);
continue;
}
ecs_remove(it->world, it->entities[i], Respawn);
ecs_remove(it->world, it->entities[i], Dead);
in[i].is_blocked = 0;
s[i].spritesheet = 0;
h[i].hp = h[i].max_hp;
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++) {
uint64_t ent_id = ents[j];
if (!ecs_get(it->world, ent_id, Mob) || ecs_get(it->world, ent_id, Dead)) {
continue;
}
const Position *p2 = ecs_get(it->world, ent_id, Position);
Velocity *v = ecs_get_mut(it->world, ent_id, 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 <= 5*WORLD_BLOCK_SIZE) {
Health *hp = ecs_get_mut(it->world, ent_id, Health);
hp->dmg += hp->max_hp/2.0f;
v->x += (dx/range)*PLAYER_RESPAWN_BLAST_FORCE;
v->y += (dy/range)*PLAYER_RESPAWN_BLAST_FORCE;
}
}
}
}
void mob_systems(ecs_world_t *ecs) {
ECS_SYSTEM_TICKED_EX(ecs, MobDetectPlayers, EcsPostUpdate, 100.0f, components.Position, components.Mob, !components.Dead);
ECS_SYSTEM(ecs, MobMovement, EcsPostUpdate, components.Velocity, components.Position, components.MobHuntPlayer, !components.Dead);
ECS_SYSTEM_TICKED(ecs, MobMeleeAtk, EcsPostUpdate, components.Position, components.Mob, components.MobHuntPlayer, components.MobMelee, !components.Dead);
ECS_SYSTEM_TICKED(ecs, MobDespawnDead, EcsPostUpdate, components.Mob, components.Dead);
ECS_SYSTEM_TICKED(ecs, MobSpawner, EcsPostUpdate, components.Input, components.Position);
ECS_SYSTEM_TICKED(ecs, MobSpawner, EcsPostUpdate, components.Input, components.Position, !components.Dead);
ECS_SYSTEM_TICKED(ecs, PlayerRespawn, EcsPostUpdate, components.Respawn, components.Input, components.Sprite, components.Position, components.Health);
//NOTE(DavoSK): weapons
ecs_mobpos_query = ecs_query_new(world_ecs(), "components.Mob, components.Position, components.Health, components.Velocity, !components.Dead");
@ -51,6 +99,10 @@ void game_setup_ecs() {
mob_systems(world_ecs());
}
void game_player_departed(uint64_t ent) {
}
void game_player_joined(uint64_t ent) {
notification_push("test1", "Hello World!");
@ -63,8 +115,14 @@ void game_player_joined(uint64_t ent) {
});
}
void game_player_departed(uint64_t ent) {
void game_player_died(uint64_t ent) {
Sprite *spr = ecs_get_mut(world_ecs(), ent, Sprite);
Velocity *v = ecs_get_mut(world_ecs(), ent, Velocity);
spr->frame = 3 + (rand()%5);
spr->spritesheet = 69; /*special code*/
*v = (Velocity){0.0f, 0.0f};
ecs_remove(world_ecs(), ent, PhysicsBody);
ecs_set(world_ecs(), ent, Respawn, { 100 });
}
void game_client_receive_code(pkt_send_code data) {

View File

@ -27,14 +27,14 @@ ZPL_DIAGNOSTIC_POP
/*
TODO
- monster spawner
- the longer we survive, the more and stronger enemies we spawn
+ monster spawner
+ the longer we survive, the more and stronger enemies we spawn
- player grows HP by leveling up
- XP increases by killing mobs
- player can pick an "ability" upon reaching level milestones
- abilities: armor/shield, TODO ...
- enemies damage player when close to him in ticks (damage effects, ...)
- basic projectile pooling (flecs)
+ enemies damage player when close to him in ticks (damage effects, ...)
+ basic projectile pooling (flecs)
- somewhat believable world gen, small hamlets with cols, etc
*/

View File

@ -109,6 +109,7 @@ void platform_render() {
debug_draw();
#if defined(_DEBUG)
if (nk_begin(game_ui, "Spritesheet Viewer", nk_rect(460, 100, 800, 600),
NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE|NK_WINDOW_TITLE))
{
@ -117,6 +118,7 @@ void platform_render() {
}
notification_draw();
#endif
game_draw_ui();
}
EndDrawing();

View File

@ -23,7 +23,9 @@ void DrawNametag(const char* name, uint64_t key, entity_view *data, float x, flo
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));
if (health > 0.0f && health <= 1.0f) {
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);
}
@ -46,7 +48,8 @@ void renderer_draw_entry(uint64_t key, entity_view *data, game_world_render_entr
tex.texture.height *= (int32_t)scale;
DrawTextureRec(tex.texture, (Rectangle){0, 0, size, -size}, (Vector2){x, y}, ColorAlpha(WHITE, data->tran_time));
} else {
DrawTextureRec(GetBlockImage(entry->blk_id), ASSET_SRC_RECT(), (Vector2){entry->x-(WORLD_BLOCK_SIZE/2), entry->y-(WORLD_BLOCK_SIZE/2)}, ColorAlpha(WHITE, data->tran_time));
Texture2D tex = GetBlockImage(entry->blk_id);
DrawTextureRec(tex, ASSET_SRC_RECT_TEX(tex.width, tex.height), (Vector2){entry->x-tex.width/2.0f, entry->y-(tex.height-WORLD_BLOCK_SIZE/2)}, ColorAlpha(WHITE, data->tran_time));
}
}break;
case EKIND_VEHICLE: {
@ -87,9 +90,7 @@ void renderer_draw_entry(uint64_t key, entity_view *data, game_world_render_entr
case EKIND_MONSTER: {
float x = data->x;
float y = data->y;
//DrawCircleEco(x, y, size, ColorAlpha(PINK, data->tran_time));
// DrawTextureRec(GetSpriteTexture2D(assets_find(data->frame)), ASSET_SRC_RECT(), (Vector2){data->x-(WORLD_BLOCK_SIZE/2), data->y-(WORLD_BLOCK_SIZE/2)}, ColorAlpha(WHITE, data->tran_time));
DrawSpriteEco(&main_sprite_sheet, data->frame, x, y, 0.0f, 2.0f, WHITE);
DrawSpriteEco(&main_sprite_sheet, data->frame, x, y, 0.0f, 2.0f, ColorAlpha(WHITE, data->tran_time));
float health = (data->hp / data->max_hp);
@ -105,7 +106,11 @@ void renderer_draw_entry(uint64_t key, entity_view *data, game_world_render_entr
DrawNametag("Player", key, data, x, y-16);
//DrawTextureRec(GetSpriteTexture2D(assets_find(ASSET_PLAYER)), ASSET_SRC_RECT(), (Vector2){data->x-(WORLD_BLOCK_SIZE/2), data->y-(WORLD_BLOCK_SIZE/2)}, ColorAlpha(WHITE, data->tran_time));
//DrawCircleEco(x, y, size, ColorAlpha(YELLOW, data->tran_time));
DrawSpriteEco(test_player_anim.spritesheet, TickSpriteAnimation(&test_player_anim), x, y, 0.0f, 2.0f, WHITE);
if (data->spritesheet == 69)
DrawSpriteEco(&main_sprite_sheet, data->frame, x, y, 0.0f, 2.0f, ColorAlpha(WHITE, data->tran_time));
else
DrawSpriteEco(test_player_anim.spritesheet, TickSpriteAnimation(&test_player_anim), x, y, 0.0f, 2.0f, WHITE);
//if (data->has_items && !data->inside_vehicle) {
// float ix = data->x;

View File

@ -1,8 +1,8 @@
#define MOB_SPAWN_DELAY (uint16_t)(20*1.5f)
#define MOB_SPAWN_PERCENTAGE_FROM_MAX 0.05f
#define MOB_INITIAL_MAX 50
#define MOB_GROWTH_FACTOR 5.0f
#define MOB_GROWTH_CONTROL 50.0f
#define MOB_GROWTH_FACTOR 0.65f
#define MOB_GROWTH_CONTROL 1.17f
#define MOB_SPAWN_DIST 12*WORLD_BLOCK_SIZE;
#define MOB_MAX_SPAWN_TRIES 5
@ -12,8 +12,7 @@ static uint16_t mob_spawn_timer = MOB_SPAWN_DELAY;
static int16_t player_spawn_counter = -1;
void recalc_max_mobs() {
max_mobs = (uint64_t)zpl_round(MOB_INITIAL_MAX + MOB_GROWTH_FACTOR * zpl_exp((float)mob_kills / MOB_GROWTH_CONTROL));
zpl_printf("Max mobs: %d\n", max_mobs);
max_mobs = (uint64_t)(MOB_INITIAL_MAX + MOB_GROWTH_FACTOR * zpl_pow((float)mob_kills, MOB_GROWTH_CONTROL));
}
void MobDetectPlayers(ecs_iter_t *it) {
@ -23,7 +22,7 @@ void MobDetectPlayers(ecs_iter_t *it) {
float closest_ent_dist = ZPL_F32_MAX;
uint64_t closest_ent = 0;
ecs_iter_t pit = ecs_query_iter(world_ecs(), world_ecs_player());
ecs_iter_t pit = ecs_query_iter(world_ecs(), world_ecs_alive_player());
while (ecs_query_next(&pit)) {
Position *p2 = ecs_field(&pit, Position, 2);
@ -54,6 +53,10 @@ void MobMovement(ecs_iter_t *it) {
for (int i = 0; i < it->count; i++) {
const Position *p2 = ecs_get(it->world, m->plr, Position);
if (p2 == NULL) {
ecs_remove(it->world, it->entities[i], MobHuntPlayer);
continue;
}
zpl_vec2 pos1 = { .x = p[i].x, .y = p[i].y };
zpl_vec2 pos2 = { .x = p2->x, .y = p2->y };
zpl_vec2 dir;
@ -71,6 +74,7 @@ void MobMovement(ecs_iter_t *it) {
#define MOB_MELEE_DMG 8.5f
#define MOB_ATK_DELAY 10
#define MOB_DESPAWN_TIMER 20*60*5
#define MOB_HIT_FORCE_PUSH 250.0f
void MobMeleeAtk(ecs_iter_t *it) {
Position *p = ecs_field(it, Position, 1);
@ -83,15 +87,25 @@ void MobMeleeAtk(ecs_iter_t *it) {
continue;
}
if (ecs_get(it->world, m->plr, Dead)) {
ecs_remove(it->world, it->entities[i], MobHuntPlayer);
continue;
}
const Position *p2 = ecs_get(it->world, m->plr, Position);
Velocity *v2 = ecs_get_mut(it->world, m->plr, Velocity);
float dx = p2->x - p[i].x;
float dy = p2->y - p[i].y;
float range = (dx*dx + dy*dy);
if (range < MOB_MELEE_DIST) {
float dd = zpl_sqrt(range);
Health *health = ecs_get_mut(it->world, m->plr, Health);
health->dmg += MOB_MELEE_DMG;
mob[i].atk_delay = MOB_ATK_DELAY;
v2->x += (dx/dd)*MOB_HIT_FORCE_PUSH;
v2->y += (dy/dd)*MOB_HIT_FORCE_PUSH;
}
}
}

View File

@ -34,6 +34,7 @@ block_id worldgen_biome_find(uint32_t biome, uint32_t kind) {
return blocks_find(asset);
}
#if 0
int32_t worldgen_build(world_data *wld) {
// TODO(zaklaus): pass world as an arg instead
world = wld;
@ -67,3 +68,58 @@ int32_t worldgen_build(world_data *wld) {
return WORLD_ERROR_NONE;
}
#else
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->outer_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
return WORLD_ERROR_NONE;
}
#endif

View File

@ -0,0 +1,14 @@
@echo off
rem build web first
call web.bat
if not %ERRORLEVEL% == 0 exit /B 1
@rd /S /Q pkg
mkdir pkg
copy ..\build_web\survival.* pkg
copy ..\build_web\survival.html pkg\index.html
IF NOT "%1"=="SKIP_DEPLOY" (
butler push pkg zaklaus/zplsurvival:html-latest
)