1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 1998-2000, Matthes Bender
5  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
6  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
7  *
8  * Distributed under the terms of the ISC license; see accompanying file
9  * "COPYING" for details.
10  *
11  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12  * See accompanying file "TRADEMARK" for details.
13  *
14  * To redistribute this file separately, substitute the full license texts
15  * for the above references.
16  */
17 
18 /* Holds crew member information */
19 
20 #include "C4Include.h"
21 #include "object/C4ObjectInfo.h"
22 
23 #include "c4group/C4Components.h"
24 #include "game/C4Application.h"
25 #include "graphics/C4GraphicsResource.h"
26 #include "lib/C4Random.h"
27 #include "object/C4Def.h"
28 #include "object/C4DefList.h"
29 #include "player/C4Player.h"
30 #include "player/C4PlayerList.h"
31 #include "player/C4RankSystem.h"
32 
33 #ifdef _MSC_VER
34 #define snprintf _snprintf
35 #endif
36 
C4ObjectInfo()37 C4ObjectInfo::C4ObjectInfo()
38 {
39 	Default();
40 }
41 
~C4ObjectInfo()42 C4ObjectInfo::~C4ObjectInfo()
43 {
44 	Clear();
45 }
46 
Default()47 void C4ObjectInfo::Default()
48 {
49 	WasInAction=false;
50 	InAction=false;
51 	InActionTime=0;
52 	HasDied=false;
53 	ControlCount=0;
54 	Filename[0]=0;
55 	Next=nullptr;
56 	pDef = nullptr;
57 }
58 
Load(C4Group & hMother,const char * szEntryname)59 bool C4ObjectInfo::Load(C4Group &hMother, const char *szEntryname)
60 {
61 
62 	// New version
63 	if (SEqualNoCase(GetExtension(szEntryname),"oci"))
64 	{
65 		C4Group hChild;
66 		if (hChild.OpenAsChild(&hMother,szEntryname))
67 		{
68 			if (!C4ObjectInfo::Load(hChild))
69 				{ hChild.Close(); return false; }
70 			// resolve definition, if possible
71 			// only works in game, but is not needed in frontend or startup editing anyway
72 			pDef = C4Id2Def(id);
73 			hChild.Close();
74 			return true;
75 		}
76 	}
77 
78 	return false;
79 }
80 
Load(C4Group & hGroup)81 bool C4ObjectInfo::Load(C4Group &hGroup)
82 {
83 	// Store group file name
84 	SCopy(GetFilename(hGroup.GetName()),Filename,_MAX_FNAME);
85 	// Load core
86 	if (!C4ObjectInfoCore::Load(hGroup)) return false;
87 	return true;
88 }
89 
Save(C4Group & hGroup,bool fStoreTiny,C4DefList * pDefs)90 bool C4ObjectInfo::Save(C4Group &hGroup, bool fStoreTiny, C4DefList *pDefs)
91 {
92 	// Set group file name; rename if necessary
93 	char szTempGroup[_MAX_PATH+1];
94 	SCopy(Name, szTempGroup, _MAX_PATH);
95 	MakeFilenameFromTitle(szTempGroup);
96 	SAppend(".oci",szTempGroup, _MAX_PATH);
97 	if (!SEqualNoCase(Filename, szTempGroup))
98 	{
99 		if (!Filename[0])
100 		{
101 			// first time creation of file - make sure it's not a duplicate
102 			SCopy(szTempGroup, Filename, _MAX_PATH);
103 			while (hGroup.FindEntry(Filename))
104 			{
105 				// if a crew info of that name exists already, rename!
106 				RemoveExtension(Filename);
107 				int32_t iFinNum = GetTrailingNumber(Filename), iLen = SLen(Filename);
108 				while (iLen && Inside(Filename[iLen-1], '0', '9')) --iLen;
109 				if (iLen>_MAX_PATH-22) { LogF("Error generating unique filename for %s(%s): Path overflow", Name, hGroup.GetFullName().getData()); break; }
110 				snprintf(Filename+iLen, 22, "%d", iFinNum+1);
111 				EnforceExtension(Filename, "oci");
112 			}
113 		}
114 		else
115 		{
116 			// Crew was renamed; file rename necessary, if the name is not blocked by another crew info
117 			if (!hGroup.FindEntry(szTempGroup))
118 			{
119 				if (hGroup.Rename(Filename, szTempGroup))
120 					SCopy(szTempGroup, Filename, _MAX_PATH);
121 				else
122 				{
123 					// could not rename. Not fatal; just use old file
124 					LogF("Error adjusting crew info for %s into %s: Rename error from %s to %s!", Name, hGroup.GetFullName().getData(), Filename, szTempGroup);
125 				}
126 			}
127 		}
128 	}
129 	// Open group
130 	C4Group hTemp;
131 	if (!hTemp.OpenAsChild(&hGroup, Filename, false, true))
132 		return false;
133 	// custom rank image present?
134 	if (pDefs && !fStoreTiny)
135 	{
136 		C4Def *pDef = pDefs->ID2Def(id);
137 		if (pDef)
138 		{
139 			if (pDef->pRankSymbols)
140 			{
141 				C4FacetSurface fctRankSymbol;
142 				if (C4RankSystem::DrawRankSymbol(&fctRankSymbol, Rank, pDef->pRankSymbols, pDef->iNumRankSymbols, true))
143 				{
144 					fctRankSymbol.GetFace().SavePNG(hTemp, C4CFN_ClonkRank);
145 				}
146 			}
147 			else
148 			{
149 				// definition does not have custom rank symbols: Remove any rank image from Clonk
150 				hTemp.Delete(C4CFN_ClonkRank);
151 			}
152 		}
153 	}
154 
155 	// Save info to temp group
156 	if (!C4ObjectInfoCore::Save(hTemp, pDefs))
157 		{ hTemp.Close(); return false; }
158 	// Close temp group
159 	hTemp.Close();
160 	// Success
161 	return true;
162 }
163 
Evaluate()164 void C4ObjectInfo::Evaluate()
165 {
166 	Retire();
167 	if (WasInAction) Rounds++;
168 }
169 
Clear()170 void C4ObjectInfo::Clear()
171 {
172 	pDef=nullptr;
173 }
174 
Recruit()175 void C4ObjectInfo::Recruit()
176 {
177 	// already recruited?
178 	if (InAction) return;
179 	WasInAction=true;
180 	InAction=true;
181 	InActionTime=Game.Time;
182 	// rank name overload by def?
183 	C4Def *pDef=::Definitions.ID2Def(id);
184 	if (pDef) if (pDef->pRankNames)
185 		{
186 			StdStrBuf sRank(pDef->pRankNames->GetRankName(Rank, true));
187 			if (sRank) sRankName.Copy(sRank);
188 		}
189 }
190 
Retire()191 void C4ObjectInfo::Retire()
192 {
193 	// not recruited?
194 	if (!InAction) return;
195 	// retire
196 	InAction=false;
197 	TotalPlayingTime+=(Game.Time-InActionTime);
198 }
199 
SetBirthday()200 void C4ObjectInfo::SetBirthday()
201 {
202 	Birthday=time(nullptr);
203 }
204