Enemy swarm movement
This commit is contained in:
@@ -447,8 +447,8 @@
|
|||||||
"type":"",
|
"type":"",
|
||||||
"visible":true,
|
"visible":true,
|
||||||
"width":10,
|
"width":10,
|
||||||
"x":1126.83666666667,
|
"x":1126.84,
|
||||||
"y":592.916666666667
|
"y":592.917
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"gid":28,
|
"gid":28,
|
||||||
@@ -459,8 +459,8 @@
|
|||||||
"type":"",
|
"type":"",
|
||||||
"visible":true,
|
"visible":true,
|
||||||
"width":10,
|
"width":10,
|
||||||
"x":1140.66666666667,
|
"x":1140.67,
|
||||||
"y":594.666666666667
|
"y":594.667
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"gid":28,
|
"gid":28,
|
||||||
@@ -471,8 +471,8 @@
|
|||||||
"type":"",
|
"type":"",
|
||||||
"visible":true,
|
"visible":true,
|
||||||
"width":10,
|
"width":10,
|
||||||
"x":1125.66666666667,
|
"x":1125.67,
|
||||||
"y":609.666666666667
|
"y":609.667
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"gid":28,
|
"gid":28,
|
||||||
@@ -484,7 +484,7 @@
|
|||||||
"visible":true,
|
"visible":true,
|
||||||
"width":10,
|
"width":10,
|
||||||
"x":1139,
|
"x":1139,
|
||||||
"y":608.666666666667
|
"y":608.667
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"gid":28,
|
"gid":28,
|
||||||
@@ -520,6 +520,162 @@
|
|||||||
"width":0,
|
"width":0,
|
||||||
"x":1119,
|
"x":1119,
|
||||||
"y":571.5
|
"y":571.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"height":0,
|
||||||
|
"id":8,
|
||||||
|
"name":"spawn",
|
||||||
|
"point":true,
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":0,
|
||||||
|
"x":32.0605727272727,
|
||||||
|
"y":768.242090909091
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"height":0,
|
||||||
|
"id":9,
|
||||||
|
"name":"p1",
|
||||||
|
"point":true,
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":0,
|
||||||
|
"x":150,
|
||||||
|
"y":510
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"height":0,
|
||||||
|
"id":20,
|
||||||
|
"name":"p0",
|
||||||
|
"point":true,
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":0,
|
||||||
|
"x":97.5,
|
||||||
|
"y":644.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"height":0,
|
||||||
|
"id":10,
|
||||||
|
"name":"p2",
|
||||||
|
"point":true,
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":0,
|
||||||
|
"x":226,
|
||||||
|
"y":353.333
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"height":0,
|
||||||
|
"id":11,
|
||||||
|
"name":"p3",
|
||||||
|
"point":true,
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":0,
|
||||||
|
"x":331.333,
|
||||||
|
"y":254
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"height":0,
|
||||||
|
"id":12,
|
||||||
|
"name":"p4",
|
||||||
|
"point":true,
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":0,
|
||||||
|
"x":458,
|
||||||
|
"y":214.667
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"height":0,
|
||||||
|
"id":13,
|
||||||
|
"name":"p5",
|
||||||
|
"point":true,
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":0,
|
||||||
|
"x":613.333,
|
||||||
|
"y":198.667
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"height":0,
|
||||||
|
"id":14,
|
||||||
|
"name":"p6",
|
||||||
|
"point":true,
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":0,
|
||||||
|
"x":744,
|
||||||
|
"y":226.667
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"height":0,
|
||||||
|
"id":15,
|
||||||
|
"name":"p7",
|
||||||
|
"point":true,
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":0,
|
||||||
|
"x":830.667,
|
||||||
|
"y":266.667
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"height":0,
|
||||||
|
"id":16,
|
||||||
|
"name":"p8",
|
||||||
|
"point":true,
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":0,
|
||||||
|
"x":912,
|
||||||
|
"y":325.333
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"height":0,
|
||||||
|
"id":17,
|
||||||
|
"name":"p9",
|
||||||
|
"point":true,
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":0,
|
||||||
|
"x":982,
|
||||||
|
"y":389.333
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"height":0,
|
||||||
|
"id":18,
|
||||||
|
"name":"p10",
|
||||||
|
"point":true,
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":0,
|
||||||
|
"x":1038.67,
|
||||||
|
"y":465.333
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"height":0,
|
||||||
|
"id":19,
|
||||||
|
"name":"p11",
|
||||||
|
"point":true,
|
||||||
|
"rotation":0,
|
||||||
|
"type":"",
|
||||||
|
"visible":true,
|
||||||
|
"width":0,
|
||||||
|
"x":1098.67,
|
||||||
|
"y":530
|
||||||
}],
|
}],
|
||||||
"opacity":1,
|
"opacity":1,
|
||||||
"type":"objectgroup",
|
"type":"objectgroup",
|
||||||
@@ -528,7 +684,7 @@
|
|||||||
"y":0
|
"y":0
|
||||||
}],
|
}],
|
||||||
"nextlayerid":10,
|
"nextlayerid":10,
|
||||||
"nextobjectid":8,
|
"nextobjectid":21,
|
||||||
"orientation":"orthogonal",
|
"orientation":"orthogonal",
|
||||||
"renderorder":"right-down",
|
"renderorder":"right-down",
|
||||||
"tiledversion":"1.10.2",
|
"tiledversion":"1.10.2",
|
||||||
|
|||||||
@@ -293,8 +293,7 @@ typedef struct Health {
|
|||||||
extern ECS_COMPONENT_DECLARE(Health);
|
extern ECS_COMPONENT_DECLARE(Health);
|
||||||
|
|
||||||
typedef struct Swarm {
|
typedef struct Swarm {
|
||||||
|
i32 currWaypoint;
|
||||||
int _;
|
|
||||||
} Swarm;
|
} Swarm;
|
||||||
extern ECS_COMPONENT_DECLARE(Swarm);
|
extern ECS_COMPONENT_DECLARE(Swarm);
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ enum {
|
|||||||
COLL_LAYER_TRANSPARENCY = 7,
|
COLL_LAYER_TRANSPARENCY = 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define PLAYER_ENEMY PLAYER_BLUE
|
||||||
|
|
||||||
typedef enum Player {
|
typedef enum Player {
|
||||||
PLAYER_RED = 0,
|
PLAYER_RED = 0,
|
||||||
PLAYER_BLUE = 1,
|
PLAYER_BLUE = 1,
|
||||||
|
|||||||
@@ -115,10 +115,57 @@ ecs_entity_t entityCreateWorker(const Position position, Player player, Game *ga
|
|||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
ecs_entity_t entityRecruit(EntityType type, Position position, Player player, Game *game) {
|
ecs_entity_t entityCreateSwarmGoblin(const Position position, Player player, Game *game) {
|
||||||
|
ecs_entity_t e = entityCreateBaseUnit(position, 10.0f, player, ENTITY_GOBLIN, ANIM_IDLE, game);
|
||||||
|
ecs_set(ECS, e, Swarm, {
|
||||||
|
.currWaypoint = 0,
|
||||||
|
});
|
||||||
|
ecs_set(ECS, e, Health, {
|
||||||
|
.startHP = 40.0f,
|
||||||
|
.hp = 40.0f,
|
||||||
|
.lastChanged = -1.0f
|
||||||
|
});
|
||||||
|
|
||||||
|
Unit *unit = ecs_get_mut(ECS, e, Unit);
|
||||||
|
unit->minDamage = 5.0f;
|
||||||
|
unit->maxDamage = 10.0f;
|
||||||
|
unit->attackCooldown = 1.0f;
|
||||||
|
unit->maxSpeed = 20.0f;
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
ecs_entity_t entityCreateSwarmOrc(const Position position, Player player, Game *game) {
|
||||||
|
ecs_entity_t e = entityCreateBaseUnit(position, 10.0f, player, ENTITY_ORC, ANIM_IDLE, game);
|
||||||
|
ecs_set(ECS, e, Swarm, {
|
||||||
|
.currWaypoint = 0,
|
||||||
|
});
|
||||||
|
ecs_set(ECS, e, Health, {
|
||||||
|
.startHP = 80.0f,
|
||||||
|
.hp = 80.0f,
|
||||||
|
.lastChanged = -1.0f
|
||||||
|
});
|
||||||
|
|
||||||
|
Unit *unit = ecs_get_mut(ECS, e, Unit);
|
||||||
|
unit->minDamage = 8.0f;
|
||||||
|
unit->maxDamage = 22.0f;
|
||||||
|
unit->attackCooldown = 1.8f;
|
||||||
|
unit->maxSpeed = 12.0f;
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
ecs_entity_t entityCreate(EntityType type, Position position, Player player, Game *game) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ENTITY_WORKER:
|
case ENTITY_WORKER:
|
||||||
entityCreateWorker(position, player, game);
|
return entityCreateWorker(position, player, game);
|
||||||
|
case ENTITY_SOLDIER:
|
||||||
|
return entityCreateSoldier(position, player, game);
|
||||||
|
case ENTITY_WARRIOR:
|
||||||
|
return entityCreateWarrior(position, player, game);
|
||||||
|
case ENTITY_GOBLIN:
|
||||||
|
return entityCreateSwarmGoblin(position, player, game);
|
||||||
|
case ENTITY_ORC:
|
||||||
|
return entityCreateSwarmOrc(position, player, game);
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,10 @@ ecs_entity_t entityCreateBaseUnit(const Position position, f32 size, Player play
|
|||||||
ecs_entity_t entityCreateSoldier(const Position position, Player player, Game *game);
|
ecs_entity_t entityCreateSoldier(const Position position, Player player, Game *game);
|
||||||
ecs_entity_t entityCreateWarrior(const Position position, Player player, Game *game);
|
ecs_entity_t entityCreateWarrior(const Position position, Player player, Game *game);
|
||||||
ecs_entity_t entityCreateWorker(const Position position, Player player, Game *game);
|
ecs_entity_t entityCreateWorker(const Position position, Player player, Game *game);
|
||||||
|
ecs_entity_t entityCreateSwarmGoblin(const Position position, Player player, Game *game);
|
||||||
|
ecs_entity_t entityCreateSwarmOrc(const Position position, Player player, Game *game);
|
||||||
|
|
||||||
ecs_entity_t entityRecruit(EntityType type, Position position, Player player, Game *game);
|
ecs_entity_t entityCreate(EntityType type, Position position, Player player, Game *game);
|
||||||
|
|
||||||
void getEntityCost(EntityType type, i32 cost[RES_COUNT]);
|
void getEntityCost(EntityType type, i32 cost[RES_COUNT]);
|
||||||
bool canAffordEntity(EntityType type, PlayerResources res);
|
bool canAffordEntity(EntityType type, PlayerResources res);
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ typedef struct PlayerResources {
|
|||||||
i64 popCapacity;
|
i64 popCapacity;
|
||||||
} PlayerResources;
|
} PlayerResources;
|
||||||
|
|
||||||
|
#define MAX_SWARM_WAYPOINTS 16
|
||||||
|
|
||||||
typedef struct Game {
|
typedef struct Game {
|
||||||
GameScreen screen;
|
GameScreen screen;
|
||||||
GameScreen nextScreen;
|
GameScreen nextScreen;
|
||||||
@@ -80,6 +82,10 @@ typedef struct Game {
|
|||||||
WaveInfo waveInfo;
|
WaveInfo waveInfo;
|
||||||
ecs_entity_t keepEntity;
|
ecs_entity_t keepEntity;
|
||||||
|
|
||||||
|
Vector2 swarmWaypoints[MAX_SWARM_WAYPOINTS];
|
||||||
|
i32 swamNumWaypoints;
|
||||||
|
Vector2 swarmSpawn;
|
||||||
|
|
||||||
BzStackAlloc stackAlloc;
|
BzStackAlloc stackAlloc;
|
||||||
struct {
|
struct {
|
||||||
BzBTNode *workerHarvest;
|
BzBTNode *workerHarvest;
|
||||||
|
|||||||
@@ -480,7 +480,7 @@ void update(float dt, void *userData) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateWave(&game->waveInfo, dt);
|
updateWave(&game->waveInfo, game, dt);
|
||||||
|
|
||||||
SoundState *soundState = ecs_singleton_get_mut(ECS, SoundState);
|
SoundState *soundState = ecs_singleton_get_mut(ECS, SoundState);
|
||||||
soundsUpdate(soundState, getCameraBounds(game->camera));
|
soundsUpdate(soundState, getCameraBounds(game->camera));
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "map_init.h"
|
#include "map_init.h"
|
||||||
|
|
||||||
#include <flecs.h>
|
#include <flecs.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "building_factory.h"
|
#include "building_factory.h"
|
||||||
#include "components.h"
|
#include "components.h"
|
||||||
@@ -10,14 +11,37 @@
|
|||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "systems/systems.h"
|
#include "systems/systems.h"
|
||||||
|
|
||||||
|
i32 getSwarmWaypointIdx(u32 id) {
|
||||||
|
char buf[4];
|
||||||
|
for (i32 i = 0; i < MAX_SWARM_WAYPOINTS; i++) {
|
||||||
|
snprintf(buf, sizeof(buf), "p%d", i);
|
||||||
|
if (id == bzStringDefaultHash(buf))
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
bool initGameObjectsLayer(BzTileMap *map, BzTileObjectGroup *objectGroup) {
|
bool initGameObjectsLayer(BzTileMap *map, BzTileObjectGroup *objectGroup) {
|
||||||
Game *game = ecs_singleton_get_mut(ECS, Game);
|
Game *game = ecs_singleton_get_mut(ECS, Game);
|
||||||
|
game->swamNumWaypoints = 0;
|
||||||
for (i32 i = 0; i < objectGroup->objectCount; i++) {
|
for (i32 i = 0; i < objectGroup->objectCount; i++) {
|
||||||
BzTileObject object = objectGroup->objects[i];
|
BzTileObject object = objectGroup->objects[i];
|
||||||
if (bzStringDefaultHash("camera") == object.id) {
|
if (bzStringDefaultHash("camera") == object.id) {
|
||||||
game->camera.target.x = object.shape.x;
|
game->camera.target.x = object.shape.x;
|
||||||
game->camera.target.y = object.shape.y;
|
game->camera.target.y = object.shape.y;
|
||||||
}
|
}
|
||||||
|
i32 swarmIdx = getSwarmWaypointIdx(object.id);
|
||||||
|
if (swarmIdx != -1) {
|
||||||
|
game->swarmWaypoints[swarmIdx] = (Vector2) {
|
||||||
|
object.shape.x,
|
||||||
|
object.shape.y,
|
||||||
|
};
|
||||||
|
game->swamNumWaypoints = BZ_MAX(game->swamNumWaypoints, swarmIdx + 1);
|
||||||
|
}
|
||||||
|
if (bzStringDefaultHash("spawn") == object.id) {
|
||||||
|
game->swarmSpawn.x = object.shape.x;
|
||||||
|
game->swarmSpawn.y = object.shape.y;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -256,6 +256,110 @@ void entityMoveToTarget(ecs_iter_t *it) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void entityMoveSwarm(ecs_iter_t *it) {
|
||||||
|
Game *game = ecs_singleton_get_mut(ECS, Game);
|
||||||
|
Position *pos = ecs_field(it, Position, 1);
|
||||||
|
Velocity *vel = ecs_field(it, Velocity, 2);
|
||||||
|
HitBox *hb = ecs_field(it, HitBox, 3);
|
||||||
|
Swarm *swarm = ecs_field(it, Swarm, 4);
|
||||||
|
Owner *owner = ecs_field(it, Owner, 5);
|
||||||
|
Steering *steer = ecs_field(it, Steering, 6);
|
||||||
|
|
||||||
|
for (i32 i = 0; i < it->count; i++) {
|
||||||
|
|
||||||
|
// Vector2 align = Vector2Zero(); // Alignment (match velocity)
|
||||||
|
Vector2 avoid = Vector2Zero(); // Separation
|
||||||
|
Vector2 target = Vector2Zero();
|
||||||
|
Vector2 cohesion = Vector2Zero(); // Cohesion (move towards center)
|
||||||
|
|
||||||
|
const f32 FRIEND_RADIUS = 22.0f;
|
||||||
|
const f32 ENEMY_RADIUS = 52.0f;
|
||||||
|
|
||||||
|
const Vector2 center = entityGetCenter(pos[i], hb[i]);
|
||||||
|
|
||||||
|
const f32 RANGE = BZ_MAX(FRIEND_RADIUS, ENEMY_RADIUS);
|
||||||
|
BzSpatialGridIter spatialIt = bzSpatialGridIter(game->entityGrid,
|
||||||
|
center.x - RANGE, center.y - RANGE,
|
||||||
|
RANGE * 2.0f, RANGE * 2.0f);
|
||||||
|
|
||||||
|
i32 numFriends = 0;
|
||||||
|
while (bzSpatialGridQueryNext(&spatialIt)) {
|
||||||
|
ecs_entity_t other = *(ecs_entity_t *) spatialIt.data;
|
||||||
|
if (!ecs_has(ECS, other, Owner))
|
||||||
|
continue;
|
||||||
|
Owner otherOwner = *ecs_get(ECS, other, Owner);
|
||||||
|
bool isFriend = owner[i].player == otherOwner.player;
|
||||||
|
|
||||||
|
if (!ecs_has(ECS, other, Position) || !ecs_has(ECS, other, HitBox))
|
||||||
|
continue;
|
||||||
|
Position otherPos = *ecs_get(ECS, other, Position);
|
||||||
|
Rectangle otherHB = *ecs_get(ECS, other, HitBox);;
|
||||||
|
Vector2 otherCenter = entityGetCenter(otherPos, otherHB);
|
||||||
|
f32 dst = Vector2Distance(center, otherCenter);
|
||||||
|
const f32 MIN_AVOID_DST = 18.0f;
|
||||||
|
const f32 MIN_ENEMY_DST = ENEMY_RADIUS;
|
||||||
|
if (isFriend) {
|
||||||
|
if (dst < MIN_AVOID_DST) {
|
||||||
|
Vector2 dif = Vector2Subtract(center, otherCenter);
|
||||||
|
dif = Vector2Scale(Vector2Normalize(dif), MIN_AVOID_DST - dst);
|
||||||
|
avoid = Vector2Add(avoid, dif);
|
||||||
|
}
|
||||||
|
Vector2Add(cohesion, otherCenter);
|
||||||
|
numFriends++;
|
||||||
|
} else {
|
||||||
|
if (dst < MIN_ENEMY_DST) {
|
||||||
|
//DrawCircleV(otherCenter, 2.0f, RED);
|
||||||
|
Vector2 dif = Vector2Subtract(otherCenter, center);
|
||||||
|
dif = Vector2Scale(Vector2Normalize(dif), MIN_ENEMY_DST - dst);
|
||||||
|
target = Vector2Add(target, dif);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numFriends > 0) {
|
||||||
|
cohesion = Vector2Divide(cohesion, (Vector2) { numFriends, numFriends });
|
||||||
|
cohesion = Vector2Subtract(cohesion, center);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//bzLogInfo("%d %d", numFriends, numEnemies);
|
||||||
|
|
||||||
|
const f32 noiseRange = 10.0f;
|
||||||
|
Vector2 noise = {
|
||||||
|
randFloatRange(-noiseRange, noiseRange),
|
||||||
|
randFloatRange(-noiseRange, noiseRange)
|
||||||
|
};
|
||||||
|
Vector2 followWaypoint = Vector2Zero();
|
||||||
|
if (swarm[i].currWaypoint < game->swamNumWaypoints) {
|
||||||
|
Vector2 waypoint = game->swarmWaypoints[swarm[i].currWaypoint];
|
||||||
|
Vector2 waypointDir = Vector2Subtract(waypoint, center);
|
||||||
|
followWaypoint = waypointDir;
|
||||||
|
|
||||||
|
f32 dst = Vector2Distance(center, waypoint);
|
||||||
|
const f32 WAYPOINT_THRESHOLD = 54.0f;
|
||||||
|
if (dst < WAYPOINT_THRESHOLD) {
|
||||||
|
swarm[i].currWaypoint++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const f32 AVOID_FACTOR = 1.0f;
|
||||||
|
const f32 TARGET_FACTOR = 2.2f;
|
||||||
|
const f32 COHESION_FACTOR = 0.10f;
|
||||||
|
const f32 WAYPOINT_FACTOR = 0.45f;
|
||||||
|
const f32 NOISE_FACTOR = 0.2f;
|
||||||
|
Vector2 move = Vector2Zero();
|
||||||
|
move = Vector2Add(move, Vector2Scale(avoid, AVOID_FACTOR));
|
||||||
|
move = Vector2Add(move, Vector2Scale(target, TARGET_FACTOR));
|
||||||
|
//move = Vector2Add(move, Vector2Scale(cohesion, COHESION_FACTOR));
|
||||||
|
move = Vector2Add(move, Vector2Scale(followWaypoint, WAYPOINT_FACTOR));
|
||||||
|
move = Vector2Add(move, Vector2Scale(noise, NOISE_FACTOR));
|
||||||
|
|
||||||
|
//bzLogInfo("%.2f %.2f", move.x, move.y);
|
||||||
|
//DrawLineV(center, Vector2Add(center, Vector2Scale(Vector2Normalize(avoid), 100)), ORANGE);
|
||||||
|
|
||||||
|
steer[i] = move;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void entityFollowPath(ecs_iter_t *it) {
|
void entityFollowPath(ecs_iter_t *it) {
|
||||||
const Game *game = ecs_singleton_get(ECS, Game);
|
const Game *game = ecs_singleton_get(ECS, Game);
|
||||||
|
|
||||||
@@ -307,7 +411,7 @@ void updateBuildingRecruitment(ecs_iter_t *it) {
|
|||||||
slot->elapsed = 0;
|
slot->elapsed = 0;
|
||||||
PlayerResources *playerRes = &game->playerResources[player];
|
PlayerResources *playerRes = &game->playerResources[player];
|
||||||
playerRes->pop--;
|
playerRes->pop--;
|
||||||
entityRecruit(slot->entityType, placePos, player, game);
|
entityCreate(slot->entityType, placePos, player, game);
|
||||||
slot->numRecruiting--;
|
slot->numRecruiting--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -391,8 +391,8 @@ void drawMainMenuUI(Game *game, f32 dt) {
|
|||||||
//loadMap(game, "assets/maps/tree_test.tmj");
|
//loadMap(game, "assets/maps/tree_test.tmj");
|
||||||
//loadMap(game, "assets/maps/entity_test.tmj");
|
//loadMap(game, "assets/maps/entity_test.tmj");
|
||||||
//loadMap(game, "assets/maps/worker_test.tmj");
|
//loadMap(game, "assets/maps/worker_test.tmj");
|
||||||
loadMap(game, "assets/maps/battle_test.tmj");
|
//loadMap(game, "assets/maps/battle_test.tmj");
|
||||||
//loadMap(game, "assets/maps/map_01.tmj");
|
loadMap(game, "assets/maps/map_01.tmj");
|
||||||
}
|
}
|
||||||
if (uiMainMenuButton("Settings", true)) {
|
if (uiMainMenuButton("Settings", true)) {
|
||||||
setScreen(game, SCREEN_SETTINGS);
|
setScreen(game, SCREEN_SETTINGS);
|
||||||
|
|||||||
@@ -181,6 +181,7 @@ void setupSystems() {
|
|||||||
ECS_SYSTEM(ECS, entityUpdate, EcsOnUpdate, Position, HitBox, Velocity, Unit, Owner, SpatialGridID);
|
ECS_SYSTEM(ECS, entityUpdate, EcsOnUpdate, Position, HitBox, Velocity, Unit, Owner, SpatialGridID);
|
||||||
|
|
||||||
ECS_SYSTEM(ECS, entityMoveToTarget, EcsOnUpdate, Position, Velocity, TargetPosition, Steering);
|
ECS_SYSTEM(ECS, entityMoveToTarget, EcsOnUpdate, Position, Velocity, TargetPosition, Steering);
|
||||||
|
ECS_SYSTEM(ECS, entityMoveSwarm, EcsOnUpdate, Position, Velocity, HitBox, Swarm, Owner, Steering);
|
||||||
ECS_SYSTEM(ECS, entityFollowPath, EcsOnUpdate, Path);
|
ECS_SYSTEM(ECS, entityFollowPath, EcsOnUpdate, Path);
|
||||||
|
|
||||||
ECS_SYSTEM(ECS, updateBuildingRecruitment, EcsOnUpdate, Owner, Building, BuildingRecruitInfo);
|
ECS_SYSTEM(ECS, updateBuildingRecruitment, EcsOnUpdate, Owner, Building, BuildingRecruitInfo);
|
||||||
|
|||||||
@@ -143,6 +143,17 @@ void entityUpdate(ecs_iter_t *it);
|
|||||||
*/
|
*/
|
||||||
void entityMoveToTarget(ecs_iter_t *it);
|
void entityMoveToTarget(ecs_iter_t *it);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 0: Game (singleton for waypoints)
|
||||||
|
* 1: Position
|
||||||
|
* 2: Velocity
|
||||||
|
* 3: HitBox
|
||||||
|
* 4: Swarm
|
||||||
|
* 5: Owner
|
||||||
|
* 6: Steering
|
||||||
|
*/
|
||||||
|
void entityMoveSwarm(ecs_iter_t *it);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 0: Game (singleton) for object pool
|
* 0: Game (singleton) for object pool
|
||||||
* 1: Path
|
* 1: Path
|
||||||
|
|||||||
27
game/wave.c
27
game/wave.c
@@ -1,6 +1,8 @@
|
|||||||
#include "wave.h"
|
#include "wave.h"
|
||||||
#include "game_state.h"
|
#include "game_state.h"
|
||||||
#include "components.h"
|
#include "components.h"
|
||||||
|
#include "entity_factory.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
WaveInfo getWaveInfo(i32 idx) {
|
WaveInfo getWaveInfo(i32 idx) {
|
||||||
BZ_ASSERT(idx >= 0);
|
BZ_ASSERT(idx >= 0);
|
||||||
@@ -24,14 +26,33 @@ WaveInfo getWaveInfo(i32 idx) {
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateWave(WaveInfo *wave, f32 dt) {
|
static Vector2 randomizeSpawnPos(Vector2 spawnPoint, i32 range) {
|
||||||
wave->orcsElapsed += dt;
|
spawnPoint.x += randFloatRange(-range, range);
|
||||||
wave->goblinsElapsed += dt;
|
spawnPoint.y += randFloatRange(-range, range);
|
||||||
|
return spawnPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateWave(WaveInfo *wave, Game *game, f32 dt) {
|
||||||
wave->elapsed += dt;
|
wave->elapsed += dt;
|
||||||
if (wave->elapsed < wave->data.timeBeforeStart)
|
if (wave->elapsed < wave->data.timeBeforeStart)
|
||||||
return;
|
return;
|
||||||
wave->started = true;
|
wave->started = true;
|
||||||
|
wave->orcsElapsed += dt;
|
||||||
|
wave->goblinsElapsed += dt;
|
||||||
|
|
||||||
|
f32 timeForGoblin = 1.0f / wave->data.goblinSendRate;
|
||||||
|
if (wave->goblinsElapsed >= timeForGoblin) {
|
||||||
|
Vector2 spawnPos = randomizeSpawnPos(game->swarmSpawn, 20);
|
||||||
|
entityCreate(ENTITY_GOBLIN, spawnPos, PLAYER_ENEMY, game);
|
||||||
|
wave->goblinsElapsed -= timeForGoblin;
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 timeForOrc = 1.0f / wave->data.orcSendRate;
|
||||||
|
if (wave->orcsElapsed >= timeForOrc) {
|
||||||
|
Vector2 spawnPos = randomizeSpawnPos(game->swarmSpawn, 20);
|
||||||
|
entityCreate(ENTITY_ORC, spawnPos, PLAYER_ENEMY, game);
|
||||||
|
wave->orcsElapsed -= timeForOrc;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isWaveSendingOver(const WaveInfo *wave) {
|
bool isWaveSendingOver(const WaveInfo *wave) {
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ typedef struct WaveInfo {
|
|||||||
|
|
||||||
#define NUM_WAVES 5
|
#define NUM_WAVES 5
|
||||||
|
|
||||||
|
typedef struct Game Game;
|
||||||
|
|
||||||
static WaveData predefWaves[NUM_WAVES] = {
|
static WaveData predefWaves[NUM_WAVES] = {
|
||||||
{ 10, 1.0f, 20, 2.0f, 0, 5 * 60 },
|
{ 10, 1.0f, 20, 2.0f, 0, 5 * 60 },
|
||||||
{ 20, 1.0f, 40, 2.0f, 0, 2 * 60 },
|
{ 20, 1.0f, 40, 2.0f, 0, 2 * 60 },
|
||||||
@@ -35,7 +37,7 @@ static WaveData predefWaves[NUM_WAVES] = {
|
|||||||
|
|
||||||
WaveInfo getWaveInfo(i32 idx);
|
WaveInfo getWaveInfo(i32 idx);
|
||||||
|
|
||||||
void updateWave(WaveInfo *wave, f32 dt);
|
void updateWave(WaveInfo *wave, Game *game, f32 dt);
|
||||||
|
|
||||||
bool isWaveSendingOver(const WaveInfo *wave);
|
bool isWaveSendingOver(const WaveInfo *wave);
|
||||||
bool isWaveOver(const WaveInfo *wave);
|
bool isWaveOver(const WaveInfo *wave);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="80" height="50" tilewidth="16" tileheight="16" infinite="0" nextlayerid="10" nextobjectid="20">
|
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="80" height="50" tilewidth="16" tileheight="16" infinite="0" nextlayerid="10" nextobjectid="21">
|
||||||
<editorsettings>
|
<editorsettings>
|
||||||
<export target="maps/map_01.tmj" format="json"/>
|
<export target="../assets/maps/map_01.tmj" format="json"/>
|
||||||
</editorsettings>
|
</editorsettings>
|
||||||
<tileset firstgid="1" source="game.tsx"/>
|
<tileset firstgid="1" source="game.tsx"/>
|
||||||
<layer id="1" name="terrain" width="80" height="50">
|
<layer id="1" name="terrain" width="80" height="50">
|
||||||
@@ -393,12 +393,15 @@
|
|||||||
<object id="1" name="camera" x="1119" y="571.5">
|
<object id="1" name="camera" x="1119" y="571.5">
|
||||||
<point/>
|
<point/>
|
||||||
</object>
|
</object>
|
||||||
<object id="8" name="spawn" x="91.3333" y="665.333">
|
<object id="8" name="spawn" x="32.0606" y="768.242">
|
||||||
<point/>
|
<point/>
|
||||||
</object>
|
</object>
|
||||||
<object id="9" name="p1" x="150" y="510">
|
<object id="9" name="p1" x="150" y="510">
|
||||||
<point/>
|
<point/>
|
||||||
</object>
|
</object>
|
||||||
|
<object id="20" name="p0" x="97.5" y="644.5">
|
||||||
|
<point/>
|
||||||
|
</object>
|
||||||
<object id="10" name="p2" x="226" y="353.333">
|
<object id="10" name="p2" x="226" y="353.333">
|
||||||
<point/>
|
<point/>
|
||||||
</object>
|
</object>
|
||||||
|
|||||||
Reference in New Issue
Block a user