File WfTree.c
File List > entities > WfTree.c
Go to the documentation of this file
#include <string.h>
#include "WfTree.h"
#include "BinarySerializer.h"
#include "Entities.h"
#include "Game2DLayer.h"
#include "Components.h"
#include "Atlas.h"
#include "WfGameLayerData.h"
#include "WfEntities.h"
#include "WfEntityMessages.h"
#include "Log.h"
#include "AssertLib.h"
#include "GameFramework.h"
#include "Maths.h"
#include "ZzFX.h"
#include "WfItemPickup.h"
#include "WfItem.h"
#include "WfPlayer.h"
#include "Audio.h"
#define STUMP_SPITE_INDEX 0
#define TRUNK_SPRITE_INDEX 1
#define TOP_SPRITE_INDEX 2
#define TREE_FALL_SPEED 60.0f
// increase speed by this many rpm every second
#define TREE_FALL_CHANGE_SPEED_PER_SECOND 90.0f
#define STUMP_HEALTH 30.0f;
#define STUMP_HEIGHT (50.0f) // the height of just the stump part of the tree sprite to the bottom of the sprite
struct ZZFXSound gTreeHitSFX = {1.0,0.05,63.14,0.049,0.086,0.203,2,1.0,3.678,-4.693,0.0,0.0,0.261,0.185,0.0,0.699,0.0,0.388,0.108,0.087,0.0};
struct ZZFXSound gTreeFallSFX = {1.0,0.05,590.6,0.005,0.28,0.257,1,1.0,0.0,0.0,-196.86,0.159,0.165,0.12,15.624050000000004,0.168,0.0,0.635,0.227,0.421,0.0};
enum WfTreeState
{
WfStanding,
WfFalling,
WfFallen,
WfStump,
};
struct WfTreeEntityData
{
struct WfTreeDef def;
vec2 groundContactPoint;
float health;
enum WfTreeState state;
float treeFallDirection;
float treeFallRate;
};
static OBJECT_POOL(struct WfTreeEntityData) gTreeDataObjectPool;
void WfTreeInit()
{
gTreeDataObjectPool = NEW_OBJECT_POOL(struct WfTreeEntityData, 512);
}
static void TreeOnDestroy(struct Entity2D* pEnt, struct GameFrameworkLayer* pData)
{
FreeObjectPoolIndex(gTreeDataObjectPool, pEnt->user.hData);
Entity2DOnDestroy(pEnt, pData);
}
static float TreeGetPreDrawSortValue(struct Entity2D* pEnt)
{
struct WfTreeEntityData* pData = &gTreeDataObjectPool[pEnt->user.hData];
return pData->groundContactPoint[1];
}
static void WfSpawnWoodAt(vec2 pos, int quant, struct GameLayer2DData* pGameLayerData)
{
/* spawn wood pickups */
struct WfItemPickupDef def =
{
.itemID = WfWoodItem,
.itemQuantity = 1
};
WfAddPickupBasedAt(pos[0], pos[1], &def, pGameLayerData);
}
static void TreeHandleEntityMsg(struct Entity2D* pEnt, struct Entity2D* pSender, struct EntityToEntityMessage* pMsg, struct GameFrameworkLayer* pLayer)
{
struct GameLayer2DData* pGameLayerData = pLayer->userData;
struct WfTreeEntityData* pData = &gTreeDataObjectPool[pEnt->user.hData];
switch(pMsg->type)
{
case E2EM_Damage:
{
struct WfDamageMsg* pDamageMessage = WfGetDamageMessage(pMsg);//pMsg->pMsgData;
switch (pDamageMessage->type)
{
case WfAxeDamage:
Au_PlayZzFX(&gTreeHitSFX);
pData->health -= pDamageMessage->damage;
if(pData->health <= 0)
{
switch(pData->state)
{
case WfStanding:
{
if(pSender->type == WfEntityType_Player)
{
vec2 playerGroundPos, treeGroundContactPoint;
WfPlayerGetGroundContactPoint(pSender, playerGroundPos);
WfTreeGetGroundContactPoint(pEnt, treeGroundContactPoint);
if(playerGroundPos[0] > treeGroundContactPoint[0])
{
// player to the right of the tree
pData->treeFallDirection = -1.0f;
}
else
{
pData->treeFallDirection = 1.0f;
}
}
else
{
pData->treeFallDirection = 1.0f;
}
pData->health = 0;
Au_PlayZzFX(&gTreeFallSFX);
pData->state = WfFalling;
}
break;
case WfStump:
{
vec2 treeGroundContactPoint;
vec2 addition = {pData->treeFallDirection * 5.0f};
WfTreeGetGroundContactPoint(pEnt, treeGroundContactPoint);
glm_vec2_add(addition, treeGroundContactPoint, treeGroundContactPoint);
WfSpawnWoodAt(treeGroundContactPoint, 2, pGameLayerData);
Et2D_DestroyEntity(pLayer, &pGameLayerData->entities, pEnt->thisEntity);
pGameLayerData->bCurrentLocationIsDirty = true;
}
break;
}
}
Log_Info("Tree with ID %i took %.2f %s damage from entity %i, %.2f health remaining", pEnt->thisEntity, pDamageMessage->damage, WfDamageTypeNameLUT[pDamageMessage->type], pSender->thisEntity, pData->health);
break;
default:
EASSERT(pDamageMessage->type < sizeof(WfDamageTypeNameLUT) / sizeof(char*) && pDamageMessage->type >= 0);
Log_Info("Tree with ID %i resisted %.2f %s damage from entity %i, immune", pEnt->thisEntity, pDamageMessage->damage, WfDamageTypeNameLUT[pDamageMessage->type], pSender->thisEntity);
break;
}
}
}
}
void TreeUpdate(struct Entity2D* pEnt, struct GameFrameworkLayer* pLayer, float deltaT)
{
struct GameLayer2DData* pGameLayerData = pLayer->userData;
struct WfTreeEntityData* pData = &gTreeDataObjectPool[pEnt->user.hData];
switch (pData->state)
{
case WfStanding:
break;
case WfFalling:
{
float treeFallRPS = pData->treeFallRate / 60.0f;
pEnt->components[TOP_SPRITE_INDEX].data.sprite.transform.rotation += (treeFallRPS * deltaT) * pData->treeFallDirection;
pEnt->components[TRUNK_SPRITE_INDEX].data.sprite.transform.rotation += (treeFallRPS * deltaT) * pData->treeFallDirection;
bool bFinished = false;
if(pData->treeFallDirection <= 0.0f)
{
bFinished = pEnt->components[TOP_SPRITE_INDEX].data.sprite.transform.rotation < -DEGREES_TO_RADIANS(90.0f);
}
else
{
bFinished = pEnt->components[TOP_SPRITE_INDEX].data.sprite.transform.rotation > DEGREES_TO_RADIANS(90.0f);
}
if(bFinished)
{
pEnt->components[TOP_SPRITE_INDEX].data.sprite.bDraw = false;
pEnt->components[TRUNK_SPRITE_INDEX].data.sprite.bDraw = false;
pData->state = WfStump;
pData->health = STUMP_HEALTH;
vec2 base;
WfTreeGetGroundContactPoint(pEnt, base);
vec2 addition = {
pData->treeFallDirection * 40.0f,
0 /*TODO: add +/- a random amount to y */
};
glm_vec2_add(base, addition, base);
WfSpawnWoodAt(base, 1, pGameLayerData);
glm_vec2_add(base, addition, base);
WfSpawnWoodAt(base, 1, pGameLayerData);
glm_vec2_add(base, addition, base);
WfSpawnWoodAt(base, 1, pGameLayerData);
}
pData->treeFallRate += TREE_FALL_CHANGE_SPEED_PER_SECOND * deltaT;
}
break;
case WfStump:
break;
default:
break;
}
}
static void WfMakeEntityIntoTreeBasedAt(struct Entity2D* pEnt, float x, float y, struct WfTreeDef* def, struct GameLayer2DData* pGameLayerData)
{
struct WfSprites* pSprites = &((struct WfGameLayerData*)pGameLayerData->pUserData)->sprites;
memset(pEnt, 0, sizeof(struct Entity2D));
const float trunkOffsetPx = 64.0f; /* Y offset from the top of the tree top sprite to the top of the trunk sprite */
const float spriteHeight = 96.0f;
const float combinedTreeSpriteHeight = trunkOffsetPx + spriteHeight;
const float combinedSpriteWidth = 96.0f;
const float bottomOfTrunkSpriteToBase = 34.0f;
// xPos and YPos are where the base of the tree is
pEnt->transform.position[0] = x - combinedSpriteWidth / 2.0f; // center it
pEnt->transform.position[1] = y - (combinedTreeSpriteHeight - bottomOfTrunkSpriteToBase);
pEnt->transform.scale[0] = 1.0f;
pEnt->transform.scale[1] = 1.0f;
pEnt->transform.rotation = 0.0f;
pEnt->transform.rotationPointRelative[0] = 0.0f;
pEnt->transform.rotationPointRelative[1] = 0.0f;
pEnt->bKeepInQuadtree = true;
pEnt->bKeepInDynamicList = false;
pEnt->type = WfEntityType_Tree;
struct Component2D* pComponent1 = &pEnt->components[pEnt->numComponents++];
struct Component2D* pComponent2 = &pEnt->components[pEnt->numComponents++];
struct Component2D* pComponent3 = &pEnt->components[pEnt->numComponents++];
struct Component2D* pComponent4 = &pEnt->components[pEnt->numComponents++];
struct WfTreeSprites* pFoundSeason = &pSprites->treeSpritesPerSeason[def->season];
hSprite topSprite = NULL_HANDLE;
hSprite trunkSprite = NULL_HANDLE;
hSprite stumpSprite = NULL_HANDLE;
switch (def->type)
{
case Coniferous:
topSprite = def->subtype == 0 ? pFoundSeason->coniferousTop1 : pFoundSeason->coniferousTop2;
trunkSprite = pFoundSeason->trunk2;
stumpSprite = pFoundSeason->stump2;
break;
case Deciduous:
topSprite = def->subtype == 0 ? pFoundSeason->deciduousTop1 : pFoundSeason->deciduousTop2;
trunkSprite = pFoundSeason->trunk1;
stumpSprite = pFoundSeason->stump1;
break;
default:
break;
}
/* order is important as we want the tree trunk to be drawn first and the top on top of that */
pComponent1->type = ETE_Sprite;
pComponent1->data.sprite.sprite = stumpSprite;
memset(&pComponent1->data.sprite.transform, 0, sizeof(struct Transform2D));
pComponent1->data.sprite.transform.position[1] = trunkOffsetPx;
pComponent1->data.sprite.transform.scale[0] = 1.0f;
pComponent1->data.sprite.transform.scale[1] = 1.0f;
pComponent1->data.sprite.bDraw = true;
pComponent2->type = ETE_Sprite;
pComponent2->data.sprite.sprite = trunkSprite;
memset(&pComponent2->data.sprite.transform, 0, sizeof(struct Transform2D));
pComponent2->data.sprite.transform.position[1] = trunkOffsetPx;
pComponent2->data.sprite.transform.scale[0] = 1.0f;
pComponent2->data.sprite.transform.scale[1] = 1.0f;
pComponent2->data.sprite.transform.rotationPointRelative[0] = (float)At_GetSprite(trunkSprite, pGameLayerData->hAtlas)->widthPx / 2.0f;
pComponent2->data.sprite.transform.rotationPointRelative[1] = (float)At_GetSprite(trunkSprite, pGameLayerData->hAtlas)->heightPx - 46.0f;
pComponent2->data.sprite.bDraw = true;
pComponent3->type = ETE_Sprite;
pComponent3->data.sprite.sprite = topSprite;
memset(&pComponent3->data.sprite.transform, 0, sizeof(struct Transform2D));
pComponent3->data.sprite.transform.scale[0] = 1.0f;
pComponent3->data.sprite.transform.scale[1] = 1.0f;
pComponent3->data.sprite.transform.rotationPointRelative[0] = (float)At_GetSprite(topSprite, pGameLayerData->hAtlas)->widthPx / 2.0f;
pComponent3->data.sprite.transform.rotationPointRelative[1] = (float)At_GetSprite(topSprite, pGameLayerData->hAtlas)->heightPx + (trunkOffsetPx - 46.0f);
pComponent3->data.sprite.bDraw = true;
pComponent4->type = ETE_StaticCollider;
pComponent4->data.staticCollider.shape.type = PBT_Circle;
pComponent4->data.staticCollider.shape.data.circle.center[0] = x;//transform2Ground[0];
pComponent4->data.staticCollider.shape.data.circle.center[1] = y;//transform2Ground[1];
pComponent4->data.staticCollider.shape.data.circle.radius = 6;
pComponent4->data.staticCollider.bIsSensor = false;
pComponent4->data.staticCollider.onSensorOverlapBegin = NULL;
pComponent4->data.staticCollider.onSensorOverlapEnd = NULL;
pComponent4->data.staticCollider.bGenerateSensorEvents = false;
pComponent4->data.sprite.bDraw = true;
HGeneric hTreeData = NULL_HANDLE;
gTreeDataObjectPool = GetObjectPoolIndex(gTreeDataObjectPool, &hTreeData);
struct WfTreeEntityData* pTreeData = &gTreeDataObjectPool[hTreeData];
pTreeData->def = *def;
pTreeData->groundContactPoint[0] = x;
pTreeData->groundContactPoint[1] = y;
pTreeData->health = 100.0f;
pTreeData->state = WfStanding;
pTreeData->treeFallRate = TREE_FALL_SPEED;
pEnt->user.hData = hTreeData;
Et2D_PopulateCommonHandlers(pEnt);
pEnt->onDestroy = &TreeOnDestroy;
pEnt->getSortPos = &TreeGetPreDrawSortValue;
pEnt->handleEntityMsg = &TreeHandleEntityMsg;
pEnt->update = &TreeUpdate;
pEnt->bSerializeToDisk = true;
pEnt->bSerializeToNetwork = true;
}
void WfDeSerializeTreeEntity(struct BinarySerializer* bs, struct Entity2D* pOutEnt, struct GameLayer2DData* pData)
{
u32 version = 0;
BS_DeSerializeU32(&version, bs); // version
switch (version)
{
case 1:
{
struct WfTreeEntityData entData;
BS_DeSerializeI32((i32*)&entData.def.season, bs);
BS_DeSerializeI32((i32*)&entData.def.type, bs);
BS_DeSerializeI32((i32*)&entData.def.subtype, bs);
BS_DeSerializeFloat(&entData.groundContactPoint[0], bs);
BS_DeSerializeFloat(&entData.groundContactPoint[1], bs);
BS_DeSerializeFloat(&entData.health, bs);
WfMakeEntityIntoTreeBasedAt(pOutEnt, entData.groundContactPoint[0], entData.groundContactPoint[1], &entData.def, pData);
}
break;
default:
break;
}
}
void WfSerializeTreeEntity(struct BinarySerializer* bs, struct Entity2D* pInEnt, struct GameLayer2DData* pData)
{
struct WfTreeEntityData* pEntData = &gTreeDataObjectPool[pInEnt->user.hData];
BS_SerializeU32(1, bs); // version
BS_SerializeI32((i32)pEntData->def.season, bs);
BS_SerializeI32((i32)pEntData->def.type, bs);
BS_SerializeI32((i32)pEntData->def.subtype, bs);
BS_SerializeFloat(pEntData->groundContactPoint[0], bs);
BS_SerializeFloat(pEntData->groundContactPoint[1], bs);
BS_SerializeFloat(pEntData->health, bs);
}
HEntity2D WfAddTreeBasedAt(float x, float y, struct WfTreeDef* def, struct GameLayer2DData* pGameLayerData)
{
struct Entity2D ent;
WfMakeEntityIntoTreeBasedAt(&ent, x, y, def, pGameLayerData);
return Et2D_AddEntity(&pGameLayerData->entities, &ent);
}
void WfTreeGetGroundContactPoint(struct Entity2D* pTreeEnt, vec2 outPos)
{
EASSERT(pTreeEnt->type == WfEntityType_Tree);
struct WfTreeEntityData* pTreeData = &gTreeDataObjectPool[pTreeEnt->user.hData];
outPos[0] = pTreeData->groundContactPoint[0];
outPos[1] = pTreeData->groundContactPoint[1];
}