1 /*
2 C-Dogs SDL
3 A port of the legendary (and fun) action/arcade cdogs.
4 Copyright (c) 2013-2016, 2018-2019 Cong Xu
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9
10 Redistributions of source code must retain the above copyright notice, this
11 list of conditions and the following disclaimer.
12 Redistributions in binary form must reproduce the above copyright notice,
13 this list of conditions and the following disclaimer in the documentation
14 and/or other materials provided with the distribution.
15
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 POSSIBILITY OF SUCH DAMAGE.
27 */
28 #include "cpic.h"
29
30 #include "blit.h"
31 #include "json_utils.h"
32 #include "log.h"
33 #include "palette.h"
34 #include "pic_manager.h"
35 #include "utils.h"
36
37
StrPicType(const char * s)38 PicType StrPicType(const char *s)
39 {
40 S2T(PICTYPE_NORMAL, "Normal");
41 S2T(PICTYPE_DIRECTIONAL, "Directional");
42 S2T(PICTYPE_ANIMATED, "Animated");
43 S2T(PICTYPE_ANIMATED_RANDOM, "AnimatedRandom");
44 CASSERT(false, "unknown pic type");
45 return PICTYPE_NORMAL;
46 }
47
48
CPicDrawContextNew(void)49 CPicDrawContext CPicDrawContextNew(void)
50 {
51 CPicDrawContext c;
52 c.Dir = DIRECTION_UP;
53 c.Offset = svec2i_zero();
54 c.Radians = 0;
55 c.Scale = svec2_one();
56 c.Mask = colorWhite;
57 c.Flip = SDL_FLIP_NONE;
58 return c;
59 }
60
61
NamedPicFree(NamedPic * n)62 void NamedPicFree(NamedPic *n)
63 {
64 CFREE(n->name);
65 PicFree(&n->pic);
66 }
67
68
NamedSpritesInit(NamedSprites * ns,const char * name)69 void NamedSpritesInit(NamedSprites *ns, const char *name)
70 {
71 CSTRDUP(ns->name, name);
72 CArrayInit(&ns->pics, sizeof(Pic));
73 }
NamedSpritesFree(NamedSprites * ns)74 void NamedSpritesFree(NamedSprites *ns)
75 {
76 if (ns == NULL)
77 {
78 return;
79 }
80 CFREE(ns->name);
81 for (int i = 0; i < (int)ns->pics.size; i++)
82 {
83 PicFree(CArrayGet(&ns->pics, i));
84 }
85 CArrayTerminate(&ns->pics);
86 }
87
88 static void LoadNormal(CPic *p, const json_t *node);
89 static void LoadMaskTint(CPic *p, json_t *node);
90
CPicLoadJSON(CPic * p,json_t * node)91 void CPicLoadJSON(CPic *p, json_t *node)
92 {
93 char *tmp = GetString(node, "Type");
94 p->Type = StrPicType(tmp);
95 CFREE(tmp);
96 tmp = NULL;
97 switch (p->Type)
98 {
99 case PICTYPE_NORMAL:
100 LoadNormal(p, json_find_first_label(node, "Pic")->child);
101 break;
102 case PICTYPE_DIRECTIONAL:
103 LoadStr(&tmp, node, "Sprites");
104 if (tmp == NULL)
105 {
106 LOG(LM_GFX, LL_ERROR, "cannot load sprites");
107 goto bail;
108 }
109 p->u.Sprites = &PicManagerGetSprites(&gPicManager, tmp)->pics;
110 CFREE(tmp);
111 break;
112 case PICTYPE_ANIMATED: // fallthrough
113 case PICTYPE_ANIMATED_RANDOM:
114 LoadStr(&tmp, node, "Sprites");
115 if (tmp == NULL)
116 {
117 LOG(LM_GFX, LL_ERROR, "cannot load sprites");
118 goto bail;
119 }
120 p->u.Animated.Sprites =
121 &PicManagerGetSprites(&gPicManager, tmp)->pics;
122 CFREE(tmp);
123 LoadInt(&p->u.Animated.Count, node, "Count");
124 LoadInt(&p->u.Animated.TicksPerFrame, node, "TicksPerFrame");
125 p->u.Animated.TicksPerFrame = MAX(p->u.Animated.TicksPerFrame, 0);
126 break;
127 default:
128 CASSERT(false, "unknown pic type");
129 break;
130 }
131 LoadMaskTint(p, node);
132 bail:
133 // TODO: return error
134 return;
135 }
136
CPicInitNormal(CPic * p,const Pic * pic)137 void CPicInitNormal(CPic *p, const Pic *pic)
138 {
139 p->Type = PICTYPE_NORMAL;
140 p->u.Pic = pic;
141 p->Mask = colorWhite;
142 }
CPicInitNormalFromName(CPic * p,const char * name)143 void CPicInitNormalFromName(CPic *p, const char *name)
144 {
145 p->Type = PICTYPE_NORMAL;
146 p->u.Pic = PicManagerGetPic(&gPicManager, name);
147 p->Mask = colorWhite;
148 }
149
CPicLoadNormal(CPic * p,const json_t * node)150 void CPicLoadNormal(CPic *p, const json_t *node)
151 {
152 p->Type = PICTYPE_NORMAL;
153 LoadNormal(p, node);
154 p->Mask = colorWhite;
155 }
156
LoadNormal(CPic * p,const json_t * node)157 static void LoadNormal(CPic *p, const json_t *node)
158 {
159 char *tmp = json_unescape(node->text);
160 p->u.Pic = PicManagerGetPic(&gPicManager, tmp);
161 CFREE(tmp);
162 }
163
LoadMaskTint(CPic * p,json_t * node)164 static void LoadMaskTint(CPic *p, json_t *node)
165 {
166 p->Mask = colorWhite;
167 if (json_find_first_label(node, "Mask"))
168 {
169 char *tmp = GetString(node, "Mask");
170 p->Mask = StrColor(tmp);
171 CFREE(tmp);
172 }
173 else if (json_find_first_label(node, "Tint"))
174 {
175 // TODO: create new pic, as tinting does not work correctly using mask
176 // Mask only darkens, whereas tinting can change hue without affecting
177 // value, for example
178 json_t *tint = json_find_first_label(node, "Tint")->child->child;
179 HSV hsv;
180 hsv.h = atof(tint->text);
181 tint = tint->next;
182 hsv.s = atof(tint->text);
183 tint = tint->next;
184 hsv.v = atof(tint->text);
185 p->Mask = ColorTint(colorWhite, hsv);
186 p->Mask.a = 0x40;
187 }
188 }
189
CPicIsLoaded(const CPic * p)190 bool CPicIsLoaded(const CPic *p)
191 {
192 switch (p->Type)
193 {
194 case PICTYPE_NORMAL:
195 return p->u.Pic != NULL;
196 case PICTYPE_DIRECTIONAL:
197 return p->u.Sprites != NULL;
198 case PICTYPE_ANIMATED:
199 case PICTYPE_ANIMATED_RANDOM:
200 return p->u.Animated.Sprites != NULL;
201 default:
202 CASSERT(false, "unknown pic type");
203 return false;
204 }
205 }
206
CPicGetSize(const CPic * p)207 struct vec2i CPicGetSize(const CPic *p)
208 {
209 const Pic *pic = CPicGetPic(p, 0);
210 if (pic == NULL)
211 {
212 return svec2i_zero();
213 }
214 return pic->size;
215 }
216
CPicCopyPic(CPic * dest,const CPic * src)217 void CPicCopyPic(CPic *dest, const CPic *src)
218 {
219 memcpy(dest, src, sizeof *src);
220 if (dest->Type == PICTYPE_ANIMATED_RANDOM)
221 {
222 // initialise frame with a random value
223 dest->u.Animated.Frame = rand() % (int)dest->u.Animated.Sprites->size;
224 }
225 }
226
CPicUpdate(CPic * p,const int ticks)227 void CPicUpdate(CPic *p, const int ticks)
228 {
229 switch (p->Type)
230 {
231 case PICTYPE_ANIMATED:
232 {
233 p->u.Animated.Count += ticks;
234 if (p->u.Animated.TicksPerFrame > 0)
235 {
236 while (p->u.Animated.Count >= p->u.Animated.TicksPerFrame)
237 {
238 p->u.Animated.Frame++;
239 p->u.Animated.Count -= p->u.Animated.TicksPerFrame;
240 }
241 while (p->u.Animated.Frame >= (int)p->u.Animated.Sprites->size)
242 {
243 p->u.Animated.Frame -= (int)p->u.Animated.Sprites->size;
244 }
245 }
246 }
247 break;
248 case PICTYPE_ANIMATED_RANDOM:
249 if (p->u.Animated.Count == 0)
250 {
251 // Initial frame
252 p->u.Animated.Frame = rand() % (int)p->u.Animated.Sprites->size;
253 }
254 p->u.Animated.Count += ticks;
255 if (p->u.Animated.TicksPerFrame > 0 &&
256 p->u.Animated.Count >= p->u.Animated.TicksPerFrame)
257 {
258 p->u.Animated.Frame = rand() % (int)p->u.Animated.Sprites->size;
259 p->u.Animated.Count = 0;
260 }
261 break;
262 default:
263 // Do nothing
264 break;
265 }
266 }
CPicGetPic(const CPic * p,const int idx)267 const Pic *CPicGetPic(const CPic *p, const int idx)
268 {
269 switch (p->Type)
270 {
271 case PICTYPE_NORMAL:
272 return p->u.Pic;
273 case PICTYPE_DIRECTIONAL:
274 return p->u.Sprites != NULL ? CArrayGet(p->u.Sprites, idx) : NULL;
275 case PICTYPE_ANIMATED:
276 case PICTYPE_ANIMATED_RANDOM:
277 if (p->u.Animated.Frame < 0 ||
278 p->u.Animated.Frame >= (int)p->u.Animated.Sprites->size)
279 {
280 return NULL;
281 }
282 return CArrayGet(p->u.Animated.Sprites, p->u.Animated.Frame);
283 default:
284 CASSERT(false, "unknown pic type");
285 return NULL;
286 }
287 }
CPicDraw(GraphicsDevice * g,const CPic * p,const struct vec2i pos,const CPicDrawContext * context)288 void CPicDraw(
289 GraphicsDevice *g, const CPic *p,
290 const struct vec2i pos, const CPicDrawContext *context)
291 {
292 CPicDrawContext ctx;
293 if (context == NULL)
294 {
295 ctx = CPicDrawContextNew();
296 context = &ctx;
297 }
298 const Pic *pic = CPicGetPic(p, context->Dir);
299 if (pic == NULL)
300 {
301 return;
302 }
303 const struct vec2i picPos = svec2i_add(pos, context->Offset);
304 PicRender(
305 pic, g->gameWindow.renderer, picPos, ColorMult(p->Mask, context->Mask),
306 context->Radians,
307 context->Scale, context->Flip, Rect2iZero());
308 }
309