Entity collisions, gravity, & velocity.

- Refactor Collision, fix deltatime bugs.
- Apply gravity to applicable entities.
- Add callbacks to ItemDef, unused so far.
- Reorganize and document LocalPlayer class.
- Give bees, rabbits, and test entities AI that is *so cute guys seriously look at it*.
- Plains biomes now have flowers.
master
Auri 2020-12-05 21:17:46 -08:00
parent f4ec97370d
commit 52ae370040
28 changed files with 676 additions and 476 deletions

View File

@ -1,7 +1,3 @@
//
// Created by aurailus on 10/06/19.
//
#include "BlockDef.h"
void BlockDef::createModel() {
@ -35,5 +31,5 @@ void BlockDef::createModel() {
}
bool BlockDef::hasInteraction() {
return callbacks.count(Callback::INTERACT) || callbacks.count(Callback::INTERACT_CLIENT);
return callbacks.count(BlockDef::Callback::INTERACT) || callbacks.count(BlockDef::Callback::INTERACT_CLIENT);
}

View File

@ -1,7 +1,3 @@
//
// Created by aurailus on 10/06/19.
//
#pragma once
#include <string>
@ -10,12 +6,30 @@
#include "lua/Lua.h"
#include "util/Util.h"
#include "lua/Callback.h"
#include "game/def/mesh/BlockModel.h"
#include "game/def/mesh/SelectionBox.h"
/**
* Represents a block definition, defined by Lua.
* Contains all the necessary information about a block.
*/
class BlockDef : public ItemDef {
public:
public:
enum class Callback {
CONSTRUCT, AFTER_CONSTRUCT,
DESTRUCT, AFTER_DESTRUCT,
PLACE, PLACE_CLIENT,
AFTER_PLACE, AFTER_PLACE_CLIENT,
BREAK, BREAK_CLIENT,
AFTER_BREAK, AFTER_BREAK_CLIENT,
INTERACT, INTERACT_CLIENT,
HIT, HIT_CLIENT
};
BlockDef() : ItemDef{ "", "", 0, 0, ItemDef::Type::BLOCK } {};
void createModel();
@ -37,5 +51,5 @@ class BlockDef : public ItemDef {
std::vector<SelectionBox> sBoxes{};
std::vector<SelectionBox> cBoxes{};
std::unordered_map<Callback, sol::protected_function, Util::EnumClassHash> callbacks{};
std::unordered_map<Callback, sol::protected_function, Util::EnumClassHash> callbacks {};
};

View File

@ -1,20 +1,26 @@
//
// Created by aurailus on 01/10/19.
//
#pragma once
#include <vector>
#include "lua/Lua.h"
#include "ItemDef.h"
class AtlasRef;
class TextureAtlas;
/**
* Represents an item definition, defined by Lua.
* Contains all the necessary information about a item.
*/
class CraftItemDef : public ItemDef {
public:
CraftItemDef() = default;
public:
enum class Callback {
USE, USE_CLIENT,
HIT, HIT_CLIENT
};
CraftItemDef() : ItemDef{ "", "", 0, 0, ItemDef::Type::CRAFTITEM } {};
CraftItemDef(const std::string& identifier, const std::string& name, unsigned short maxStackSize,
const std::vector<std::string>& textures, const std::vector<std::shared_ptr<AtlasRef>>& textureRefs);
@ -25,6 +31,8 @@ class CraftItemDef : public ItemDef {
void createModel(TextureAtlas& atlas);
std::vector<std::string> textures{};
std::vector<std::shared_ptr<AtlasRef>> textureRefs{};
std::vector<std::string> textures {};
std::vector<std::shared_ptr<AtlasRef>> textureRefs {};
std::unordered_map<Callback, sol::protected_function, Util::EnumClassHash> callbacks {};
};

View File

@ -1,15 +1,16 @@
//
// Created by aurailus on 11/08/19.
//
#pragma once
#include <string>
#include "client/graph/Model.h"
/**
* Base definition class, contains properties that all definition types use.
* Inherited by BlockDef and CraftItemDef, which have specialized properties.
*/
class ItemDef {
public:
public:
ItemDef(const ItemDef& o) = delete;
enum class Type {

View File

@ -4,28 +4,3 @@
#pragma once
enum class Callback {
CONSTRUCT,
AFTER_CONSTRUCT,
DESTRUCT,
AFTER_DESTRUCT,
PLACE,
PLACE_CLIENT,
AFTER_PLACE,
AFTER_PLACE_CLIENT,
BREAK,
BREAK_CLIENT,
AFTER_BREAK,
AFTER_BREAK_CLIENT,
INTERACT,
INTERACT_CLIENT,
HIT,
HIT_CLIENT
};

View File

@ -1,4 +1,3 @@
#include "CreateRegister.h"
#include "lua/Lua.h"
@ -36,19 +35,6 @@ namespace {
}
}
/**
* Create a new register_* function in the core namespace and an associated registered_* table.
* The passed in function will be called after a new element has been inserted into the table using the
* generated function.
*
* @param lua - The Sol Lua State.
* @param core - The core table, i.e. `_G['zepha']`.
* @param name - The name of the element being registered. The name of the generated function will be 'register_[name]'.
* @param after - The function to run every time an element is registered.
* @param table - Optional, overrides the default table name and changes it to 'register_[table]'.
*/
void Api::Util::createRegister(sol::state& lua, sol::table& core,
const std::string& name, std::function<void(std::string)> after, const std::string& table) {

View File

@ -1,9 +1,3 @@
/*
* A Utility Function that creates register functions for Lua.
*
* - Auri, 03/11/20
*/
#pragma once
#include <string>
@ -11,6 +5,19 @@
#include <sol/forward.hpp>
namespace Api::Util {
/**
* Create a new register_* function in the core namespace and an associated registered_* table.
* The passed in function will be called after a new element has been inserted into the table using the
* generated function.
*
* @param lua - The Sol Lua State.
* @param core - The core table, i.e. `_G['zepha']`.
* @param name - The name of the element being registered. The name of the generated function will be 'register_[name]'.
* @param after - The function to run every time an element is registered.
* @param table - Optional, overrides the default table name and changes it to 'register_[table]'.
*/
void createRegister(sol::state& lua, sol::table& core, const std::string& name,
std::function<void(std::string)> after = nullptr, const std::string& table = "");
}

View File

@ -1,7 +1,3 @@
//
// Created by aurailus on 2020-01-10.
//
#pragma once
#include <libnoise/module/add.h>

View File

@ -1,11 +1,6 @@
//
// Created by aurailus on 2020-01-10.
//
#pragma once
#include "lua/Lua.h"
#include "lua/Callback.h"
#include "game/def/ItemDef.h"
#include "game/LocalSubgame.h"
#include "game/def/BiomeDef.h"
@ -298,9 +293,9 @@ namespace RegisterBlock {
*/
static void
addCallback(BlockDef* blockDef, sol::table& blockTable, const std::string& name, Callback cbType) {
addCallback(BlockDef& blockDef, sol::table& blockTable, const std::string& name, BlockDef::Callback cbType) {
auto cb = blockTable.get<sol::optional<sol::protected_function>>(name);
if (cb) blockDef->callbacks.insert({ cbType, *cb });
if (cb) blockDef.callbacks.insert({ cbType, *cb });
}
@ -392,26 +387,26 @@ namespace RegisterBlock {
if (atlas) def->createModel();
// Bind Callbacks
addCallback(def, blockTable, "on_construct", Callback::CONSTRUCT);
addCallback(def, blockTable, "after_construct", Callback::AFTER_CONSTRUCT);
addCallback(*def, blockTable, "on_construct", BlockDef::Callback::CONSTRUCT);
addCallback(*def, blockTable, "after_construct", BlockDef::Callback::AFTER_CONSTRUCT);
addCallback(def, blockTable, "on_destruct", Callback::DESTRUCT);
addCallback(def, blockTable, "after_destruct", Callback::AFTER_DESTRUCT);
addCallback(*def, blockTable, "on_destruct", BlockDef::Callback::DESTRUCT);
addCallback(*def, blockTable, "after_destruct", BlockDef::Callback::AFTER_DESTRUCT);
addCallback(def, blockTable, "on_place", Callback::PLACE);
addCallback(def, blockTable, "on_place_client", Callback::PLACE_CLIENT);
addCallback(*def, blockTable, "on_place", BlockDef::Callback::PLACE);
addCallback(*def, blockTable, "on_place_client", BlockDef::Callback::PLACE_CLIENT);
addCallback(def, blockTable, "after_place", Callback::AFTER_PLACE);
addCallback(def, blockTable, "after_place_client", Callback::AFTER_PLACE_CLIENT);
addCallback(*def, blockTable, "after_place", BlockDef::Callback::AFTER_PLACE);
addCallback(*def, blockTable, "after_place_client", BlockDef::Callback::AFTER_PLACE_CLIENT);
addCallback(def, blockTable, "on_break", Callback::BREAK);
addCallback(def, blockTable, "on_break_client", Callback::BREAK_CLIENT);
addCallback(*def, blockTable, "on_break", BlockDef::Callback::BREAK);
addCallback(*def, blockTable, "on_break_client", BlockDef::Callback::BREAK_CLIENT);
addCallback(def, blockTable, "after_break", Callback::AFTER_BREAK);
addCallback(def, blockTable, "after_break_client", Callback::AFTER_BREAK_CLIENT);
addCallback(*def, blockTable, "after_break", BlockDef::Callback::AFTER_BREAK);
addCallback(*def, blockTable, "after_break_client", BlockDef::Callback::AFTER_BREAK_CLIENT);
addCallback(def, blockTable, "on_interact", Callback::INTERACT);
addCallback(def, blockTable, "on_interact_client", Callback::INTERACT_CLIENT);
addCallback(*def, blockTable, "on_interact", BlockDef::Callback::INTERACT);
addCallback(*def, blockTable, "on_interact_client", BlockDef::Callback::INTERACT_CLIENT);
// Add Block Definition to the Atlas
defs.registerDef(def);

View File

@ -1,7 +1,3 @@
//
// Created by aurailus on 2020-01-10.
//
#pragma once
#include "lua/Lua.h"
@ -15,6 +11,25 @@
namespace RegisterItem {
namespace {
/**
* Attempts to add a callback of the type specified from the block table provided
* to the block definition provided. Does nothing if the block table doesn't have a callback of said type.
*
* @param blockDef - The block definition to add the callback to.
* @param blockTable - The lua block table to get the callback from.
* @param name - The name of the callback to look for in the lua table.
* @param cbType - The type to register the callback as.
*/
static void addCallback(CraftItemDef& itemDef, sol::table& itemTable,
const std::string& name, CraftItemDef::Callback cbType) {
auto cb = itemTable.get<sol::optional<sol::protected_function>>(name);
if (cb) itemDef.callbacks.insert({ cbType, *cb });
}
/**
* Registers an item from the items table to the Definition Atlas.
* Generic method that works on both the client and the server, depending on if `atlas` is non-null.
@ -52,7 +67,7 @@ namespace RegisterItem {
}
// Creat the definition
CraftItemDef* itemDef = new CraftItemDef(
CraftItemDef* def = new CraftItemDef(
identifier,
defs.size(),
*nameOpt,
@ -61,8 +76,16 @@ namespace RegisterItem {
textureRefs
);
if (atlas) itemDef->createModel(*atlas);
defs.registerDef(itemDef);
// Bind Callbacks
addCallback(*def, itemTable, "on_use", CraftItemDef::Callback::USE);
addCallback(*def, itemTable, "on_use_client", CraftItemDef::Callback::USE_CLIENT);
addCallback(*def, itemTable, "on_hit", CraftItemDef::Callback::HIT);
addCallback(*def, itemTable, "on_hit_client", CraftItemDef::Callback::HIT_CLIENT);
// Add Item Definition to the Atlas
if (atlas) def->createModel(*atlas);
defs.registerDef(def);
}
}

View File

@ -1,7 +1,3 @@
//
// Created by aurailus on 2020-01-10.
//
#pragma once
#include "lua/Lua.h"

View File

@ -12,7 +12,7 @@ class Target {
public:
/** A struct containing data for a targeted entity. */
struct EntityData { unsigned int id; };
struct EntityData { long long id; };
/** A struct containing data for a targeted block & face. */
struct BlockData { glm::ivec3 pos; EVec face; };
/** A struct containing no data for targets pointing to nothing. */

View File

@ -63,7 +63,7 @@ bool Dimension::setBlock(glm::ivec3 pos, unsigned int block) {
//}
std::unordered_set<glm::ivec3, Vec::ivec3> Dimension::propogateAddNodes() {
std::unordered_set<glm::ivec3, Vec::ivec3> chunksUpdated{};
std::unordered_set<glm::ivec3, Vec::ivec3> chunksUpdated {};
for (unsigned int channel = 0; channel < 4; channel++) {
while (true) {
@ -111,7 +111,7 @@ std::unordered_set<glm::ivec3, Vec::ivec3> Dimension::propogateAddNodes() {
}
std::unordered_set<glm::ivec3, Vec::ivec3> Dimension::propogateRemoveNodes() {
std::unordered_set<glm::ivec3, Vec::ivec3> chunksUpdated{};
std::unordered_set<glm::ivec3, Vec::ivec3> chunksUpdated {};
for (unsigned int channel = 0; channel < 4; channel++) {
while (true) {
@ -141,8 +141,8 @@ std::unordered_set<glm::ivec3, Vec::ivec3> Dimension::propogateRemoveNodes() {
if (checkLight != 0 &&
(checkLight < node.value || (channel == SUNLIGHT_CHANNEL && i.y == -1 && node.value == 15))) {
unsigned int replaceLight = (channel == SUNLIGHT_CHANNEL ? 0 :
game->getDefs().blockFromId(
chunk->getBlock(Space::Block::index(check))).lightSource[channel]);
game->getDefs().blockFromId(
chunk->getBlock(Space::Block::index(check))).lightSource[channel]);
chunk->setLight(ind, channel, replaceLight);
l.lock();
@ -176,7 +176,7 @@ bool Dimension::containsWorldPos(Chunk* chunk, glm::ivec3 pos) {
glm::ivec4 Dimension::getLight(glm::ivec3 worldPos, Chunk* chunk) {
if (containsWorldPos(chunk, worldPos)) return chunk->getLight(Space::Block::index(worldPos));
auto oChunk = getChunk(Space::Chunk::world::fromBlock(worldPos)).get();
return (oChunk ? oChunk->getLight(Space::Block::index(worldPos)) : glm::ivec4{});
return (oChunk ? oChunk->getLight(Space::Block::index(worldPos)) : glm::ivec4 {});
}
void Dimension::calculateHorizontalEdge(std::shared_ptr<Chunk> a, std::shared_ptr<Chunk> b) {
@ -206,7 +206,7 @@ void Dimension::calculateVerticalEdge(std::shared_ptr<Chunk> above, std::shared_
auto lightAbove = above->getLight(Space::Block::index({ xx, 0, zz }), 3);
auto lightBelow = below->getLight(Space::Block::index({ xx, 15, zz }), 3);
if (lightBelow > lightAbove) removeSunlight(below->getPos() * 16 + glm::ivec3{ xx, 15, zz });
if (lightBelow > lightAbove) removeSunlight(below->getPos() * 16 + glm::ivec3 { xx, 15, zz });
}
}
@ -240,8 +240,8 @@ void Dimension::removeBlockLight(glm::ivec3 pos) {
}
void Dimension::reflowLight(glm::ivec3 pos) {
glm::ivec4 placeLight{};
const static std::array<glm::ivec3, 6> checks = { glm::ivec3{ -1, 0, 0 }, { 1, 0, 0 }, { 0, -1, 0 }, { 0, 1, 0 },
glm::ivec4 placeLight {};
const static std::array<glm::ivec3, 6> checks = { glm::ivec3 { -1, 0, 0 }, { 1, 0, 0 }, { 0, -1, 0 }, { 0, 1, 0 },
{ 0, 0, -1 }, { 0, 0, 1 }};
auto chunk = getChunk(Space::Chunk::world::fromBlock(pos));

View File

@ -1,9 +1,3 @@
//
// Created by aurailus on 2020-07-30.
//
#include <algorithm>
#include "Collision.h"
#include "game/Subgame.h"
@ -12,97 +6,42 @@
#include "game/atlas/DefinitionAtlas.h"
#include "game/def/mesh/SelectionBox.h"
bool Collision::isOnGround(SubgamePtr game, DimensionPtr dim, SelectionBox& collision, glm::vec3 pos, glm::vec3 vel) {
pos.y -= 0.05f;
return collidesAt(game, dim, collision, pos) && vel.y <= 0;
bool Collision::isOnGround(SubgamePtr game, DimensionPtr dim,
const SelectionBox& collisionBox, glm::vec3 pos, glm::vec3 vel) {
constexpr static double INC = 0.05;
pos.y -= INC;
return vel.y <= 0 && collidesAt(game, dim, collisionBox, pos);
}
void Collision::moveCollide(SubgamePtr game, DimensionPtr dim, SelectionBox& collision, glm::vec3& pos, glm::vec3& vel,
float delta, float stepUpAmount) {
const static double increment = 0.05;
std::tuple<glm::vec3, glm::vec3> Collision::applyVel(SubgamePtr game, DimensionPtr dim,
const SelectionBox& collisionBox, glm::vec3 pos, glm::vec3 vel, float delta) {
constexpr static double INC = 0.05;
double moved = 0;
for (int i = 0; i < std::abs(vel.y * delta) / increment; i++) {
double move = std::max(std::min(increment, std::abs(vel.y * delta) - moved), 0.);
moved += increment;
glm::vec3 newPos = pos;
newPos.y += move * (vel.y < 0 ? -1 : 1);
if (!collidesAt(game, dim, collision, newPos)) pos = newPos;
else if (vel.y > 0) vel.y = 0;
}
moved = 0;
for (int i = 0; i < std::abs(vel.x * delta) / increment; i++) {
double move = std::max(std::min(increment, std::abs(vel.x * delta) - moved), 0.);
moved += increment;
glm::vec3 newPos = pos;
newPos.x += move * (vel.x < 0 ? -1 : 1);
if (!collidesAt(game, dim, collision, newPos, stepUpAmount)) pos = newPos;
else vel.x = 0;
}
moved = 0;
for (int i = 0; i < std::abs(vel.z * delta) / increment; i++) {
double move = std::max(std::min(increment, std::abs(vel.z * delta) - moved), 0.);
moved += increment;
glm::vec3 newPos = pos;
newPos.z += move * (vel.z < 0 ? -1 : 1);
if (!collidesAt(game, dim, collision, newPos, stepUpAmount)) pos = newPos;
else vel.z = 0;
}
}
bool
Collision::collidesAt(SubgamePtr game, DimensionPtr dim, SelectionBox& collision, glm::vec3& pos, float stepUpMax) {
// Find the minimum vertical increase needed to step up
float stepUpAmount = 0;
if (stepUpMax > 0) {
glm::vec3 offset{};
offset.x = collision.a.x;
while (true) {
offset.y = collision.a.y;
while (true) {
offset.z = collision.a.z;
while (true) {
glm::vec3 offsetPos = glm::floor(pos + offset);
auto& def = game->getDefs().blockFromId(dim->getBlock(offsetPos));
if (def.solid)
for (auto& cBox : def.cBoxes)
stepUpAmount = std::fmax(cBox.b.y + offsetPos.y - pos.y, stepUpAmount);
if (offset.z == collision.b.z) break;
offset.z = std::fmin(std::floor(offset.z + 1), collision.b.z);
}
if (offset.y == collision.a.y + stepUpMax + 0.025f) break; // Hack for precision errors
offset.y = std::fmin(std::floor(offset.y + 1), collision.a.y + stepUpMax + 0.025f);
}
if (offset.x == collision.b.x) break;
offset.x = std::fmin(std::floor(offset.x + 1), collision.b.x);
for (u_char i = 0; i < 3; i++) {
for (int j = 0; j < std::abs(vel[i] * delta) / INC; j++) {
double moveAmount = std::max(std::min(INC, std::abs(vel[i] * delta) - (j * INC)), 0.);
glm::vec3 newPos = pos;
newPos[i] += moveAmount * (vel[i] < 0 ? -1 : 1);
if (!collidesAt(game, dim, collisionBox, newPos)) pos = newPos;
else if (i != 1 || vel[i] > 0) vel[i] = 0;
}
}
// Step up if possible, or return false
if (stepUpAmount > stepUpMax) return true;
if (stepUpAmount > 0) pos.y += stepUpAmount + 0.025; // Hack for precision errors
return std::make_tuple(pos, vel);
}
bool Collision::collidesAt(SubgamePtr game, DimensionPtr dim, const SelectionBox& collisionBox, glm::vec3 pos) {
SelectionBox worldAlignedCollisionBox = { collisionBox.a + pos, collisionBox.b + pos };
glm::vec3 offset {};
SelectionBox collidableBox = { collision.a + pos, collision.b + pos };
glm::vec3 offset{};
// Regular collision check
offset.x = collision.a.x;
offset.x = collisionBox.a.x;
while (true) {
offset.y = collision.a.y;
offset.y = collisionBox.a.y;
while (true) {
offset.z = collision.a.z;
offset.z = collisionBox.a.z;
while (true) {
glm::vec3 offsetPos = glm::floor(pos + offset);
auto& def = game->getDefs().blockFromId(dim->getBlock(offsetPos));
@ -111,21 +50,21 @@ Collision::collidesAt(SubgamePtr game, DimensionPtr dim, SelectionBox& collision
for (auto& cBox : def.cBoxes) {
SelectionBox blockBox = { cBox.a + offsetPos, cBox.b + offsetPos };
if ((blockBox.a.x <= collidableBox.b.x && blockBox.b.x >= collidableBox.a.x) &&
(blockBox.a.y <= collidableBox.b.y && blockBox.b.y >= collidableBox.a.y) &&
(blockBox.a.z <= collidableBox.b.z && blockBox.b.z >= collidableBox.a.z))
if ((blockBox.a.x <= worldAlignedCollisionBox.b.x && blockBox.b.x >= worldAlignedCollisionBox.a.x) &&
(blockBox.a.y <= worldAlignedCollisionBox.b.y && blockBox.b.y >= worldAlignedCollisionBox.a.y) &&
(blockBox.a.z <= worldAlignedCollisionBox.b.z && blockBox.b.z >= worldAlignedCollisionBox.a.z))
return true;
}
}
if (offset.z == collision.b.z) break;
offset.z = (std::min)(std::floor(offset.z + 1), collision.b.z);
if (offset.z == collisionBox.b.z) break;
offset.z = (std::min)(std::floor(offset.z + 1), collisionBox.b.z);
}
if (offset.y == collision.b.y) break;
offset.y = (std::min)(std::floor(offset.y + 1), collision.b.y);
if (offset.y == collisionBox.b.y) break;
offset.y = (std::min)(std::floor(offset.y + 1), collisionBox.b.y);
}
if (offset.x == collision.b.x) break;
offset.x = (std::min)(std::floor(offset.x + 1), collision.b.x);
if (offset.x == collisionBox.b.x) break;
offset.x = (std::min)(std::floor(offset.x + 1), collisionBox.b.x);
}
return false;

View File

@ -1,7 +1,3 @@
//
// Created by aurailus on 2020-07-30.
//
#pragma once
#include <glm/vec3.hpp>
@ -9,18 +5,54 @@
#include "util/CovariantPtr.h"
class Subgame;
class Dimension;
class SelectionBox;
class Collision {
public:
static bool isOnGround(SubgamePtr game, DimensionPtr dim, SelectionBox& collision, glm::vec3 pos, glm::vec3 vel);
static void moveCollide(SubgamePtr game, DimensionPtr dim, SelectionBox& collision, glm::vec3& pos, glm::vec3& vel,
float delta, float stepUpAmount = 0);
/**
* Checks if a collision box is "on the ground", meaning it is
* a suitably small distance away from the nearest bottom collision.
* The velocity is also tested, and the ground-check will only succeed if the y velocity is negative.
*
* @param game - A reference to the subgame.
* @param dim - A reference to the dimension to test in.
* @param collisionBox - The collision box to test.
* @param pos - The position of the collision box.
* @param vel - The velocity of the collision box.
* @returns a boolean indicating if the collision box is on the ground.
*/
static bool isOnGround(SubgamePtr game, DimensionPtr dim,
const SelectionBox& collisionBox, glm::vec3 pos, glm::vec3 vel);
static bool
collidesAt(SubgamePtr game, DimensionPtr dim, SelectionBox& collision, glm::vec3& pos, float stepUpMax = 0);
/**
* Attempts to move a collision box in the world according to its
* velocity, not allowing movement into solid blocks.
* Returns the new position and velocity in a tuple.
*
* @param game - A reference to the subgame.
* @param dim - A reference to the dimension to act upon.
* @param collisionBox - The collision box to attempt to move.
* @param pos - The initial position of the collision box.
* @param vel - The velocity to apply to the collision box.
* @param delta - The delta time since last frame, used in velocity calculations.
* @returns a tuple whose first value is the new positon, and the second is the new velocity.
*/
static std::tuple<glm::vec3, glm::vec3> applyVel(SubgamePtr game, DimensionPtr dim,
const SelectionBox& collisionBox, glm::vec3 pos, glm::vec3 vel, float delta);
/**
* Checks if a collision box collides at a specified position.
*
* @param game - A reference to the subgame.
* @param dim - A reference to the dimension to test in.
* @param collisionBox - The collision box to test.
* @param pos - The position to test the collision box at.
* @returns a boolean indicating if the collision box collides at the specified position.
*/
static bool collidesAt(SubgamePtr game, DimensionPtr dim, const SelectionBox& collisionBox, glm::vec3 pos);
};

View File

@ -118,15 +118,23 @@ SubgamePtr Entity::getGame() const {
}
void Entity::update(double delta) {
static constexpr float TERMINAL_VELOCITY = 150.f;
animation.update(delta);
if (vel.length() != 0) {
if (collides) {
Collision::moveCollide(game, dim, *collisionBox, pos, vel,
Collision::isOnGround(game, dim, *collisionBox, pos, vel) ? 0.6 : vel.y <= 0 ? 0.1 : 0);
}
else {
setPos(getPos() + vel * static_cast<float>(delta));
glm::vec3 newPos, newVel;
std::tie(newPos, newVel) = Collision::applyVel(game, dim, *collisionBox, pos, vel, delta);
setPos(newPos);
setVel(newVel);
}
else setPos(getPos() + vel * static_cast<float>(delta));
}
if (gravity) {
if (!Collision::isOnGround(game, dim, *collisionBox, pos, vel))
setVel({ vel.x, std::fmax(vel.y - (gravity * delta), -TERMINAL_VELOCITY), vel.z });
else if (vel.y < 0) setVel({ vel.x, 0, vel.z });
}
}

View File

@ -44,19 +44,19 @@ ItemStack InventoryList::placeStack(unsigned short i, const ItemStack& stack, bo
unsigned short allowedTake = otherStack.count;
// if (playerInitiated) {
// auto allowTake = luaCallbacks[static_cast<int>(Callback::ALLOW_TAKE)];
// auto allowTake = luaCallbacks[static_cast<int>(BlockDef::Callback::ALLOW_TAKE)];
// if (allowTake) allowedTake = std::min(static_cast<unsigned short>(
// allowTake(i+1, LuaItemStack(otherStack, defs))), allowedTake);
// }
unsigned short allowedPut = stack.count;
// if (playerInitiated) {
// auto allowPut = luaCallbacks[static_cast<int>(Callback::ALLOW_PUT)];
// auto allowPut = luaCallbacks[static_cast<int>(BlockDef::Callback::ALLOW_PUT)];
// if (allowPut) allowedPut = std::min(static_cast<unsigned short>(
// allowPut(i+1, LuaItemStack(stack, defs))), allowedPut);
// }
// sol::function on_put = luaCallbacks[static_cast<int>(Callback::ON_PUT)];
// sol::function on_take = luaCallbacks[static_cast<int>(Callback::ON_TAKE)];
// sol::function on_put = luaCallbacks[static_cast<int>(BlockDef::Callback::ON_PUT)];
// sol::function on_take = luaCallbacks[static_cast<int>(BlockDef::Callback::ON_TAKE)];
if (stack.count == 0) {
if (allowedTake == otherStack.count) setStack(i, { DefinitionAtlas::AIR, 0 });
@ -111,7 +111,7 @@ ItemStack InventoryList::splitStack(unsigned short i, bool playerInitiated) {
unsigned short allowedTake = stack.count;
// if (playerInitiated) {
// auto allowTake = luaCallbacks[static_cast<int>(Callback::ALLOW_TAKE)];
// auto allowTake = luaCallbacks[static_cast<int>(BlockDef::Callback::ALLOW_TAKE)];
// if (allowTake) allowedTake = std::min(static_cast<unsigned short>(
// allowTake(i + 1, LuaItemStack(stack, defs))), allowedTake);
// }
@ -120,7 +120,7 @@ ItemStack InventoryList::splitStack(unsigned short i, bool playerInitiated) {
unsigned short takeCount = std::min(static_cast<unsigned short>(ceil(initialCount / 2.f)), allowedTake);
setStack(i, { stack.id, static_cast<unsigned short>(initialCount - takeCount) });
// sol::function on_take = luaCallbacks[static_cast<int>(Callback::ON_TAKE)];
// sol::function on_take = luaCallbacks[static_cast<int>(BlockDef::Callback::ON_TAKE)];
// if (on_take) on_take(i+1, stack);
return { stack.id, takeCount };
}

View File

@ -31,86 +31,27 @@ void LocalPlayer::update(Input& input, double delta, glm::vec2 mouseDelta) {
updatePhysics(input, delta, mouseDelta);
Collision::moveCollide(game, dim, *collisionBox, pos, vel, delta,
Collision::isOnGround(game, dim, *collisionBox, pos, vel) ? 0.6 : vel.y <= 0 ? 0.1 : 0);
glm::vec3 newPos, newVel;
std::tie(newPos, newVel) = Collision::applyVel(game, dim, *collisionBox, pos, vel, delta);
setPos(newPos);
setVel(newVel);
updateCamera();
findPointedThing(input);
findTarget(input);
updateWireframe();
if (!gameGui.isInMenu()) interact(input, delta);
}
void LocalPlayer::assertField(Packet packet) {
packet.type = Packet::Type::THIS_PLAYER_INFO;
static_cast<LocalWorld&>(world).getNet().sendPacket(packet, Packet::Channel::INTERACT);
}
void LocalPlayer::handleAssertion(Deserializer& d) {
while (!d.atEnd()) {
const auto field = d.readE<NetField>();
switch (field) {
default:
std::cout << Log::err << "Player received unhandled NetField, Type "
<< static_cast<int>(field) << "." << Log::endl;
break;
case NetField::ID:
setId(d.read<long long>());
break;
case NetField::POS:
setPos(d.read<glm::vec3>());
break;
case NetField::VEL:
setVel(d.read<glm::vec3>());
break;
case NetField::DIM:
setDim(world.getDimension(d.read<unsigned int>()));
break;
case NetField::LOOK_PITCH:
setPitch(d.read<float>());
break;
case NetField::LOOK_YAW:
setYaw(d.read<float>());
break;
case NetField::FLYING:
setFlying(d.read<bool>());
break;
case NetField::HAND_INV:
setHandList(d.read<std::string>());
break;
case NetField::WIELD_INV:
setWieldList(d.read<std::string>());
break;
case NetField::WIELD_INDEX:
setWieldIndex(d.read<unsigned short>());
break;
}
}
}
//
// Overridden Setters
//
void LocalPlayer::setPos(glm::vec3 pos, bool assert) {
Player::setPos(pos, assert);
this->renderer.camera.setPos(pos + getLookOffset());
renderer.camera.setPos(pos + getLookOffset());
}
void LocalPlayer::setLookOffset(glm::vec3 eyeOffset, bool assert) {
Player::setLookOffset(eyeOffset, assert);
this->renderer.camera.setPos(pos + getLookOffset());
renderer.camera.setPos(pos + getLookOffset());
}
@ -139,9 +80,13 @@ void LocalPlayer::setDim(DimensionPtr dim) {
static_cast<LocalWorld&>(world).setActiveDimension(dim);
}
//
// UI Related
//
Target& LocalPlayer::getTarget() {
return target;
}
InventoryPtr LocalPlayer::getInventory() {
return dim->getWorld().getRefs()->getInventory("current_player");
}
bool LocalPlayer::isInMenu() {
return gameGui.isInMenu();
@ -157,34 +102,18 @@ void LocalPlayer::closeMenu() {
renderer.window.input.lockMouse(true);
}
void LocalPlayer::setHud(std::shared_ptr<LuaGuiElement> hud) {
gameGui.setHud(hud);
}
std::shared_ptr<LuaGuiElement> LocalPlayer::getHud() {
return gameGui.getHud();
}
void LocalPlayer::setHud(std::shared_ptr<LuaGuiElement> hud) {
gameGui.setHud(hud);
}
void LocalPlayer::setHudVisible(bool hudVisible) {
gameGui.setVisible(hudVisible);
}
//
// Misc Getters
//
InventoryPtr LocalPlayer::getInventory() {
return dim->getWorld().getRefs()->getInventory("current_player");
}
Target& LocalPlayer::getTarget() {
return target;
}
//
// Draw Functions
//
void LocalPlayer::draw(Renderer&) {
wireframe.draw(renderer);
handItemModel.draw(renderer);
@ -198,9 +127,62 @@ void LocalPlayer::drawMenu(Renderer&) {
gameGui.drawMenu(renderer);
}
//
// Physics, camera, and player specific functionality.
//
void LocalPlayer::assertField(Packet packet) {
packet.type = Packet::Type::THIS_PLAYER_INFO;
static_cast<LocalWorld&>(world).getNet().sendPacket(packet, Packet::Channel::INTERACT);
}
void LocalPlayer::handleAssertion(Deserializer& d) {
while (!d.atEnd()) {
const auto field = d.readE<NetField>();
switch (field) {
default:
std::cout << Log::err << "Player received unhandled NetField, Type "
<< static_cast<int>(field) << "." << Log::endl;
break;
case NetField::ID:
setId(d.read<long long>());
break;
case NetField::POS:
setPos(d.read<glm::vec3>());
break;
case NetField::VEL:
setVel(d.read<glm::vec3>());
break;
case NetField::DIM:
setDim(world.getDimension(d.read<unsigned int>()));
break;
case NetField::LOOK_PITCH:
setPitch(d.read<float>());
break;
case NetField::LOOK_YAW:
setYaw(d.read<float>());
break;
case NetField::FLYING:
setFlying(d.read<bool>());
break;
case NetField::HAND_INV:
setHandList(d.read<std::string>());
break;
case NetField::WIELD_INV:
setWieldList(d.read<std::string>());
break;
case NetField::WIELD_INDEX:
setWieldIndex(d.read<unsigned short>());
break;
}
}
}
bool LocalPlayer::getKey(Input& input, LocalPlayer::PlayerControl control) {
if (gameGui.isInMenu()) return false;
@ -222,7 +204,7 @@ void LocalPlayer::updatePhysics(Input& input, double delta, glm::vec2 mouseDelta
static constexpr float MOUSE_SENSITIVITY = 0.1f;
//Position movement
// Position movement
bool sprinting = getKey(input, PlayerControl::MOD2);
double moveSpeed = MOVE_VEL * (sprinting ? 1.6 : 1);
@ -233,10 +215,10 @@ void LocalPlayer::updatePhysics(Input& input, double delta, glm::vec2 mouseDelta
friction = 0.15f;
}
else if (getKey(input, PlayerControl::JUMP) &&
Collision::isOnGround(game, dim, *collisionBox, pos, vel))
Collision::isOnGround(game, dim, *collisionBox, pos, vel))
vel.y = JUMP_VEL;
//Calculate movement vector from camera angle.
// Calculate movement vector from camera angle.
auto& camera = renderer.camera;
glm::vec3 frontFlat = glm::normalize(glm::vec3(camera.getFront().x, 0, camera.getFront().z));
glm::vec3 rightFlat = glm::normalize(glm::vec3(camera.getRight().x, 0, camera.getRight().z));
@ -263,18 +245,18 @@ void LocalPlayer::updatePhysics(Input& input, double delta, glm::vec2 mouseDelta
if (!flying) {
glm::vec3 velFlat = { vel.x, 0, vel.z };
//Add movement vector with friction.
// Add movement vector with friction.
velFlat = velFlat * (1.0f - friction) + mod * friction;
vel.x = velFlat.x;
vel.z = velFlat.z;
}
else {
//If flying factor in vertical mod values.
// If flying factor in vertical mod values.
vel = vel * (1.0f - friction) + mod * friction;
}
//View movement
// View movement
mouseDelta.x *= MOUSE_SENSITIVITY;
mouseDelta.y *= MOUSE_SENSITIVITY;
@ -326,7 +308,44 @@ void LocalPlayer::updateCamera() {
handItemModel.setScale((type == ItemDef::Type::CRAFTITEM ? 0.2f : 0.12f));
}
void LocalPlayer::findPointedThing(Input& input) {
void LocalPlayer::updateWireframe() {
if (gameGui.isVisible() && target.type != Target::Type::NOTHING) {
std::vector<SelectionBox> boxes {};
glm::vec3 thicknessOffset {};
glm::vec3 renderPos {};
if (target.type == Target::Type::BLOCK) {
boxes = game->getDefs().blockFromId(dim->getBlock(target.data.block.pos)).sBoxes;
renderPos = target.data.block.pos;
thicknessOffset = glm::vec3(0.5);
}
else {
const auto& entity = **dim.l()->getEntityById(target.data.entity.id).entity;
boxes.push_back(*entity.getCollisionBox());
renderPos = entity.getPos();
}
float distance = glm::distance(pos, renderPos + thicknessOffset);
wireframe.updateMesh(boxes, 0.002f + distance * 0.0014f);
wireframe.setPos(renderPos);
wireframe.setVisible(true);
}
else wireframe.setVisible(false);
}
void LocalPlayer::updateWieldAndHandItems() {
auto handList = world.getRefs().l()->getHandList();
auto wieldList = world.getRefs().l()->getWieldList();
handItem = handList && handList->getLength() > 0 ? handList->getStack(0).id : 0;
wieldItem = wieldList && wieldList->getLength() > wieldIndex ? wieldList->getStack(wieldIndex).id : 0;
auto& model = game->getDefs().fromId(wieldItem <= DefinitionAtlas::AIR ? handItem : wieldItem).entityModel;
handItemModel.setModel(model);
}
void LocalPlayer::findTarget(Input& input) {
static constexpr float LOOK_DISTANCE = 6.5f;
static constexpr float LOOK_PRECISION = 0.01f;
@ -380,32 +399,6 @@ void LocalPlayer::findPointedThing(Input& input) {
target = newTarget;
}
void LocalPlayer::updateWireframe() {
if (gameGui.isVisible() && target.type != Target::Type::NOTHING) {
std::vector<SelectionBox> boxes {};
glm::vec3 thicknessOffset {};
glm::vec3 renderPos {};
if (target.type == Target::Type::BLOCK) {
boxes = game->getDefs().blockFromId(dim->getBlock(target.data.block.pos)).sBoxes;
renderPos = target.data.block.pos;
thicknessOffset = glm::vec3(0.5);
}
else {
const auto& entity = **dim.l()->getEntityById(target.data.entity.id).entity;
boxes.push_back(*entity.getCollisionBox());
renderPos = entity.getPos();
}
float distance = glm::distance(pos, renderPos + thicknessOffset);
wireframe.updateMesh(boxes, 0.002f + distance * 0.0014f);
wireframe.setPos(renderPos);
wireframe.setVisible(true);
}
else wireframe.setVisible(false);
}
void LocalPlayer::interact(Input& input, double delta) {
if (target.type == Target::Type::BLOCK) {
if (input.mouseDown(GLFW_MOUSE_BUTTON_LEFT) && breakTime == 0) {
@ -420,17 +413,6 @@ void LocalPlayer::interact(Input& input, double delta) {
if (breakTime > breakInterval) breakTime = 0;
}
void LocalPlayer::updateWieldAndHandItems() {
auto handList = world.getRefs().l()->getHandList();
auto wieldList = world.getRefs().l()->getWieldList();
handItem = handList && handList->getLength() > 0 ? handList->getStack(0).id : 0;
wieldItem = wieldList && wieldList->getLength() > wieldIndex ? wieldList->getStack(wieldIndex).id : 0;
auto& model = game->getDefs().fromId(wieldItem <= DefinitionAtlas::AIR ? handItem : wieldItem).entityModel;
handItemModel.setModel(model);
}
LocalPlayer::~LocalPlayer() {
renderer.window.removeResizeCallback("player");
}

View File

@ -1,7 +1,3 @@
//
// Created by aurailus on 28/12/18.
//
#pragma once
#include "Player.h"
@ -13,32 +9,44 @@
#include "client/entity/WireframeEntity.h"
class Input;
class Deserializer;
class LuaGuiElement;
class LocalInventory;
class LocalInventoryRefs;
enum class NetPlayerField;
/**
* The client-side local player. Manages the camera, and renders UI elements.
*/
class LocalPlayer : public virtual DrawableEntity, public Player {
public:
enum class PlayerControl {
FORWARD, LEFT, BACKWARD, RIGHT, JUMP, MOD1, MOD2
};
public:
/** Enum class for identifying player controls in a keyboard-agnostic manner. */
enum class PlayerControl { FORWARD, LEFT, BACKWARD, RIGHT, JUMP, MOD1, MOD2 };
/**
* Creates the local player, and initializes player UI.
*
* @param game - A reference to the subgame.
* @param world - A reference to the world.
* @param dim - A reference to player's current dimension.
* @param renderer - A reference to the renderer.
*/
LocalPlayer(SubgamePtr game, LocalWorld& world, DimensionPtr dim, Renderer& renderer);
/**
* Updates the player, processing inputs, physics,
* updating the camera, and finding / interacting with the target.
*
* @param input - The input manager, to pull keyboard events from.
* @param delta - The delta time since the last frame.
* @param mouseDelta - The mouse offset from the center of the screen.
*/
void update(Input& input, double delta, glm::vec2 mouseDelta);
virtual void assertField(Packet packet) override;
virtual void handleAssertion(Deserializer& d) override;
virtual InventoryPtr getInventory() override;
/** The following setters call Player setters, but updates some rendering-related information. */
virtual void setPos(glm::vec3 pos, bool assert = false) override;
@ -52,52 +60,179 @@ class LocalPlayer : public virtual DrawableEntity, public Player {
virtual void setDim(DimensionPtr dim) override;
bool isInMenu();
void showMenu(std::shared_ptr<LuaGuiElement> root);
void closeMenu();
void setHud(std::shared_ptr<LuaGuiElement> hud);
std::shared_ptr<LuaGuiElement> getHud();
void setHudVisible(bool hudVisible);
/**
* Gets the player's target.
* @returns the player's active target.
*/
Target& getTarget();
/**
* Gets the player's inventory.
* @returns a reference to the player's inventory.
*/
virtual InventoryPtr getInventory() override;
/**
* Checks if a player is currently in a menu.
* @returns if a player is currently in a menu.
*/
bool isInMenu();
/**
* Displays a GUI tree to the player, making it the actively open menu.
* @param root - The root GUI element to display.
*/
void showMenu(std::shared_ptr<LuaGuiElement> root);
/**
* Closes the currently open menu.
*/
void closeMenu();
/**
* Gets the player's HUD element.
* @returns the root GUI element of the hud.
*/
std::shared_ptr<LuaGuiElement> getHud();
/**
* Sets the HUD to the specified GUI tree. The hud does not constitute
* a menu and is always displayed unless the hud is hidden.
*
* @param hud - The root GUI element to display.
*/
void setHud(std::shared_ptr<LuaGuiElement> hud);
/**
* Sets the visibility state of the HUD.
* @param hudVisible - Whether or not the HUD should be visible.
*/
void setHudVisible(bool hudVisible);
/**
* Draws the player's target wireframe and held item.
* @param renderer - A reference to the renderer.
*/
void draw(Renderer& renderer) override;
/**
* Draws the player's HUD tree.
* @param renderer - A reference to the renderer.
*/
void drawHud(Renderer& renderer);
/**
* Draws the player's menu if they have one open.
* @param renderer - A reference to the renderer.
*/
void drawMenu(Renderer& renderer);
/**
* Asserts a player field by sending the specified player packet to the server.
* @param packet - The packet to send to the server.
*/
virtual void assertField(Packet packet) override;
/**
* Accepts an assertion packet from the server, updating local properties.
* @param d - A deserializer for the assertion packet.
*/
virtual void handleAssertion(Deserializer& d) override;
/**
* Removes the resize callback from the callbacks.
* TODO: The resize callbacks should be stored in a shared pointer member, instead of string identified,
* to reduce the chance of forgetting to unbind it, and to not require explicit destructors.
*/
~LocalPlayer();
private:
private:
/**
* Checks if a specified player-control was pressed.
*
* @param input - The input handler to pull key states from.
* @param control - The control to check the state of.
* @returns a boolean indicating if a specified player control was pressed.
*/
bool getKey(Input& input, PlayerControl control);
/**
* Moves the player in accordance to the inputs provided.
*
* @param input - The input handler to pull key states from.
* @param delta - The previous frame's delta time.
* @param mouseDelta - The offset of the mouse from the center of the screen.
*/
void updatePhysics(Input& input, double delta, glm::vec2 mouseDelta);
/**
* Moves the camera to the player's position, and updates the wield model.
*/
void updateCamera();
void findPointedThing(Input& input);
/**
* Updates the player's wireframe to point at the target.
*/
void updateWireframe();
void interact(Input& input, double delta);
/**
* Updates the hand and wield item definitions
* when their respective lists are changed.
*/
void updateWieldAndHandItems();
GameGui gameGui;
DrawableEntity handModel;
DrawableEntity handItemModel;
WireframeEntity wireframe;
/**
* Finds the target under the player's pointer.
* @param input - The input handler.
*/
void findTarget(Input& input);
/**
* Checks if the player is trying to interact with a
* block or item, and does so if they are.
*
* @param input - The input handler.
* @param delta - The delta time elapsed in the last frame.
*/
void interact(Input& input, double delta);
GameGui gameGui;
Renderer& renderer;
double breakTime = 0;
double breakInterval = 0;
/** The player's current target. */
Target target;
/** The time remaining until the next hit. */
double breakTime = 0;
/** The interval at which the currently wielded tool can hit. */
double breakInterval = 0;
/** The targeted wireframe entity. */
WireframeEntity wireframe;
/** A wrapper for the wield-item, translated to the bottom right of the player's viewport. */
DrawableEntity handModel;
/** The actual wield-item model, set to the currently held item. */
DrawableEntity handItemModel;
};

View File

@ -2,7 +2,16 @@ zepha.register_item("@auri:basic_tools:flint_pickaxe", {
name = "Flint Pick",
textures = {
"@auri:basic_tools:flint_pickaxe"
}
},
tool_props = {
interval = 0.4,
damage_groups = {
smash = 3,
crack = 7
}
},
on_hit = function(dim, pos) if dim:get_block(pos) == 'zeus:default:grass' then dim:set_block(pos, 'zeus:default:dirt') end end,
on_hit_client = function(dim, pos) if dim:get_block(pos) == 'zeus:default:grass' then dim:set_block(pos, 'zeus:default:dirt') end end
})
crafting.register_recipe({

View File

@ -1,3 +1,4 @@
zepha.register_entity("zeus:default:bee", {
display = "model",
display_object = "zeus:default:bee",
@ -5,32 +6,78 @@ zepha.register_entity("zeus:default:bee", {
on_create = function(self)
self.object.scale = 1/12
self.object.gravity = 9.8
self.object.gravity = 3
self.object.collides = true
self.object.collision_box = { { -3/16, 0/16, -3/16 }, { 3/16, 4/16, 3/16 } }
self.object.anims:define({
fly = {1, 45}
})
self.object.anims:define({ fly = {1, 45} })
self.object.anims:set_anim("fly")
self.object.anims:play()
self.object.yaw = math.random(0, 360)
self.object.vel = V {0, -0.25, 0}
self.resting = false
self.rest_timer = 0
self.next_rest = 45 + math.random() * 30
self.orient_timer = 0
self.next_orient = 5 + math.random() * 3
if math.random() < (1/1024) then
self.object:set_display_type('model', 'zeus:default:bee', 'zeus:default:bee_shiny')
end
end,
on_update = function(self, delta)
self.object.yaw = self.object.yaw + (180 * delta)
self.object.vel = V(
0.5 * math.sin(math.rad(self.object.yaw + 90)),
self.object.vel.y,
0.5 * math.cos(math.rad(self.object.yaw + 90)))
self.rest_timer = self.rest_timer + delta
self.orient_timer = self.orient_timer + delta
if self.rest_timer > self.next_rest then
self.resting = not self.resting
if self.resting then
self.object.vel = V {}
self.object.anims:pause()
else
self.object.anims:play()
end
self.rest_timer = 0
self.next_rest = self.resting and (3 + math.random() * 2) or (45 + math.random() * 30)
end
if self.orient_timer > self.next_orient then
self.orient_timer = 0
self.next_orient = 5 + math.random() * 3
self.object.yaw = math.random(0, 360)
end
if not self.resting then
self.object.vel = V {
1 * math.sin(math.rad(self.object.yaw + 90)),
self.object.vel.y,
1 * math.cos(math.rad(self.object.yaw + 90))
}
local fly = false
local def = zepha.registered_blocks[self.object.dim:get_block(self.object.pos - V{ 0, 2, 0 })]
if def and (def.solid == nil or def.solid ~= false) then fly = true end
if fly and self.object.vel.y <= 0 then
self.object.vel = self.object.vel + V { 0, 1, 0 }
end
end
end
})
if zepha.server then
zepha.bind("message", function(channel, message, player)
if channel ~= "zeus:default:spawn" then return end
if channel ~= "zeus:default:spawn" or message ~= "bee" then return end
player.dim:add_entity(player.pos + V(0, 1.7, 0), "zeus:default:bee")
end)
zepha.bind("new_player", function(player)
for i = 0, 10 do
player.dim:add_entity(player.pos + V { math.random(-100, 100), 30, math.random(-100, 100) }, "zeus:default:bee")
end
end)
else
zepha.register_keybind("zeus:default:spawn_bee", {
description = "Spawn Bee",

View File

@ -6,7 +6,11 @@ zepha.register_entity("zeus:default:rabbit", {
display_texture = "zeus:default:rabbit",
on_create = function(self)
self.object:set_scale(1/16)
self.object.scale = 1/16
self.object.gravity = 40
self.object.collides = true
self.object.yaw = math.random(0, 360)
self.object.collision_box = { { -4/16, 0/16, -4/16 }, { 4/16, 7/16, 4/16 } }
self.object.anims:define({
idle = {1, 181},
@ -15,55 +19,72 @@ zepha.register_entity("zeus:default:rabbit", {
stand_idle = {232, 401}
})
self.object.anims:set_anim("run"):play()
self.object.anims:set_anim("run")
self.object.anims:play()
self.resting = false
self.rest_timer = 0
self.next_rest = 45 + math.random() * 30
self.orient_timer = 0
self.next_orient = 5 + math.random() * 3
end,
on_update = function(self, delta)
local dist = vector.distance(zepha.player.pos, self.object.pos)
self.rest_timer = self.rest_timer + delta
self.orient_timer = self.orient_timer + delta
if friendly then
if dist > 3 and dist < 7 and not self.targeting then
self.targeting = true
self.object.anims:set_anim("run"):play()
elseif dist < 2 or dist > 7 and self.targeting then
self.targeting = false
self.object.anims:set_anim("idle"):play()
end
if self.rest_timer > self.next_rest then
self.resting = not self.resting
if self.targeting then
self.object.pos = self.object.pos +
V(0.08 * math.sin(math.rad(self.object.yaw)), 0, 0.08 * math.cos(math.rad(self.object.yaw)))
self.object.yaw = math.deg(math.atan2(zepha.player.pos.x - self.object.pos.x, zepha.player.pos.z - self.object.pos.z))
end
else
if dist < 5 and not self.targeting then
self.targeting = true
self.object.anims:set_anim("run"):play()
elseif dist > 6 and self.targeting then
self.targeting = false
self.object.anims:set_anim("idle"):play()
end
if self.targeting then
self.object.pos = self.object.pos +
V(0.08 * math.sin(math.rad(self.object.yaw)), 0, 0.08 * math.cos(math.rad(self.object.yaw)))
self.object.yaw = math.deg(math.atan2(zepha.player.pos.x - self.object.pos.x, zepha.player.pos.z - self.object.pos.z)) + 180
if self.resting then
self.object.vel = V {}
self.object.anims:set_anim("idle")
self.object.anims:play()
else
self.object.yaw = math.deg(math.atan2(zepha.player.pos.x - self.object.pos.x, zepha.player.pos.z - self.object.pos.z))
self.object.anims:set_anim("run")
self.object.anims:play()
end
self.rest_timer = 0
self.next_rest = self.resting and (3 + math.random() * 2) or (45 + math.random() * 30)
end
if self.orient_timer > self.next_orient then
self.orient_timer = 0
self.next_orient = 5 + math.random() * 3
self.object.yaw = math.random(0, 360)
end
if not self.resting then
if self.object.vel:length() < 2.8 and self.object.vel.y == 0 then
self.object:set_vel(V { self.object.vel.x, 9, self.object.vel.z })
end
self.object.vel = V {
3 * math.sin(math.rad(self.object.yaw + 90)),
self.object.vel.y,
3 * math.cos(math.rad(self.object.yaw + 90))
}
end
end
})
zepha.register_keybind("zeus:default:spawn_rabbit", {
description = "Spawn Rabbit",
default = zepha.keys.y,
on_press = function() zepha.player.dim:add_entity(zepha.player.pos, "zeus:default:rabbit") end
})
if zepha.server then
zepha.bind("message", function(channel, message, player)
if channel ~= "zeus:default:spawn" or message ~= "rabbit" then return end
player.dim:add_entity(player.pos + V(0, 1.7, 0), "zeus:default:rabbit")
end)
zepha.register_keybind("zeus:default:change_rabbit_behavior", {
description = "Change rabbit behavior",
default = zepha.keys.u,
on_press = function()
friendly = not friendly
end
})
zepha.bind("new_player", function(player)
for i = 0, 5 do
player.dim:add_entity(player.pos + V { math.random(-100, 100), 30, math.random(-100, 100) }, "zeus:default:rabbit")
end
end)
else
zepha.register_keybind("zeus:default:spawn_rabbit", {
description = "Spawn Rabbit",
default = zepha.keys.v,
on_press = function()
zepha.send_message("zeus:default:spawn", "rabbit");
end
})
end

View File

@ -9,28 +9,46 @@ zepha.register_entity("zeus:default:test", {
})
self.object.anims:set_anim("walk")
self.object.anims:play()
self.object.gravity = 40
self.object.collides = true
self.object.collision_box = { V { -5/16, 0/16, -5/16 }, V { 5/16, 29/16, 5/16 } }
math.randomseed(zepha.time.ns())
-- zepha.after(function()
-- local in_desert = self.object.dimension.identifier == 'zeus:world:endless_desert'
-- player:set_dimension(in_desert and "zeus:world:default" or "zeus:world:endless_desert")
-- end, 2)
-- self.object.dim:add_entity(self.object.pos, "zeus:default:test")
-- return true
-- end, 10)
end,
on_update = function(self, delta)
self.object.pos = self.object.pos +
V(0.6 * math.sin(math.rad(self.object.yaw)), 0, 0.6 * math.cos(math.rad(self.object.yaw))) * delta
self.object.yaw = self.object.yaw + 50 * delta
-- self.object.yaw = self.object.yaw + (180 * delta)
self.object.vel = V {
2 * math.sin(math.rad(self.object.yaw)),
self.object.vel.y,
2 * math.cos(math.rad(self.object.yaw))
}
if math.random() > 0.99 then
self.object.yaw = math.random(0, 360)
end
if math.random() > 0.99 then
self.object:set_vel(V { self.object.vel.x, 10, self.object.vel.z })
end
end
})
if zepha.client then
zepha.after(function()
zepha.player.dim:add_entity(V(0, -34, 0), "zeus:default:test")
end, 0)
end
--if zepha.client then
-- zepha.after(function()
-- zepha.player.dim:add_entity(V(0, -34, 0), "zeus:default:test")
-- end, 0)
--end
if zepha.server then
zepha.bind("player_join", function(player)
player.dim:add_entity(V(0, -32, 0), "zeus:default:test")
for i = 0, 4 do
player.dim:add_entity(V(-145, -32, -30), "zeus:default:test")
end
end)
end

View File

@ -2,6 +2,6 @@
"name": "zeus:world",
"description": "World and Mapgen definitions for the Zeus subgame.",
"version": "0.0.1",
"depends": ["zeus:default"]
"depends": [ "zeus:default", "zeus:flowers" ]
}

View File

@ -42,6 +42,18 @@ for i = 1, 5 do
}))
end
table.insert(structures, zepha.create_structure({
origin = V(),
probability = 0.025,
schematic = {{{ "zeus:flowers:flower_geranium" }}}
}))
table.insert(structures, zepha.create_structure({
origin = V(),
probability = 0.025,
schematic = {{{ "zeus:flowers:flower_white_dandelion" }}}
}))
local noise = {
heightmap = {
module = "add",