File WfItem.c
File List > game > src > nonEntityGameData > WfItem.c
Go to the documentation of this file
// std lib
#include <stdlib.h>
#include <stdio.h>
#include "string.h"
// 3rd party
#include <libxml/parser.h>
#include <libxml/tree.h>
#include "cwalk.h"
#include "lua.h"
#include <lualib.h>
// zzfx
#include "ZzFX.h"
// engine
#include "DynArray.h"
#include "AssertLib.h"
#include "Scripting.h"
#include "Log.h"
#include "main.h"
#include "FilesystemUtils.h"
#include "XMLHelpers.h"
#include "XMLSchema.h"
#include "SharedLib.h"
#include "StringKeyHashMap.h"
// game
#include "WfAllItems.h"
#if BAKE_ITEM_DEFS
#include "BakedItems.h"
#endif
static VECTOR(struct WfItemDef) gItemDefs = NULL;
static struct HashMap gItemNameHashmap;
void WfAddItemDef(struct WfItemDef* pDef)
{
gItemDefs = VectorPush(gItemDefs, pDef);
int i = VectorSize(gItemDefs) - 1;
HashmapInsert(&gItemNameHashmap, pDef->itemName, &i);
}
static void* ResolveXMLSpecifiedFunction(xmlNode* pXMLFn)
{
void* pR = NULL;
if(!pXMLFn)
{
return NULL;
}
xmlNode* pChild = GetNthChild(pXMLFn, 0);
if(strcmp( pChild->name, "c-function") == 0)
{
xmlChar* dll = NULL;
xmlChar* name = xmlGetProp(pChild, "name");
if(dll = xmlGetProp(pChild, "dll"))
{
char buf[256];
cwk_path_join(gCmdArgs.assetsDir, name, buf, 256);
#ifdef WIN32_
strcat(dll, ".dll");
#else
strcat(dll, ".so");
#endif
struct SharedLib* pSO = SharedLib_LoadSharedLib(buf);
pR = SharedLib_GetProc(pSO, name);
}
else
{
pR = SharedLib_GetCurrentlyLoadedFn(name);
}
}
else if(strcmp(pChild->name, "lua-function") == 0)
{
EASSERT(false); // not implemented yet
}
return pR;
}
enum WfActionAnimation GetAnimationFromString(xmlChar* str)
{
if(xmlStrcmp(str, "WfSlashAnim") == 0)
{
return WfSlashAnim;
}
else if(xmlStrcmp(str, "WfThrustAnim") == 0)
{
return WfThrustAnim;
}
return WfNoActionAnim;
}
static struct ZZFXSound ParseZzfxSound(xmlNode* pZzfxOnPickup)
{
if(!pZzfxOnPickup)
{
struct ZZFXSound snd = {};
return snd;
}
float vals[21];
xmlChar* zzfxString = xmlGetProp(pZzfxOnPickup, "str");
sscanf(zzfxString, "{%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,%f}",
&vals[0],
&vals[1],
&vals[2],
&vals[3],
&vals[4],
&vals[5],
&vals[6],
&vals[7],
&vals[8],
&vals[9],
&vals[10],
&vals[11],
&vals[12],
&vals[13],
&vals[14],
&vals[15],
&vals[16],
&vals[17],
&vals[18],
&vals[19],
&vals[20]
);
struct ZZFXSound r = {
vals[0],
vals[1],
vals[2],
vals[3],
vals[4],
vals[5],
vals[6],
vals[7],
vals[8],
vals[9],
vals[10],
vals[11],
vals[12],
vals[13],
vals[14],
vals[15],
vals[16],
vals[17],
vals[18],
vals[19],
vals[20]
};
return r;
}
static struct WfItemConfigPropertyBag ParseConfigData(xmlNode* pConfigData)
{
struct WfItemConfigPropertyBag b;
b.bSet = false;
if(!pConfigData)
{
return b;
}
HashmapInit(&b.properties, 16, sizeof(struct WfItemConfigProperty));
unsigned long numchildren = xmlChildElementCount(pConfigData);
for(int i = 0; i < numchildren; i++)
{
xmlNode* pChildI = GetNthChild(pConfigData, i);
int setCount = 0;
struct WfItemConfigProperty prop;
xmlChar* propName = xmlGetProp(pChildI, "name");
xmlChar* value = xmlGetProp(pChildI, "value");
if(strcmp(pChildI->name, "Float") == 0)
{
prop.type = WfItemConfig_Float;
prop.val.floatVal = (float)atof(value);
}
if(strcmp(pChildI->name, "Int") == 0)
{
prop.type = WfItemConfig_Int;
prop.val.intVal = atoi(value);
}
if(strcmp(pChildI->name, "Bool") == 0)
{
prop.type = WfItemConfig_Bool;
prop.val.boolVal = (strcmp(value, "true") == 0) ? true : false;
}
if(strcmp(pChildI->name, "String") == 0)
{
prop.type = WfItemConfig_String;
prop.val.stringVal = malloc(strlen(value) + 1);
strcpy(prop.val.stringVal, value);
}
if(strcmp(pChildI->name, "Array") == 0)
{
prop.type = WfItemConfig_Array;
unsigned long numGrandchildrend = xmlChildElementCount(pChildI);
prop.val.propertyArray.pData = malloc(sizeof(struct WfItemConfigProperty) * numGrandchildrend);
prop.val.propertyArray.length = numGrandchildrend;
for(int j=0; j<numGrandchildrend; j++)
{
prop.val.propertyArray.pData[j].val.bag = ParseConfigData(GetNthChild(pChildI, j));
}
}
HashmapInsert(&b.properties, propName, &prop);
b.bSet = true;
}
return b;
}
static void AddItemDefXML(xmlNode* pChildI)
{
xmlNode* pUISpriteName = XMLFindChild(pChildI, "ui-sprite-name");
xmlNode* pOnMakeCurrent = XMLFindChild(pChildI, "on-make-current");
xmlNode* pOnStopCurrent = XMLFindChild(pChildI, "on-stop-being-current");
xmlNode* pOnUseItem = XMLFindChild(pChildI, "on-use-item");
xmlNode* pOnTryEquip = XMLFindChild(pChildI, "on-try-equip");
xmlNode* pOnDrawDebugLines = XMLFindChild(pChildI, "on-draw-debug-lines");
xmlNode* pOnUseAnimation = XMLFindChild(pChildI, "on-use-animation");
xmlNode* pCanUseItem = XMLFindChild(pChildI, "can-use-item");
xmlNode* pPickupSpriteName = XMLFindChild(pChildI, "pickup-sprite-name");
xmlNode* pOnGamelayerPush = XMLFindChild(pChildI, "on-gamelayer-push");
xmlNode* pConfigData = XMLFindChild(pChildI, "config-data");
xmlNode* pbSFXOnPickup = XMLFindChild(pChildI, "b-sound-effect-on-pickup");
xmlNode* pZzfxOnPickup = XMLFindChild(pChildI, "zzfx-sound-on-pickup");
xmlChar* itemName = xmlGetProp(pChildI, "name");
struct WfItemDef def = {
.UISpriteName = xmlGetProp(pUISpriteName, "str"),
.onMakeCurrent = ResolveXMLSpecifiedFunction(pOnMakeCurrent),
.onStopBeingCurrent = ResolveXMLSpecifiedFunction(pOnStopCurrent),
.onUseItem = ResolveXMLSpecifiedFunction(pOnUseItem),
.onTryEquip = ResolveXMLSpecifiedFunction(pOnTryEquip),
.onGameLayerPush = ResolveXMLSpecifiedFunction(pOnGamelayerPush),
.drawDebugLines = ResolveXMLSpecifiedFunction(pOnDrawDebugLines),
.onUseAnimation = GetAnimationFromString(xmlGetProp(pOnUseAnimation, "str")),
.bCanUseItem = strcmp(xmlGetProp(pCanUseItem, "bool"), "true") == 0,
.pickupSpriteName = xmlGetProp(pPickupSpriteName, "str"),
.bSoundEffectOnPickup = pbSFXOnPickup && (strcmp(xmlGetProp(pbSFXOnPickup, "bool"), "true") == 0),
.zzfxPickup = ParseZzfxSound(pZzfxOnPickup),
.itemName = malloc(strlen(itemName) + 1),
.config = ParseConfigData(pConfigData),
};
strcpy(def.itemName, itemName);
WfAddItemDef(&def);
}
void WfInitItems()
{
#if BAKE_ITEM_DEFS
gItemDefs = NEW_VECTOR(struct WfItemDef);
HashmapInit(&gItemNameHashmap, 32, sizeof(int));
WfInitBakedItems();
return;
#else
char buf[256];
char buf2[256];
gItemDefs = NEW_VECTOR(struct WfItemDef);
HashmapInit(&gItemNameHashmap, 32, sizeof(int));
cwk_path_join(gCmdArgs.assetsDir, "items_data.xml", buf, 256);
cwk_path_join(gCmdArgs.assetsDir, "items_data_schema.xml", buf2, 256);
xmlDoc* pXMLDoc = NULL;
if(FS_DoesFileExist(buf))
{
if(FS_DoesFileExist(buf2))
{
bool valid = XS_XSDValidate(buf2, buf);
if(!valid)
{
Log_Error("items_data.xml: invalid schema. Falling back to builtin items");
}
}
else
{
Log_Error("Schema for item data doesn't exist! Attempting to continue without");
}
pXMLDoc = xmlReadFile(buf, NULL, 0);
if (pXMLDoc)
{
Log_Verbose("pXMLDoc is valid");
xmlNode* root = xmlDocGetRootElement(pXMLDoc);
unsigned long numchildren = xmlChildElementCount(root);
for(int i = 0; i < numchildren; i++)
{
xmlNode* pChildI = GetNthChild(root, i);
AddItemDefXML(pChildI);
}
}
}
if (pXMLDoc)
{
xmlFreeDoc(pXMLDoc);
}
#endif
}
int l_GetItemUISpriteName(lua_State* L)
{
if(lua_isinteger(L, -1))
{
int arg = lua_tointeger(L, -1);
if(arg < VectorSize(gItemDefs) && arg >= 0)
{
lua_pushstring(L, gItemDefs[arg].UISpriteName);
return 1;
}
else
{
Log_Error("l_GetItemUISpriteName ARGUMENT OUT OF RANGE: %i. Itemdefs size: %i", arg, VectorSize(gItemDefs));
}
}
else
{
Log_Error("l_GetItemUISpriteName BAD ARGS, expected int");
}
lua_pushstring(L, "no-item");
return 1;
}
void WfRegisterItemScriptFunctions()
{
Sc_RegisterCFunction("WfGetItemSpriteName", &l_GetItemUISpriteName);
}
const struct WfItemDef* WfGetItemDef(int itemIndex)
{
if(itemIndex < 0)
{
return NULL;
}
return &gItemDefs[itemIndex];
}
struct WfItemDef* WfGetItemDefs(int* numDefs)
{
*numDefs = VectorSize(gItemDefs);
return gItemDefs;
}
struct WfItemConfigProperty* WfGetItemConfigProperty(struct WfItemConfigPropertyBag* pBag, const char* propertyName)
{
if(!pBag->bSet)
{
Log_Warning("Warning, item has no config properties set!");
return NULL;
}
return HashmapSearch(&pBag->properties, propertyName);
}