1 /*
2 	C-Dogs SDL
3 	A port of the legendary (and fun) action/arcade cdogs.
4 
5 	Copyright (c) 2016-2017, 2019-2021 Cong Xu
6 	All rights reserved.
7 
8 	Redistribution and use in source and binary forms, with or without
9 	modification, are permitted provided that the following conditions are met:
10 
11 	Redistributions of source code must retain the above copyright notice, this
12 	list of conditions and the following disclaimer.
13 	Redistributions in binary form must reproduce the above copyright notice,
14 	this list of conditions and the following disclaimer in the documentation
15 	and/or other materials provided with the distribution.
16 
17 	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 	AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 	ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 	LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 	CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 	POSSIBILITY OF SUCH DAMAGE.
28 */
29 #include "character_class.h"
30 
31 #include "json_utils.h"
32 #include "log.h"
33 
34 #define VERSION 2
35 #define FOOTSTEP_DISTANCE_PLUS 250
36 
37 CharacterClasses gCharacterClasses;
38 
39 // TODO: use map structure?
StrCharacterClass(const char * s)40 const CharacterClass *StrCharacterClass(const char *s)
41 {
42 	CA_FOREACH(const CharacterClass, c, gCharacterClasses.CustomClasses)
43 	if (strcmp(s, c->Name) == 0)
44 	{
45 		return c;
46 	}
47 	CA_FOREACH_END()
48 	CA_FOREACH(const CharacterClass, c, gCharacterClasses.Classes)
49 	if (strcmp(s, c->Name) == 0)
50 	{
51 		return c;
52 	}
53 	CA_FOREACH_END()
54 	LOG(LM_MAIN, LL_ERROR, "Cannot find character name: %s", s);
55 	return NULL;
56 }
57 static const char *characterNames[] = {
58 	"Jones", "Ice",			"Ogre",	  "Dragon",	   "WarBaby", "Bug-eye",
59 	"Smith", "Ogre Boss",	"Grunt",  "Professor", "Snake",	  "Wolf",
60 	"Bob",	 "Mad bug-eye", "Cyborg", "Robot",	   "Lady"};
IntCharacterFace(const int face)61 const char *IntCharacterFace(const int face)
62 {
63 	return characterNames[face];
64 }
CharacterOldFaceToHair(const char * face,char ** newFace,char ** hair)65 void CharacterOldFaceToHair(const char *face, char **newFace, char **hair)
66 {
67 	// Convert old faces to face + hair
68 	if (strcmp(face, "Bob") == 0)
69 	{
70 		CSTRDUP(*newFace, "Jones");
71 		CSTRDUP(*hair, "beard");
72 	}
73 	else if (strcmp(face, "Cyber Jones") == 0)
74 	{
75 		CSTRDUP(*newFace, "Cyborg");
76 		CSTRDUP(*hair, "cyber_shades");
77 	}
78 	else if (strcmp(face, "Cyber Smith") == 0)
79 	{
80 		CSTRDUP(*newFace, "Cyborg");
81 		CSTRDUP(*hair, "flattop");
82 	}
83 	else if (strcmp(face, "Cyber WarBaby") == 0)
84 	{
85 		CSTRDUP(*newFace, "Cyborg");
86 		CSTRDUP(*hair, "beret");
87 	}
88 	else if (strcmp(face, "Cyborg") == 0)
89 	{
90 		CSTRDUP(*newFace, "Cyborg");
91 		CSTRDUP(*hair, "cyborg");
92 	}
93 	else if (strcmp(face, "Dragon") == 0)
94 	{
95 		CSTRDUP(*newFace, "Jones");
96 		CSTRDUP(*hair, "hogan");
97 	}
98 	else if (strcmp(face, "Evil Ogre") == 0)
99 	{
100 		CSTRDUP(*newFace, "Ogre");
101 		CSTRDUP(*hair, "horns");
102 	}
103 	else if (strcmp(face, "Freeze") == 0)
104 	{
105 		CSTRDUP(*newFace, "Jones");
106 		CSTRDUP(*hair, "ski_goggles");
107 	}
108 	else if (strcmp(face, "Goggles") == 0)
109 	{
110 		CSTRDUP(*newFace, "Jones");
111 		CSTRDUP(*hair, "goggles");
112 	}
113 	else if (strcmp(face, "Grunt") == 0)
114 	{
115 		CSTRDUP(*newFace, "Jones");
116 		CSTRDUP(*hair, "riot_helmet");
117 	}
118 	else if (strcmp(face, "Ice") == 0)
119 	{
120 		CSTRDUP(*newFace, "Jones");
121 		CSTRDUP(*hair, "shades");
122 	}
123 	else if (strcmp(face, "Lady") == 0)
124 	{
125 		CSTRDUP(*newFace, "Lady");
126 		CSTRDUP(*hair, "ponytail");
127 	}
128 	else if (strcmp(face, "Ogre Boss") == 0)
129 	{
130 		CSTRDUP(*newFace, "Ogre");
131 		CSTRDUP(*hair, "mohawk");
132 	}
133 	else if (strcmp(face, "Professor") == 0)
134 	{
135 		CSTRDUP(*newFace, "Jones");
136 		CSTRDUP(*hair, "professor");
137 	}
138 	else if (strcmp(face, "Smith") == 0)
139 	{
140 		CSTRDUP(*newFace, "Jones");
141 		CSTRDUP(*hair, "flattop");
142 	}
143 	else if (strcmp(face, "Snake") == 0)
144 	{
145 		CSTRDUP(*newFace, "Jones");
146 		CSTRDUP(*hair, "eye_patch");
147 	}
148 	else if (strcmp(face, "Sweeper") == 0)
149 	{
150 		CSTRDUP(*newFace, "Jones");
151 		CSTRDUP(*hair, "helmet");
152 	}
153 	else if (strcmp(face, "WarBaby") == 0)
154 	{
155 		CSTRDUP(*newFace, "Jones");
156 		CSTRDUP(*hair, "beret");
157 	}
158 	else if (strcmp(face, "Wolf") == 0)
159 	{
160 		CSTRDUP(*newFace, "Jones");
161 		CSTRDUP(*hair, "dutch");
162 	}
163 	else
164 	{
165 		CSTRDUP(*newFace, face);
166 	}
167 }
168 
CharacterClassGetDeathSprites(const CharacterClass * c,const PicManager * pm)169 const NamedSprites *CharacterClassGetDeathSprites(const CharacterClass *c, const PicManager *pm)
170 {
171 	char buf[256];
172 	sprintf(buf, "chars/%s", c->DeathSprites);
173 	return PicManagerGetSprites(pm, buf);
174 }
IndexCharacterClass(const int i)175 const CharacterClass *IndexCharacterClass(const int i)
176 {
177 	CASSERT(
178 		i >= 0 && i < (int)gCharacterClasses.Classes.size +
179 						  (int)gCharacterClasses.CustomClasses.size,
180 		"Character class index out of bounds");
181 	if (i < (int)gCharacterClasses.Classes.size)
182 	{
183 		return CArrayGet(&gCharacterClasses.Classes, i);
184 	}
185 	return CArrayGet(
186 		&gCharacterClasses.CustomClasses, i - gCharacterClasses.Classes.size);
187 }
CharacterClassIndex(const CharacterClass * c)188 int CharacterClassIndex(const CharacterClass *c)
189 {
190 	if (c == NULL)
191 	{
192 		return 0;
193 	}
194 	CA_FOREACH(const CharacterClass, cc, gCharacterClasses.Classes)
195 	if (cc == c)
196 	{
197 		return _ca_index;
198 	}
199 	CA_FOREACH_END()
200 	CA_FOREACH(const CharacterClass, cc, gCharacterClasses.CustomClasses)
201 	if (cc == c)
202 	{
203 		return _ca_index + (int)gCharacterClasses.Classes.size;
204 	}
205 	CA_FOREACH_END()
206 	CASSERT(false, "cannot find character class");
207 	return -1;
208 }
209 
CharacterClassGetSound(const CharacterClass * c,char * out,const char * sound)210 void CharacterClassGetSound(const CharacterClass *c, char *out, const char *sound)
211 {
212 	sprintf(out, "chars/%s/%s", sound, c->Sounds);
213 }
214 
CharacterClassesInitialize(CharacterClasses * c,const char * filename)215 void CharacterClassesInitialize(CharacterClasses *c, const char *filename)
216 {
217 	memset(c, 0, sizeof *c);
218 	CArrayInit(&c->Classes, sizeof(CharacterClass));
219 	CArrayInit(&c->CustomClasses, sizeof(CharacterClass));
220 
221 	char buf[CDOGS_PATH_MAX];
222 	GetDataFilePath(buf, filename);
223 	FILE *f = fopen(buf, "r");
224 	json_t *root = NULL;
225 	if (f == NULL)
226 	{
227 		LOG(LM_MAIN, LL_ERROR, "cannot load characters file %s", buf);
228 		goto bail;
229 	}
230 	enum json_error e = json_stream_parse(f, &root);
231 	if (e != JSON_OK)
232 	{
233 		LOG(LM_MAIN, LL_ERROR, "error parsing characters file %s", buf);
234 		goto bail;
235 	}
236 	CharacterClassesLoadJSON(&c->Classes, root);
237 
238 bail:
239 	if (f != NULL)
240 	{
241 		fclose(f);
242 	}
243 	json_free_value(&root);
244 }
245 static void LoadCharacterClass(CharacterClass *c, json_t *node);
CharacterClassesLoadJSON(CArray * classes,json_t * root)246 void CharacterClassesLoadJSON(CArray *classes, json_t *root)
247 {
248 	int version;
249 	LoadInt(&version, root, "Version");
250 	if (version > VERSION || version <= 0)
251 	{
252 		LOG(LM_MAIN, LL_ERROR, "Cannot read character file version: %d",
253 			version);
254 		return;
255 	}
256 
257 	json_t *charactersNode = json_find_first_label(root, "Characters")->child;
258 	for (json_t *child = charactersNode->child; child; child = child->next)
259 	{
260 		CharacterClass cc;
261 		LoadCharacterClass(&cc, child);
262 		CArrayPushBack(classes, &cc);
263 	}
264 }
LoadCharacterClass(CharacterClass * c,json_t * node)265 static void LoadCharacterClass(CharacterClass *c, json_t *node)
266 {
267 	memset(c, 0, sizeof *c);
268 	c->Name = GetString(node, "Name");
269 	LoadBool(&c->Vehicle, node, "Vehicle");
270 	// TODO: allow non-directional head sprites?
271 	json_t *headPics = json_find_first_label(node, "HeadPics")->child;
272 	c->HeadSprites = GetString(headPics, "Sprites");
273 	LoadStr(&c->Body, node, "Body");
274 	if (c->Body == NULL)
275 	{
276 		CSTRDUP(c->Body, "base");
277 	}
278 	LoadStr(&c->DeathSprites, node, "DeathSprites");
279 	if (c->DeathSprites == NULL)
280 	{
281 		CSTRDUP(c->DeathSprites, "death");
282 	}
283 	c->Mass = CHARACTER_DEFAULT_MASS;
284 	LoadInt(&c->Mass, node, "Mass");
285 	c->Sprites = StrCharSpriteClass(c->Body);
286 
287 	LoadStr(&c->Sounds, node, "Sounds");
288 
289 	LoadStr(&c->Footsteps, node, "Footsteps");
290 	if (c->Footsteps == NULL)
291 	{
292 		CSTRDUP(c->Footsteps, "boots");
293 	}
294 	c->FootstepsDistancePlus = FOOTSTEP_DISTANCE_PLUS;
295 	LoadInt(&c->FootstepsDistancePlus, node, "FootstepsDistancePlus");
296 
297 	c->BloodColor = colorRed;
298 	LoadColor(&c->BloodColor, node, "BloodColor");
299 
300 	c->HasHair = true;
301 	LoadBool(&c->HasHair, node, "HasHair");
302 
303 	LoadStr(&c->Corpse, node, "Corpse");
304 }
305 static void CharacterClassFree(CharacterClass *c);
CharacterClassesClear(CArray * classes)306 void CharacterClassesClear(CArray *classes)
307 {
308 	for (int i = 0; i < (int)classes->size; i++)
309 	{
310 		CharacterClassFree(CArrayGet(classes, i));
311 	}
312 	CArrayClear(classes);
313 }
CharacterClassFree(CharacterClass * c)314 static void CharacterClassFree(CharacterClass *c)
315 {
316 	CFREE(c->Name);
317 	CFREE(c->HeadSprites);
318 	CFREE(c->Body);
319 	CFREE(c->DeathSprites);
320 	CFREE(c->Sounds);
321 	CFREE(c->Footsteps);
322 	CFREE(c->Corpse);
323 }
CharacterClassesTerminate(CharacterClasses * c)324 void CharacterClassesTerminate(CharacterClasses *c)
325 {
326 	CharacterClassesClear(&c->Classes);
327 	CArrayTerminate(&c->Classes);
328 	CharacterClassesClear(&c->CustomClasses);
329 	CArrayTerminate(&c->CustomClasses);
330 }
331