/*
 * OpenClonk, http://www.openclonk.org
 *
 * Copyright (c) 1998-2000, Matthes Bender
 * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
 * Copyright (c) 2009-2016, The OpenClonk Team and contributors
 *
 * Distributed under the terms of the ISC license; see accompanying file
 * "COPYING" for details.
 *
 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
 * See accompanying file "TRADEMARK" for details.
 *
 * To redistribute this file separately, substitute the full license texts
 * for the above references.
 */

/* Object definition */

#include "C4Include.h"
#include "C4ForbidLibraryCompilation.h"
#include "object/C4Def.h"

#include "c4group/C4Components.h"
#include "graphics/C4Draw.h"
#include "graphics/C4DrawGL.h"
#include "graphics/C4GraphicsResource.h"
#include "graphics/CSurface8.h"
#include "landscape/C4Particles.h"
#include "landscape/C4SolidMask.h"
#include "lib/StdColors.h"
#include "lib/StdMeshLoader.h"
#include "object/C4Object.h"
#include "platform/C4FileMonitor.h"
#include "platform/C4SoundSystem.h"
#include "player/C4RankSystem.h"

// Helper class to load additional resources required for meshes from
// a C4Group.
class C4DefAdditionalResourcesLoader: public StdMeshMaterialLoader
{
public:
	C4DefAdditionalResourcesLoader(C4Group& hGroup): Group(hGroup) {}

	C4Surface* LoadTexture(const char* filename) override
	{
		if (!Group.AccessEntry(filename)) return nullptr;
		C4Surface* surface = new C4Surface;
		// Suppress error message here, StdMeshMaterial loader
		// will show one.
		if (!surface->Read(Group, GetExtension(filename), C4SF_MipMap))
			{ delete surface; surface = nullptr; }
		return surface;
	}

	StdStrBuf LoadShaderCode(const char* filename) override
	{
		StdStrBuf ret;
		if (!Group.LoadEntryString(filename, &ret)) return StdStrBuf();
		return ret;
	}

	void AddShaderSlices(C4Shader& shader, int ssc) override
	{
#ifndef USE_CONSOLE
		// Add mesh-independent slices
		shader.AddDefine("OPENCLONK");
		shader.AddDefine("OC_MESH");

		if (ssc & C4SSC_MOD2) shader.AddDefine("OC_CLRMOD_MOD2");
		if (ssc & C4SSC_LIGHT) shader.AddDefine("OC_DYNAMIC_LIGHT");

		// Note these are never set for meshes at the moment:
		if (ssc & C4SSC_BASE) shader.AddDefine("OC_HAVE_BASE");
		if (ssc & C4SSC_OVERLAY) shader.AddDefine("OC_HAVE_OVERLAY");

		shader.LoadFragmentSlices(&::GraphicsResource.Files, "CommonShader.glsl");
		shader.LoadFragmentSlices(&::GraphicsResource.Files, "ObjectShader.glsl");

		// Categories for script shaders.
		shader.SetScriptCategories({"Common", "Object"});
#endif
	}

private:
	C4Group& Group;
};



void C4Def::DefaultDefCore()
{
	rC4XVer[0]=rC4XVer[1]=0;
	RequireDef.Clear();
	Shape.Default();
	Entrance.Default();
	Collection.Default();
	PictureRect.Default();
	SolidMask.Default();
	TopFace.Default();
	BurnTurnTo=C4ID::None;
	GrowthType=0;
	CrewMember=0;
	NativeCrew=0;
	Mass=0;
	Value=0;
	Exclusive=0;
	Category=0;
	Constructable=0;
	Rotateable=0;
	RotatedEntrance=0;
	Float=0;
	ColorByOwner=0;
	NoHorizontalMove=0;
	LiftTop=0;
	GrabPutGet=0;
	UprightAttach=0;
	Line=0;
	LineIntersect=0;
	IncompleteActivity=0;
	Oversize=0;
	Fragile=0;
	NoPushEnter=0;
	Projectile=0;
	VehicleControl=0;
	Pathfinder=0;
	NoMassFromContents=0;
	MoveToRange=0;
	NoStabilize=0;
	ClosedContainer=0;
	SilentCommands=0;
	TemporaryCrew=0;
	BlitMode=C4D_Blit_Normal;
	NoBreath=0;
	ConSizeOff=0;
	NoGet=0;
	NoTransferZones=0;
	HideInCreator = false;
}

bool C4Def::LoadDefCore(C4Group &hGroup)
{
	StdStrBuf Source;
	if (hGroup.LoadEntryString(C4CFN_DefCore,&Source))
	{
		StdStrBuf Name = hGroup.GetFullName() + (const StdStrBuf &)FormatString("%cDefCore.txt", DirectorySeparator);
		if (!Compile(Source.getData(), Name.getData()))
			return false;
		Source.Clear();

		// Check mass
		if (Mass < 0)
		{
			DebugLogF("WARNING: Def %s (%s) at %s has invalid mass!", GetName(), id.ToString(), hGroup.GetFullName().getData());
			Mass = 0;
		}

		return true;
	}
	return false;
}

bool C4Def::Save(C4Group &hGroup)
{
	StdStrBuf Out;
	if (! Decompile(&Out, FormatString("%s::DefCore.txt", id.ToString()).getData()) )
		return false;
	return hGroup.Add(C4CFN_DefCore,Out,false,true);
}

bool C4Def::Compile(const char *szSource, const char *szName)
{
	return CompileFromBuf_LogWarn<StdCompilerINIRead>(mkNamingAdapt(*this, "DefCore"), StdStrBuf(szSource), szName);
}

bool C4Def::Decompile(StdStrBuf *pOut, const char *szName)
{
	return DecompileToBuf_Log<StdCompilerINIWrite>(mkNamingAdapt(*this, "DefCore"), pOut, szName);
}

void C4Def::CompileFunc(StdCompiler *pComp)
{

	pComp->Value(mkNamingAdapt(id,                "id",                 C4ID::None          ));
	pComp->Value(mkNamingAdapt(toC4CArr(rC4XVer),             "Version"                               ));
	pComp->Value(mkNamingAdapt(mkParAdapt(RequireDef, false), "RequireDef",         C4IDList()        ));

	const StdBitfieldEntry<int32_t> Categories[] =
	{

		{ "C4D_None",                     C4D_None                },
		{ "C4D_StaticBack",               C4D_StaticBack          },
		{ "C4D_Structure",                C4D_Structure           },
		{ "C4D_Vehicle",                  C4D_Vehicle             },
		{ "C4D_Living",                   C4D_Living              },
		{ "C4D_Object",                   C4D_Object              },

		{ "C4D_Goal",                     C4D_Goal                },
		{ "C4D_Rule",                     C4D_Rule                },
		{ "C4D_Environment",              C4D_Environment         },
		
		{ "C4D_Background",               C4D_Background          },
		{ "C4D_Parallax",                 C4D_Parallax            },
		{ "C4D_MouseSelect",              C4D_MouseSelect         },
		{ "C4D_Foreground",               C4D_Foreground          },
		{ "C4D_MouseIgnore",              C4D_MouseIgnore         },
		{ "C4D_IgnoreFoW",                C4D_IgnoreFoW           },

		{ nullptr,                           0                       }
	};

	pComp->Value(mkNamingAdapt(mkBitfieldAdapt<int32_t>(Category, Categories),
	                           "Category",           0             ));

	pComp->Value(mkParAdapt(Shape, static_cast<C4Shape*>(nullptr)));
	pComp->Value(mkNamingAdapt(Value,                         "Value",              0                 ));
	pComp->Value(mkNamingAdapt(Mass,                          "Mass",               0                 ));
	pComp->Value(mkNamingAdapt(SolidMask,                     "SolidMask",          TargetRect0       ));
	pComp->Value(mkNamingAdapt(TopFace,                       "TopFace",            TargetRect0       ));
	pComp->Value(mkNamingAdapt(PictureRect,                   "Picture",            Rect0             ));
	pComp->Value(mkNamingAdapt(Entrance,                      "Entrance",           Rect0             ));
	pComp->Value(mkNamingAdapt(Collection,                    "Collection",         Rect0             ));
	pComp->Value(mkNamingAdapt(Exclusive,                     "Exclusive",          0                 ));
	pComp->Value(mkNamingAdapt(Line,                          "Line",               0                 ));
	// <Newton> undocumented, but obsolete? I don't understand the sense of this value.
	pComp->Value(mkNamingAdapt(LineIntersect,                 "LineIntersect",      0                 ));
	pComp->Value(mkNamingAdapt(CrewMember,                    "CrewMember",         0                 ));
	pComp->Value(mkNamingAdapt(NativeCrew,                    "NoStandardCrew",     0                 ));
	pComp->Value(mkNamingAdapt(Constructable,                 "Construction",       0                 ));

	const StdBitfieldEntry<int32_t> GrabPutGetTypes[] =
	{

		{ "C4D_GrabGet"            ,C4D_Grab_Get},
		{ "C4D_GrabPut"            ,C4D_Grab_Put},

		{ nullptr,                     0}
	};

	pComp->Value(mkNamingAdapt(mkBitfieldAdapt(GrabPutGet, GrabPutGetTypes),
	                           "GrabPutGet",         0                 ));

	pComp->Value(mkNamingAdapt(Rotateable,                    "Rotate",             0                 ));
	pComp->Value(mkNamingAdapt(RotatedEntrance,               "RotatedEntrance",    0                 ));
	pComp->Value(mkNamingAdapt(Float,                         "Float",              0                 ));
	pComp->Value(mkNamingAdapt(ColorByOwner,                  "ColorByOwner",       0                 ));
	pComp->Value(mkNamingAdapt(NoHorizontalMove,              "HorizontalFix",      0                 ));
	pComp->Value(mkNamingAdapt(LiftTop,                       "LiftTop",            0                 ));
	pComp->Value(mkNamingAdapt(UprightAttach,                 "UprightAttach",      0                 ));
	pComp->Value(mkNamingAdapt(GrowthType,                    "StretchGrowth",      0                 ));
	pComp->Value(mkNamingAdapt(IncompleteActivity,            "IncompleteActivity", 0                 ));
	pComp->Value(mkNamingAdapt(Oversize,                      "Oversize",           0                 ));
	// <Newton> Fragile and Projectile are kinda obsolete.
	// Only used at one point in the command system. Should rather be solved with properties if at all
	pComp->Value(mkNamingAdapt(Fragile,                       "Fragile",            0                 ));
	pComp->Value(mkNamingAdapt(Projectile,                    "Projectile",         0                 ));

	pComp->Value(mkNamingAdapt(NoPushEnter,                   "NoPushEnter",        0                 ));
	pComp->Value(mkNamingAdapt(VehicleControl,                "VehicleControl",     0                 ));
	pComp->Value(mkNamingAdapt(Pathfinder,                    "Pathfinder",         0                 ));
	pComp->Value(mkNamingAdapt(MoveToRange,                   "MoveToRange",        0                 ));
	pComp->Value(mkNamingAdapt(NoMassFromContents,            "NoMassFromContents", 0                 ));
	pComp->Value(mkNamingAdapt(NoStabilize,                   "NoStabilize",        0                 ));
	pComp->Value(mkNamingAdapt(ClosedContainer,               "ClosedContainer",    0                 ));
	pComp->Value(mkNamingAdapt(SilentCommands,                "SilentCommands",     0                 ));
	pComp->Value(mkNamingAdapt(TemporaryCrew,                 "TemporaryCrew",      0                 ));
	pComp->Value(mkNamingAdapt(BlitMode,                      "BlitMode",           C4D_Blit_Normal   ));
	pComp->Value(mkNamingAdapt(NoBreath,                      "NoBreath",           0                 ));
	pComp->Value(mkNamingAdapt(ConSizeOff,                    "ConSizeOff",         0                 ));
	pComp->Value(mkNamingAdapt(NoGet,                         "NoGet",              0                 ));
	pComp->Value(mkNamingAdapt(NoTransferZones,               "NoTransferZones",    0                 ));

	const StdBitfieldEntry<int32_t> AllowPictureStackModes[] =
	{

		{ "APS_Color",      APS_Color    },
		{ "APS_Graphics",   APS_Graphics },
		{ "APS_Name",       APS_Name     },
		{ "APS_Overlay",    APS_Overlay  },
		{ nullptr,             0            }
	};

	pComp->Value(mkNamingAdapt(mkBitfieldAdapt<int32_t>(AllowPictureStack, AllowPictureStackModes),		//undocumented
	                           "AllowPictureStack",   0                ));
	pComp->Value(mkNamingAdapt(HideInCreator, "HideInCreator", false));
}

//-------------------------------- C4Def -------------------------------------------------------

C4Def::C4Def(): Script(), C4PropListStatic(ScriptEngine.GetPropList(), nullptr, nullptr)
{
	Script.SetDef(this);
	assert(ScriptEngine.GetPropList());
	Graphics.pDef = this;
	Default();
}

void C4Def::Default()
{
	DefaultDefCore();
	Next=nullptr;
	Temporary=false;
	Filename[0]=0;
	Creation=0;
	Count=0;
	MainFace.Set(nullptr,0,0,0,0);
	Script.Clear();
	StringTable.Clear();
	pClonkNames=nullptr;
	pRankNames=nullptr;
	pRankSymbols=nullptr;
	fClonkNamesOwned = fRankNamesOwned = fRankSymbolsOwned = false;
	iNumRankSymbols=1;
	pSolidMask = nullptr;
}

C4Def::~C4Def()
{
	Clear();
}

void C4Def::Clear()
{
	Script.Clear();
	C4PropList::Clear();

	Graphics.Clear();

	StringTable.Clear();
	if (pClonkNames && fClonkNamesOwned) delete pClonkNames; pClonkNames=nullptr;
	if (pRankNames && fRankNamesOwned) delete pRankNames; pRankNames=nullptr;
	if (pRankSymbols && fRankSymbolsOwned) delete pRankSymbols; pRankSymbols=nullptr;
	fClonkNamesOwned = fRankNamesOwned = fRankSymbolsOwned = false;
	delete pSolidMask; pSolidMask = nullptr;
}

bool C4Def::Load(C4Group &hGroup,
	StdMeshSkeletonLoader &loader,
	DWORD dwLoadWhat,
	const char *szLanguage,
	C4SoundSystem *pSoundSystem,
	C4DefGraphicsPtrBackup *gfx_backup
	)
{
	bool AddFileMonitoring = false;
	if (Game.pFileMonitor && !SEqual(hGroup.GetFullName().getData(),Filename) && !hGroup.IsPacked())
		AddFileMonitoring = true;

	// Store filename
	SCopy(hGroup.GetFullName().getData(),Filename);

	// Verbose log filename
	if (Config.Graphics.VerboseObjectLoading>=3)
		Log(hGroup.GetFullName().getData());

	if (AddFileMonitoring) Game.pFileMonitor->AddDirectory(Filename);

	// Pre-read all images and shader stuff because they ar eaccessed in unpredictable order during loading
	hGroup.PreCacheEntries(C4CFN_ShaderFiles);
	hGroup.PreCacheEntries(C4CFN_ImageFiles);

	LoadMeshMaterials(hGroup, gfx_backup);
	bool fSuccess = LoadParticleDef(hGroup);

	// Read DefCore
	if (fSuccess) fSuccess = LoadDefCore(hGroup);

	// Skip def: don't even read sounds!
	if (fSuccess && Game.C4S.Definitions.SkipDefs.GetIDCount(id, 1)) return false;

	// Read sounds, even if not a valid def (for pure ocd sound folders)
	if (dwLoadWhat & C4D_Load_Sounds) LoadSounds(hGroup, pSoundSystem);

	// cancel if not a valid definition
	if (!fSuccess) return false;

	// Read and parse SolidMask bitmap
	if (!LoadSolidMask(hGroup)) return false;

	// Read surface bitmap, meshes, skeletons
	if ((dwLoadWhat & C4D_Load_Bitmap) && !LoadGraphics(hGroup, loader)) return false;

	// Read string table
	C4Language::LoadComponentHost(&StringTable, hGroup, C4CFN_ScriptStringTbl, szLanguage);

	// Register ID with script engine
	::ScriptEngine.RegisterGlobalConstant(id.ToString(), C4VPropList(this));
	ParentKeyName = ::Strings.RegString(id.ToString());

	// Read script
	if (dwLoadWhat & C4D_Load_Script) LoadScript(hGroup, szLanguage);

	// Read clonknames
	if (dwLoadWhat & C4D_Load_ClonkNames) LoadClonkNames(hGroup, pClonkNames, szLanguage);

	// Read clonkranks
	if (dwLoadWhat & C4D_Load_RankNames) LoadRankNames(hGroup, szLanguage);

	// Read rankfaces
	if (dwLoadWhat & C4D_Load_RankFaces) LoadRankFaces(hGroup);

	// Temporary flag
	if (dwLoadWhat & C4D_Load_Temporary) Temporary=true;

	return true;
}

void C4Def::LoadMeshMaterials(C4Group &hGroup, C4DefGraphicsPtrBackup *gfx_backup)
{
	// Load all mesh materials from this folder
	C4DefAdditionalResourcesLoader loader(hGroup);
	hGroup.ResetSearch();
	char MaterialFilename[_MAX_PATH + 1]; *MaterialFilename = 0;
	
	for (const auto &mat : mesh_materials)
	{
		::MeshMaterialManager.Remove(mat, &gfx_backup->GetUpdater());
	}
	mesh_materials.clear();
	while (hGroup.FindNextEntry(C4CFN_DefMaterials, MaterialFilename, nullptr, !!*MaterialFilename))
	{
		StdStrBuf material;
		if (hGroup.LoadEntryString(MaterialFilename, &material))
		{
			try
			{
				StdStrBuf buf;
				buf.Copy(hGroup.GetName());
				buf.Append("/"); buf.Append(MaterialFilename);
				auto new_materials = ::MeshMaterialManager.Parse(material.getData(), buf.getData(), loader);
				mesh_materials.insert(new_materials.begin(), new_materials.end());
			}
			catch (const StdMeshMaterialError& ex)
			{
				DebugLogF("Failed to read material script: %s", ex.what());
			}
		}
	}
}

bool C4Def::LoadParticleDef(C4Group &hGroup)
{
	bool fSuccess = true;
	// particle def?
	if (hGroup.AccessEntry(C4CFN_ParticleCore))
	{
		// def loading not successful; abort after reading sounds
		fSuccess = false;
		// create new particle def
		C4ParticleDef *pParticleDef = new C4ParticleDef();
		// load it
		if (!pParticleDef->Load(hGroup))
		{
			// not successful :( - destroy it again
			delete pParticleDef;
		}
		// done
	}
	return fSuccess;
}

bool C4Def::LoadSolidMask(C4Group &hGroup)
{
	if (hGroup.FindEntry(C4CFN_SolidMask))
	{
		pSolidMask = C4SolidMask::LoadMaskFromFile(hGroup, C4CFN_SolidMask);
		if (!pSolidMask)
		{
			DebugLogF("  Error loading SolidMask of %s (%s)", hGroup.GetFullName().getData(), id.ToString());
			return false;
		}
		// check SolidMask size
		if (SolidMask.x<0 || SolidMask.y<0 || SolidMask.x + SolidMask.Wdt>pSolidMask->Wdt || SolidMask.y + SolidMask.Hgt>pSolidMask->Hgt) SolidMask.Default();
	}
	else if (SolidMask.Wdt)
	{
		// Warning in case someone wants to define SolidMasks the old way (in the main graphics file)
		DebugLogF("WARNING: Definition %s (%s) defines SolidMask in DefCore but has no SolidMask file!", hGroup.GetFullName().getData(), id.ToString());
		SolidMask.Default();
	}

	return true;
}

bool C4Def::LoadGraphics(C4Group &hGroup, StdMeshSkeletonLoader &loader)
{
	// Try to load graphics
	// No fail on error - just have an object without graphics.
	Graphics.Load(hGroup, loader, !!ColorByOwner);

	if (Graphics.Type == C4DefGraphics::TYPE_Bitmap)
	{
		// Bitmap post-load settings
		if (Graphics.GetBitmap())
		{
			// Set MainFace (unassigned bitmap: will be set by GetMainFace())
			MainFace.Set(nullptr, 0, 0, Shape.Wdt, Shape.Hgt);
		}

		// Adjust picture rect
		if ((PictureRect.Wdt == 0) || (PictureRect.Hgt == 0))
			PictureRect.Set(0, 0, Shape.Wdt*Graphics.Bmp.Bitmap->Scale, Shape.Hgt*Graphics.Bmp.Bitmap->Scale);

		// validate TopFace
		if (TopFace.x<0 || TopFace.y<0 || TopFace.x + TopFace.Wdt>Graphics.Bmp.Bitmap->Wdt || TopFace.y + TopFace.Hgt>Graphics.Bmp.Bitmap->Hgt)
		{
			TopFace.Default();
			// warn in debug mode
			DebugLogF("invalid TopFace in %s (%s)", GetName(), id.ToString());
		}
	}
	else
	{
		TopFace.Default();
	}

	return true;
}

void C4Def::LoadScript(C4Group &hGroup, const char* szLanguage)
{
	// reg script to engine
	Script.Reg2List(&::ScriptEngine);
	// Load script
	Script.Load(hGroup, C4CFN_Script, szLanguage, &StringTable);
}

void C4Def::LoadClonkNames(C4Group &hGroup, C4ComponentHost* pClonkNames, const char* szLanguage)
{
	// clear any previous
	if (pClonkNames) delete pClonkNames; pClonkNames = nullptr;
	if (hGroup.FindEntry(C4CFN_ClonkNameFiles))
	{
		// create new
		pClonkNames = new C4ComponentHost();
		if (!C4Language::LoadComponentHost(pClonkNames, hGroup, C4CFN_ClonkNames, szLanguage))
		{
			delete pClonkNames; pClonkNames = nullptr;
		}
		else
			fClonkNamesOwned = true;
	}
}

void C4Def::LoadRankNames(C4Group &hGroup, const char* szLanguage)
{
	// clear any previous
	if (pRankNames) delete pRankNames; pRankNames = nullptr;
	if (hGroup.FindEntry(C4CFN_RankNameFiles))
	{
		// create new
		pRankNames = new C4RankSystem();
		// load from group
		if (!pRankNames->Load(hGroup, C4CFN_RankNames, 1000, szLanguage))
		{
			delete pRankNames; pRankNames = nullptr;
		}
		else
			fRankNamesOwned = true;
	}
}

void C4Def::LoadRankFaces(C4Group &hGroup)
{
	// clear any previous
	if (pRankSymbols) delete pRankSymbols; pRankSymbols = nullptr;
	// load new
	if (hGroup.AccessEntry(C4CFN_RankFacesPNG))
	{
		pRankSymbols = new C4FacetSurface();
		if (!pRankSymbols->GetFace().ReadPNG(hGroup, false)) { delete pRankSymbols; pRankSymbols = nullptr; }
	}
	// set size
	if (pRankSymbols)
	{
		pRankSymbols->Set(&pRankSymbols->GetFace(), 0, 0, pRankSymbols->GetFace().Hgt, pRankSymbols->GetFace().Hgt);
		int32_t Q; pRankSymbols->GetPhaseNum(iNumRankSymbols, Q);
		if (!iNumRankSymbols) { delete pRankSymbols; pRankSymbols = nullptr; }
		else
		{
			if (pRankNames)
			{
				// if extended rank names are defined, subtract those from the symbol count. The last symbols are used as overlay
				iNumRankSymbols = std::max<int32_t>(1, iNumRankSymbols - pRankNames->GetExtendedRankNum());
			}
			fRankSymbolsOwned = true;
		}
	}
}

void C4Def::LoadSounds(C4Group &hGroup, C4SoundSystem* pSoundSystem)
{
	if (pSoundSystem)
		pSoundSystem->LoadEffects(hGroup, (id == C4ID::None) ? nullptr : id.ToString(), true);
}

void C4Def::Draw(C4Facet &cgo, bool fSelected, DWORD iColor, C4Object *pObj, int32_t iPhaseX, int32_t iPhaseY, C4DrawTransform* trans, const char *graphicsName)
{
	if(fSelected)
		pDraw->DrawBoxDw(cgo.Surface, cgo.X, cgo.Y, cgo.X + cgo.Wdt - 1, cgo.Y + cgo.Hgt - 1, C4RGB(0xca, 0, 0));

	C4DefGraphics* graphics = pObj ? pObj->GetGraphics() : &Graphics;
	if (graphicsName)
	{
		C4DefGraphics *other = graphics->Get(graphicsName);
		if (other) graphics = other;
	}
	graphics->Draw(cgo, iColor, pObj, iPhaseX, iPhaseY, trans);
}

int32_t C4Def::GetValue(C4Object *pInBase, int32_t iBuyPlayer)
{
	C4Value r = Call(PSF_CalcDefValue, &C4AulParSet(pInBase, iBuyPlayer));
	int32_t iValue = Value;
	if (r != C4VNull)
		iValue = r.getInt();
	// do any adjustments based on where the item is bought
	if (pInBase)
	{
		r = pInBase->Call(PSF_CalcBuyValue, &C4AulParSet(this, iValue));
		if (r != C4VNull)
			iValue = r.getInt();
	}
	return iValue;
}

void C4Def::Synchronize()
{
}

void C4Def::IncludeDefinition(C4Def *pIncludeDef)
{
	// inherited rank infos and clonk names, if this definition doesn't have its own
	if (!fClonkNamesOwned) pClonkNames = pIncludeDef->pClonkNames;
	if (!fRankNamesOwned) pRankNames = pIncludeDef->pRankNames;
	if (!fRankSymbolsOwned) { pRankSymbols = pIncludeDef->pRankSymbols; iNumRankSymbols = pIncludeDef->iNumRankSymbols; }
}

void C4Def::ResetIncludeDependencies()
{
	// clear all pointers into foreign defs
	if (!fClonkNamesOwned) pClonkNames = nullptr;
	if (!fRankNamesOwned) pRankNames = nullptr;
	if (!fRankSymbolsOwned) { pRankSymbols = nullptr; iNumRankSymbols = 0; }
}

C4PropList *C4Def::GetActionByName(const char *actname)
{
	if (!actname) return nullptr;
	C4String * actname_str = Strings.RegString(actname);
	actname_str->IncRef();
	C4PropList *r = GetActionByName(actname_str);
	actname_str->DecRef();
	return r;
}

C4PropList *C4Def::GetActionByName(C4String *actname)
{
	assert(actname);
	// If we get the null string or ActIdle by name, return nullptr action
	if (!actname || actname == &Strings.P[P_Idle]) return nullptr;
	// otherwise, query actmap
	C4Value ActMap; GetProperty(P_ActMap, &ActMap);
	if (!ActMap.getPropList()) return nullptr;
	C4Value Action; ActMap.getPropList()->GetPropertyByS(actname, &Action);
	if (!Action.getPropList()) return nullptr;
	return Action.getPropList();
}
