openclonk/engine/src/C4InfoCore.cpp

601 lines
18 KiB
C++

/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 1998-2000, 2003, 2006-2007 Matthes Bender
* Copyright (c) 2004, 2006 Günther Brammer
* Copyright (c) 2005-2008 Sven Eberhardt
* Copyright (c) 2006 Peter Wortmann
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
*
* Portions might be copyrighted by other authors who have contributed
* to OpenClonk.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
* See isc_license.txt for full license and disclaimer.
*
* "Clonk" is a registered trademark of Matthes Bender.
* See clonk_trademark_license.txt for full license.
*/
/* Structures for object and player info components */
#include <C4Include.h>
#include <C4InfoCore.h>
#ifndef BIG_C4INCLUDE
#include <C4Random.h>
#include <C4RankSystem.h>
#include <C4Group.h>
#include <C4Components.h>
#include <C4Game.h>
#include <C4GameObjects.h>
#endif
#include <C4Random.h>
#if defined(C4FRONTEND) || defined(C4GROUP)
#include "C4CompilerWrapper.h"
#include "C4Def.h"
#endif
//------------------------------- Player Info ----------------------------------------
C4PlayerInfoCore::C4PlayerInfoCore()
{
ZeroMem(this,sizeof(C4PlayerInfoCore));
Default();
}
void C4PlayerInfoCore::Default(C4RankSystem *pRanks)
{
ZeroMem(this,sizeof(C4PlayerInfoCore));
Rank=0;
SCopy("Neuling",PrefName);
if (pRanks) SCopy(pRanks->GetRankName(Rank,false).getData(),RankName);
else SCopy("Rang",RankName);
PrefColor=0;
PrefColorDw=0xff;
PrefColor2Dw=0;
PrefControl=C4P_Control_Keyboard1;
PrefPosition=0;
PrefMouse=1;
PrefControlStyle = 0;
PrefAutoContextMenu = 0;
ExtraData.Reset();
}
DWORD C4PlayerInfoCore::GetPrefColorValue(int32_t iPrefColor)
{
DWORD valRGB[12] = { 0x0000E8, 0xF40000, 0x00C800, 0xFCF41C,
0xC48444, 0x784830, 0xA04400, 0xF08050,
0x848484, 0xFFFFFF, 0x0094F8, 0xBC00C0 };
if (Inside<int32_t>(iPrefColor, 0, 11))
return valRGB[iPrefColor];
//return RGB(valRGB[iPrefColor] >> 16, valRGB[iPrefColor] >> 8, valRGB[iPrefColor] >> 0);
return 0xAAAAAA;
}
BOOL C4PlayerInfoCore::Load(C4Group &hGroup)
{
// New version
StdStrBuf Source;
if (hGroup.LoadEntryString(C4CFN_PlayerInfoCore,Source))
{
// Compile
StdStrBuf GrpName = hGroup.GetFullName(); GrpName.Append(DirSep C4CFN_PlayerInfoCore);
if(!CompileFromBuf_LogWarn<StdCompilerINIRead>(*this, Source, GrpName.getData()))
return FALSE;
// Pref for AutoContextMenus is still undecided: default by player's control style
if (PrefAutoContextMenu == -1)
PrefAutoContextMenu = PrefControlStyle;
// Determine true color from indexed pref color
if (!PrefColorDw)
PrefColorDw = GetPrefColorValue(PrefColor);
// Validate colors
PrefColorDw &= 0xffffff;
PrefColor2Dw &= 0xffffff;
// Validate name
#ifdef C4ENGINE
CMarkup::StripMarkup(PrefName);
#endif
// Success
return TRUE;
}
// Old version no longer supported - sorry
return FALSE;
}
BOOL C4PlayerInfoCore::Save(C4Group &hGroup)
{
StdStrBuf Source, Name = hGroup.GetFullName(); Name.Append(DirSep C4CFN_PlayerInfoCore);
if(!DecompileToBuf_Log<StdCompilerINIWrite>(*this, &Source, Name.getData()))
return FALSE;
if (!hGroup.Add(C4CFN_PlayerInfoCore,Source,FALSE,TRUE))
return FALSE;
hGroup.Delete("C4Player.c4b");
return TRUE;
}
void C4PlayerInfoCore::Promote(int32_t iRank, C4RankSystem &rRanks)
{
Rank=iRank;
SCopy(rRanks.GetRankName(Rank,true).getData(),RankName,C4MaxName);
}
BOOL C4PlayerInfoCore::CheckPromotion(C4RankSystem &rRanks)
{
if (Score>=rRanks.Experience(Rank+1))
{
Promote(Rank+1,rRanks);
return TRUE;
}
return FALSE;
}
void C4PlayerInfoCore::CompileFunc(StdCompiler *pComp)
{
pComp->Name("Player");
pComp->Value(mkNamingAdapt(toC4CStr(PrefName),"Name", "Neuling"));
pComp->Value(mkNamingAdapt(toC4CStr(Comment), "Comment", ""));
pComp->Value(mkNamingAdapt(Rank, "Rank", 0));
pComp->Value(mkNamingAdapt(toC4CStr(RankName),"RankName", /*pRanks ? pRanks->Name(Rank,false) :*/ LoadResStr("IDS_MSG_RANK"))); // TODO: check if this would be desirable
pComp->Value(mkNamingAdapt(Score, "Score", 0));
pComp->Value(mkNamingAdapt(Rounds, "Rounds", 0));
pComp->Value(mkNamingAdapt(RoundsWon, "RoundsWon", 0));
pComp->Value(mkNamingAdapt(RoundsLost, "RoundsLost", 0));
pComp->Value(mkNamingAdapt(TotalPlayingTime, "TotalPlayingTime", 0));
pComp->Value(mkNamingAdapt(ExtraData, "ExtraData", C4ValueMapData()));
pComp->Value(mkNamingAdapt(toC4CStr(LeagueName),"LeagueName", ""));
pComp->NameEnd();
pComp->Name("Preferences");
pComp->Value(mkNamingAdapt(PrefColor, "Color", 0));
pComp->Value(mkNamingAdapt(PrefColorDw, "ColorDw", 0xffu));
pComp->Value(mkNamingAdapt(PrefColor2Dw, "AlternateColorDw", 0u));
pComp->Value(mkNamingAdapt(PrefControl, "Control", C4P_Control_Keyboard2));
pComp->Value(mkNamingAdapt(PrefControlStyle, "AutoStopControl", 0));
pComp->Value(mkNamingAdapt(PrefAutoContextMenu, "AutoContextMenu", -1)); // compiling default is -1 (if this is detected, AutoContextMenus will be defaulted by control style)
pComp->Value(mkNamingAdapt(PrefPosition, "Position", 0));
pComp->Value(mkNamingAdapt(PrefMouse, "Mouse", 1));
pComp->NameEnd();
pComp->Value(mkNamingAdapt(LastRound, "LastRound" ));
}
//------------------------------- Physical Info ----------------------------------------
struct C4PhysInfoNameMap_t { const char *szName; C4PhysicalInfo::Offset off; } C4PhysInfoNameMap[] = {
{ "Energy", &C4PhysicalInfo::Energy },
{ "Breath", &C4PhysicalInfo::Breath },
{ "Walk", &C4PhysicalInfo::Walk },
{ "Jump", &C4PhysicalInfo::Jump },
{ "Scale", &C4PhysicalInfo::Scale },
{ "Hangle", &C4PhysicalInfo::Hangle },
{ "Dig", &C4PhysicalInfo::Dig },
{ "Swim", &C4PhysicalInfo::Swim },
{ "Throw", &C4PhysicalInfo::Throw },
{ "Push", &C4PhysicalInfo::Push },
{ "Fight", &C4PhysicalInfo::Fight },
{ "Magic", &C4PhysicalInfo::Magic },
{ "Float", &C4PhysicalInfo::Float },
{ "CanScale", &C4PhysicalInfo::CanScale },
{ "CanHangle", &C4PhysicalInfo::CanHangle },
{ "CanDig", &C4PhysicalInfo::CanDig },
{ "CanConstruct", &C4PhysicalInfo::CanConstruct },
{ "CanChop", &C4PhysicalInfo::CanChop },
{ "CanSwimDig", &C4PhysicalInfo::CanSwimDig },
{ "CanFly", &C4PhysicalInfo::CanFly },
{ "CorrosionResist", &C4PhysicalInfo::CorrosionResist },
{ "BreatheWater", &C4PhysicalInfo::BreatheWater },
{ NULL, NULL } };
void C4PhysicalInfo::PromotionUpdate(int32_t iRank, bool fUpdateTrainablePhysicals, C4Def *pTrainDef)
{
#ifdef C4ENGINE
if (iRank>=0) { CanDig=1; CanChop=1; CanConstruct=1; }
if (iRank>=0) { CanScale=1; }
if (iRank>=0) { CanHangle=1; }
Energy= Max<int32_t>( Energy, (50+5*BoundBy<int32_t>(iRank,0,10)) *C4MaxPhysical/100 );
if (fUpdateTrainablePhysicals && pTrainDef)
{
// do standard training: Expect everything to be trained fully at rank 20
int32_t iTrainRank = BoundBy<int32_t>(iRank, 0,20);
Scale = pTrainDef->Physical.Scale + (C4MaxPhysical - pTrainDef->Physical.Scale) * iTrainRank / 20;
Hangle = pTrainDef->Physical.Hangle + (C4MaxPhysical - pTrainDef->Physical.Hangle) * iTrainRank / 20;
Swim = pTrainDef->Physical.Swim + (C4MaxPhysical - pTrainDef->Physical.Swim) * iTrainRank / 20;
Fight = pTrainDef->Physical.Fight + (C4MaxPhysical - pTrainDef->Physical.Fight) * iTrainRank / 20;
// do script updates for any physicals as required (this will train stuff like magic)
const char *szPhysName; C4PhysicalInfo::Offset PhysOff;
for (int32_t iPhysIdx=0; szPhysName = GetNameByIndex(iPhysIdx, &PhysOff); ++iPhysIdx)
{
C4Value PhysVal(this->*PhysOff, C4V_Int);
C4AulParSet Pars(C4VString(szPhysName), C4VInt(iRank), C4VRef(&PhysVal));
if (!!pTrainDef->Script.Call(PSF_GetFairCrewPhysical, 0, &Pars))
{
this->*PhysOff = PhysVal.getInt();
}
}
}
#endif
}
C4PhysicalInfo::C4PhysicalInfo()
{
Default();
}
void C4PhysicalInfo::Default()
{
ZeroMem(this,sizeof(C4PhysicalInfo));
}
bool C4PhysicalInfo::GetOffsetByName(const char *szPhysicalName, Offset *pmpiOut)
{
// query map
for (C4PhysInfoNameMap_t *entry = C4PhysInfoNameMap; entry->szName; ++entry)
if (SEqual(entry->szName, szPhysicalName))
{
*pmpiOut = entry->off;
return true;
}
return false;
}
const char *C4PhysicalInfo::GetNameByOffset(Offset mpiOff)
{
// query map
for (C4PhysInfoNameMap_t *entry = C4PhysInfoNameMap; entry->szName; ++entry)
if (entry->off == mpiOff)
return entry->szName;
return NULL;
}
const char *C4PhysicalInfo::GetNameByIndex(int32_t iIdx, Offset *pmpiOut)
{
// query map
if (!Inside<int32_t>(iIdx, 0, sizeof(C4PhysInfoNameMap)/sizeof(C4PhysInfoNameMap_t))) return NULL;
if (pmpiOut) *pmpiOut = C4PhysInfoNameMap[iIdx].off;
return C4PhysInfoNameMap[iIdx].szName;
}
void C4PhysicalInfo::CompileFunc(StdCompiler *pComp)
{
for (C4PhysInfoNameMap_t *entry = C4PhysInfoNameMap; entry->szName; ++entry)
pComp->Value(mkNamingAdapt((this->*(entry->off)), entry->szName, 0));
}
void C4PhysicalInfo::TrainValue(int32_t *piVal, int32_t iTrainBy, int32_t iMaxTrain)
{
// only do training if value was nonzero before (e.g., Magic for revaluated Clonks)
if (*piVal)
// do train value: Do not increase above maximum, but never decrease either
*piVal = Max(Min(*piVal + iTrainBy, iMaxTrain), *piVal);
}
void C4PhysicalInfo::Train(Offset mpiOffset, int32_t iTrainBy, int32_t iMaxTrain)
{
// train own value
TrainValue(&(this->*mpiOffset), iTrainBy, iMaxTrain);
}
bool C4PhysicalInfo::operator ==(const C4PhysicalInfo &cmp) const
{
// all fields must be equal
for (C4PhysInfoNameMap_t *entry = C4PhysInfoNameMap; entry->szName; ++entry)
if (this->*(entry->off) != cmp.*(entry->off))
return false;
return true;
}
void C4TempPhysicalInfo::CompileFunc(StdCompiler *pComp)
{
C4PhysicalInfo::CompileFunc(pComp);
#ifdef C4ENGINE
pComp->Value(mkNamingAdapt( mkSTLContainerAdapt(Changes), "Changes", std::vector<C4PhysicalChange>()));
#endif
}
void C4TempPhysicalInfo::Train(Offset mpiOffset, int32_t iTrainBy, int32_t iMaxTrain)
{
// train own value
C4PhysicalInfo::Train(mpiOffset, iTrainBy, iMaxTrain);
// train all temp values
#ifdef C4ENGINE
for (std::vector<C4PhysicalChange>::iterator i = Changes.begin(); i != Changes.end(); ++i)
if (i->mpiOffset == mpiOffset)
TrainValue(&(i->PrevVal), iTrainBy, iMaxTrain);
#endif
}
bool C4TempPhysicalInfo::HasChanges(C4PhysicalInfo *pRefPhysical)
{
#ifdef C4ENGINE
// always return true if there are temp changes
if (!Changes.empty()) return true;
// also return true if any value deviates from the reference
if (pRefPhysical)
{
if (!(*pRefPhysical == *this)) return true;
}
#endif
// no change known
return false;
}
void C4TempPhysicalInfo::RegisterChange(C4PhysicalInfo::Offset mpiOffset)
{
// append physical change to list
#ifdef C4ENGINE
Changes.push_back(C4PhysicalChange(this->*mpiOffset, mpiOffset));
#endif
}
bool C4TempPhysicalInfo::ResetPhysical(C4PhysicalInfo::Offset mpiOffset)
{
#ifdef C4ENGINE
// search last matching physical check (should always be last if well scripted)
for (std::vector<C4PhysicalChange>::reverse_iterator i = Changes.rbegin(); i != Changes.rend(); ++i)
if ((*i).mpiOffset == mpiOffset)
{
this->*mpiOffset = (*i).PrevVal;
Changes.erase((i+1).base());
return true;
}
#endif
return false;
}
void C4PhysicalChange::CompileFunc(StdCompiler *pComp)
{
// name=oldval
char phyn[C4MaxName+1];
const char *szPhyn = C4PhysicalInfo::GetNameByOffset(mpiOffset);
if (szPhyn) SCopy(szPhyn, phyn, C4MaxName); else *phyn='\0';
pComp->Value(mkStringAdapt(phyn, C4MaxName, StdCompiler::RCT_Idtf));
if (!C4PhysicalInfo::GetOffsetByName(phyn, &mpiOffset)) pComp->excNotFound("Physical change name \"%s\" not found.");
pComp->Seperator(StdCompiler::SEP_SET);
pComp->Value(PrevVal);
}
//------------------------------- Object Info ----------------------------------------
C4ObjectInfoCore::C4ObjectInfoCore()
{
Default();
}
void C4ObjectInfoCore::Default(C4ID n_id,
C4DefList *pDefs,
const char *cpNames)
{
// Def
C4Def *pDef=NULL;
if (pDefs) pDef = pDefs->ID2Def(n_id);
// Defaults
id=n_id;
Participation=1;
Rank=0;
Experience=0;
Rounds=0;
DeathCount=0;
Birthday=0;
TotalPlayingTime=0;
SCopy("Clonk",Name,C4MaxName);
SCopy("Clonk",TypeName,C4MaxName);
sRankName.Copy("Clonk");
sNextRankName.Clear();
NextRankExp=0;
DeathMessage[0]='\0';
*PortraitFile=0;
Age=0;
ExtraData.Reset();
// Type
if (pDef) SCopy(pDef->GetName(),TypeName,C4MaxName);
// Name
if (cpNames)
{
SCopySegment(cpNames,Random(SCharCount(0x0A,cpNames)),Name,0x0A,C4MaxName+1);
SClearFrontBack(Name);
SReplaceChar(Name,0x0D,0x00);
if (!Name[0]) SCopy("Clonk",Name,C4MaxName);
}
#ifdef C4ENGINE
if (pDefs) UpdateCustomRanks(pDefs);
#endif
// Physical
Physical.Default();
if (pDef) Physical = pDef->Physical;
Physical.PromotionUpdate(Rank);
// Old format
}
void C4ObjectInfoCore::Promote(int32_t iRank, C4RankSystem &rRanks, bool fForceRankName)
{
Rank=iRank;
Physical.PromotionUpdate(Rank);
// copy new rank name if defined only, or forced to use highest defined rank for too high info ranks
StdStrBuf sNewRank(rRanks.GetRankName(Rank,fForceRankName));
if (sNewRank) sRankName.Copy(sNewRank);
}
#ifdef C4ENGINE
void C4ObjectInfoCore::UpdateCustomRanks(C4DefList *pDefs)
{
assert(pDefs);
C4Def *pDef = pDefs->ID2Def(id);
if (!pDef) return;
if (pDef->pRankNames)
{
StdStrBuf sRank(pDef->pRankNames->GetRankName(Rank,false));
if (sRank) sRankName.Copy(sRank);
// next rank data
StdStrBuf sNextRank(pDef->pRankNames->GetRankName(Rank+1,false));
if (sNextRank)
{
sNextRankName.Copy(sNextRank);
NextRankExp = pDef->pRankNames->Experience(Rank+1);
}
else
{
// no more promotion possible by custom rank system
sNextRankName.Clear();
NextRankExp = C4RankSystem::EXP_NoPromotion;
}
}
else
{
// definition does not have custom rank names
sNextRankName.Clear();
NextRankExp = 0;
}
}
#endif
bool C4ObjectInfoCore::GetNextRankInfo(C4RankSystem &rDefaultRanks, int32_t *piNextRankExp, StdStrBuf *psNextRankName)
{
int32_t iNextRankExp;
// custom rank assigned?
if (NextRankExp)
{
iNextRankExp = NextRankExp;
if (psNextRankName) psNextRankName->Copy(sNextRankName);
}
else
{
// no custom rank: Get from default set
StdStrBuf sRank(rDefaultRanks.GetRankName(Rank+1, false));
if (sRank)
{
iNextRankExp = rDefaultRanks.Experience(Rank+1);
if (psNextRankName) psNextRankName->Copy(sRank);
}
else
// no more promotion
iNextRankExp = C4RankSystem::EXP_NoPromotion;
}
// return result
if (piNextRankExp) *piNextRankExp = iNextRankExp;
// return value is whether additional promotion is possible
return iNextRankExp != C4RankSystem::EXP_NoPromotion;
}
BOOL C4ObjectInfoCore::Load(C4Group &hGroup)
{
StdStrBuf Source;
return hGroup.LoadEntryString(C4CFN_ObjectInfoCore, Source) &&
Compile(Source.getData());
}
BOOL C4ObjectInfoCore::Save(C4Group &hGroup, C4DefList *pDefs)
{
#ifdef C4ENGINE
// rank overload by def: Update any NextRank-stuff
if (pDefs) UpdateCustomRanks(pDefs);
#endif
char *Buffer; size_t BufferSize;
if (!Decompile(&Buffer,&BufferSize))
return FALSE;
if (!hGroup.Add(C4CFN_ObjectInfoCore,Buffer,BufferSize,FALSE,TRUE) )
{ delete [] Buffer; return FALSE; }
return TRUE;
}
void C4ObjectInfoCore::CompileFunc(StdCompiler *pComp)
{
pComp->Value(mkNamingAdapt(mkC4IDAdapt(id), "id", C4ID_None));
pComp->Value(mkNamingAdapt(toC4CStr(Name), "Name", "Clonk"));
pComp->Value(mkNamingAdapt(toC4CStr(DeathMessage), "DeathMessage", ""));
pComp->Value(mkNamingAdapt(toC4CStr(PortraitFile), "PortraitFile", ""));
pComp->Value(mkNamingAdapt(Rank, "Rank", 0));
pComp->Value(mkNamingAdapt(sRankName, "RankName", "Clonk"));
pComp->Value(mkNamingAdapt(sNextRankName, "NextRankName", ""));
pComp->Value(mkNamingAdapt(toC4CStr(TypeName), "TypeName", "Clonk"));
pComp->Value(mkNamingAdapt(Participation, "Participation", 1));
pComp->Value(mkNamingAdapt(Experience, "Experience", 0));
pComp->Value(mkNamingAdapt(NextRankExp, "NextRankExp", 0));
pComp->Value(mkNamingAdapt(Rounds, "Rounds", 0));
pComp->Value(mkNamingAdapt(DeathCount, "DeathCount", 0));
pComp->Value(mkNamingAdapt(Birthday, "Birthday", 0));
pComp->Value(mkNamingAdapt(TotalPlayingTime, "TotalPlayingTime", 0));
pComp->Value(mkNamingAdapt(Age, "Age", 0));
pComp->Value(mkNamingAdapt(ExtraData, "ExtraData", C4ValueMapData()));
pComp->FollowName("Physical");
pComp->Value(Physical);
}
BOOL C4ObjectInfoCore::Compile(const char *szSource)
{
bool ret = CompileFromBuf_LogWarn<StdCompilerINIRead>(
mkNamingAdapt(*this, "ObjectInfo"),
StdStrBuf(szSource),
"ObjectInfo");
// Do a promotion update to set physicals right
Physical.PromotionUpdate(Rank);
// DeathMessages are not allowed to stay forever
if ('@' == DeathMessage[0]) DeathMessage[0] = ' ';
return ret;
}
BOOL C4ObjectInfoCore::Decompile(char **ppOutput, size_t *ipSize)
{
StdStrBuf Buf;
if(!DecompileToBuf_Log<StdCompilerINIWrite>(
mkNamingAdapt(*this, "ObjectInfo"),
&Buf,
"ObjectInfo"))
{
if(ppOutput) *ppOutput = NULL;
if(ipSize) *ipSize = 0;
return FALSE;
}
if(ppOutput) *ppOutput = Buf.GrabPointer();
if(ipSize) *ipSize = Buf.getSize();
return TRUE;
}
//------------------------------- Round Info ------------------------------------------
C4RoundResult::C4RoundResult()
{
Default();
}
void C4RoundResult::Default()
{
ZeroMem(this,sizeof(C4RoundResult));
}
void C4RoundResult::CompileFunc(StdCompiler *pComp)
{
pComp->Value(mkNamingAdapt(Title, "Title", ""));
pComp->Value(mkNamingAdapt(Date, "Date", 0u));
pComp->Value(mkNamingAdapt(Duration, "Duration", 0));
pComp->Value(mkNamingAdapt(Won, "Won", 0));
pComp->Value(mkNamingAdapt(Score, "Score", 0));
pComp->Value(mkNamingAdapt(FinalScore, "FinalScore", 0));
pComp->Value(mkNamingAdapt(TotalScore, "TotalScore", 0));
pComp->Value(mkNamingAdapt(Bonus, "Bonus", 0));
pComp->Value(mkNamingAdapt(Level, "Level", 0));
}