1/*
2 * LegacyClonk
3 *
4 * Copyright (c) 1998-2000, Matthes Bender (RedWolf Design)
5 * Copyright (c) 2017-2021, The LegacyClonk Team and contributors
6 *
7 * Distributed under the terms of the ISC license; see accompanying file
8 * "COPYING" for details.
9 *
10 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11 * See accompanying file "TRADEMARK" for details.
12 *
13 * To redistribute this file separately, substitute the full license texts
14 * for the above references.
15 */
16
17/* A facet that can hold its own surface and also target coordinates */
18
19#include <C4Include.h>
20#include <C4FacetEx.h>
21
22#include <C4Random.h>
23#include <C4Shape.h>
24#include <C4Group.h>
25
26void C4FacetEx::Set(C4Surface *nsfc, int nx, int ny, int nwdt, int nhgt, int ntx, int nty)
27{
28 C4Facet::Set(nsfc, nx, ny, nwdt, nhgt);
29 TargetX = ntx; TargetY = nty;
30}
31
32C4FacetEx C4FacetEx::GetSection(int iSection)
33{
34 C4FacetEx fctResult;
35 fctResult.Set(nsfc: Surface, nx: X + Hgt * iSection, ny: Y, nwdt: Hgt, nhgt: Hgt, ntx: 0, nty: 0);
36 return fctResult;
37}
38
39C4FacetEx C4FacetEx::GetPhase(int iPhaseX, int iPhaseY)
40{
41 C4FacetEx fctResult;
42 fctResult.Set(nsfc: Surface, nx: X + Wdt * iPhaseX, ny: Y + Hgt * iPhaseY, nwdt: Wdt, nhgt: Hgt, ntx: 0, nty: 0);
43 return fctResult;
44}
45
46void C4FacetEx::DrawLine(int iX1, int iY1, int iX2, int iY2, uint8_t bCol1, uint8_t bCol2)
47{
48 if (!lpDDraw || !Surface || !Wdt || !Hgt) return;
49 // Scroll position
50 iX1 -= TargetX; iY1 -= TargetY; iX2 -= TargetX; iY2 -= TargetY;
51 // No clipping is done here, because clipping will be done by gfx wrapper anyway
52 // Draw line
53 lpDDraw->DrawLine(sfcTarget: Surface, x1: X + iX1, y1: Y + iY1, x2: X + iX2, y2: Y + iY2, byCol: bCol1);
54 lpDDraw->DrawPix(sfcDest: Surface, tx: static_cast<float>(X + iX1), ty: static_cast<float>(Y + iY1), dwCol: lpDDraw->Pal.GetClr(byCol: bCol2));
55}
56
57// bolt random size
58#define DrawBoltR1 7
59#define DrawBoltR2 3
60
61void C4FacetEx::DrawBolt(int iX1, int iY1, int iX2, int iY2, uint8_t bCol, uint8_t bCol2)
62{
63 if (!lpDDraw || !Surface || !Wdt || !Hgt) return;
64 // Scroll position
65 iX1 -= TargetX; iY1 -= TargetY; iX2 -= TargetX; iY2 -= TargetY;
66 // Facet bounds
67 if (!Inside(ival: iX1, lbound: 0, rbound: Wdt - 1) && !Inside(ival: iX2, lbound: 0, rbound: Wdt - 1)) return;
68 if (!Inside(ival: iY1, lbound: 0, rbound: Hgt - 1) && !Inside(ival: iY2, lbound: 0, rbound: Hgt - 1)) return;
69 iX1 += X; iX2 += X; iY1 += Y; iY2 += Y;
70 // Draw bolt
71 int pvtx[2 * 4];
72 pvtx[0] = iX1; pvtx[1] = iY1; pvtx[2] = iX2; pvtx[3] = iY2;
73 pvtx[4] = iX2 + SafeRandom(DrawBoltR1) - DrawBoltR2; pvtx[5] = iY2 + SafeRandom(DrawBoltR1) - DrawBoltR2;
74 pvtx[6] = iX1 + SafeRandom(DrawBoltR1) - DrawBoltR2; pvtx[7] = iY1 + SafeRandom(DrawBoltR1) - DrawBoltR2;
75 // Draw in surface
76 uint32_t dwClr1 = lpDDraw->Pal.GetClr(byCol: bCol), dwClr2;
77 uint32_t dwClr3 = lpDDraw->Pal.GetClr(byCol: bCol2), dwClr4;
78 dwClr2 = dwClr1;
79 dwClr4 = dwClr3;
80 lpDDraw->DrawQuadDw(sfcTarget: Surface, ipVtx: pvtx, dwClr1, dwClr2: dwClr3, dwClr3: dwClr4, dwClr4: dwClr2);
81}
82
83// C4FacetExSurface
84
85bool C4FacetExSurface::Create(int iWdt, int iHgt, int iWdt2, int iHgt2)
86{
87 Clear();
88 // Create surface
89 Face.Default();
90 if (!Face.Create(iWdt, iHgt)) return false;
91 // Set facet
92 if (iWdt2 == C4FCT_Full) iWdt2 = Face.Wdt; if (iWdt2 == C4FCT_Height) iWdt2 = Face.Hgt; if (iWdt2 == C4FCT_Width) iWdt2 = Face.Wdt;
93 if (iHgt2 == C4FCT_Full) iHgt2 = Face.Hgt; if (iHgt2 == C4FCT_Height) iHgt2 = Face.Hgt; if (iHgt2 == C4FCT_Width) iHgt2 = Face.Wdt;
94 Set(nsfc: &Face, nx: 0, ny: 0, nwdt: iWdt2, nhgt: iHgt2, ntx: 0, nty: 0);
95 return true;
96}
97
98bool C4FacetExSurface::CreateClrByOwner(C4Surface *pBySurface)
99{
100 Clear();
101 // create surface
102 if (!Face.CreateColorByOwner(pBySurface)) return false;
103 // set facet
104 Set(nsfc: &Face, nx: 0, ny: 0, nwdt: Face.Wdt, nhgt: Face.Hgt, ntx: 0, nty: 0);
105 // success
106 return true;
107}
108
109bool C4FacetExSurface::EnsureSize(int iMinWdt, int iMinHgt)
110{
111 // safety
112 if (!Surface) return false;
113 // check size
114 int iWdt = Face.Wdt, iHgt = Face.Hgt;
115 if (iWdt >= iMinWdt && iHgt >= iMinHgt) return true;
116 // create temp surface
117 C4Surface *sfcDup = new C4Surface(iWdt, iHgt);
118 if (!sfcDup) return false;
119 if (!lpDDraw->BlitSurface(sfcSurface: &Face, sfcTarget: sfcDup, tx: 0, ty: 0, fBlitBase: false))
120 {
121 delete sfcDup; return false;
122 }
123 // calc needed size
124 int iDstWdt = Surface->Wdt, iDstHgt = iHgt;
125 while (iDstWdt < iMinWdt) iDstWdt += iWdt;
126 while (iDstHgt < iMinHgt) iDstHgt += iHgt;
127 // recreate this one
128 if (!Face.Create(iWdt: iDstWdt, iHgt: iDstHgt)) { delete sfcDup; Clear(); return false; }
129 // blit tiled into it
130 bool fSuccess = lpDDraw->BlitSurfaceTile(sfcSurface: sfcDup, sfcTarget: &Face, iToX: 0, iToY: 0, iToWdt: iDstWdt, iToHgt: iDstHgt, iOffsetX: 0, iOffsetY: 0, fSrcColKey: false);
131 // del temp surface
132 delete sfcDup;
133 // done
134 return fSuccess;
135}
136
137bool C4FacetExSurface::Load(C4Group &hGroup, const char *szName, int iWdt, int iHgt, bool fOwnPal, bool fNoErrIfNotFound)
138{
139 Clear();
140 // Entry name
141 char szFilename[_MAX_FNAME + 1];
142 SCopy(szSource: szName, sTarget: szFilename, _MAX_FNAME);
143 char *szExt = GetExtension(fname: szFilename);
144 if (!*szExt)
145 {
146 // no extension: Default to extension that is found as file in group
147 const char *const extensions[] = { "png", "bmp", "jpeg", "jpg", nullptr };
148 int i = 0; const char *szExt;
149 while (szExt = extensions[i++])
150 {
151 EnforceExtension(szFileName: szFilename, szExtension: szExt);
152 if (hGroup.FindEntry(szWildCard: szFilename)) break;
153 }
154 }
155 // Load surface
156 if (!Face.Load(hGroup, szFilename, fOwnPal, fNoErrIfNotFound)) return false;
157 // Set facet
158 if (iWdt == C4FCT_Full) iWdt = Face.Wdt; if (iWdt == C4FCT_Height) iWdt = Face.Hgt; if (iWdt == C4FCT_Width) iWdt = Face.Wdt;
159 if (iHgt == C4FCT_Full) iHgt = Face.Hgt; if (iHgt == C4FCT_Height) iHgt = Face.Hgt; if (iHgt == C4FCT_Width) iHgt = Face.Wdt;
160 Set(nsfc: &Face, nx: 0, ny: 0, nwdt: iWdt, nhgt: iHgt, ntx: 0, nty: 0);
161 return true;
162}
163
164bool C4FacetExSurface::CopyFromSfcMaxSize(C4Surface &srcSfc, int32_t iMaxSize, uint32_t dwColor)
165{
166 // safety
167 if (!srcSfc.Wdt || !srcSfc.Hgt) return false;
168 Clear();
169 // no scale?
170 bool fNeedsScale = !(srcSfc.Wdt <= iMaxSize && srcSfc.Hgt <= iMaxSize);
171 if (!fNeedsScale && !dwColor)
172 {
173 // no change necessary; just copy then
174 Face.Copy(fromSfc&: srcSfc);
175 }
176 else
177 {
178 // must scale down or colorize. Just blit.
179 C4Facet fctSource;
180 fctSource.Set(nsfc: &srcSfc, nx: 0, ny: 0, nwdt: srcSfc.Wdt, nhgt: srcSfc.Hgt);
181 int32_t iTargetWdt, iTargetHgt;
182 if (fNeedsScale)
183 {
184 if (fctSource.Wdt > fctSource.Hgt)
185 {
186 iTargetWdt = iMaxSize;
187 iTargetHgt = fctSource.Hgt * iTargetWdt / fctSource.Wdt;
188 }
189 else
190 {
191 iTargetHgt = iMaxSize;
192 iTargetWdt = fctSource.Wdt * iTargetHgt / fctSource.Hgt;
193 }
194 }
195 else
196 {
197 iTargetWdt = fctSource.Wdt;
198 iTargetHgt = fctSource.Hgt;
199 }
200 if (dwColor) srcSfc.SetClr(dwColor);
201 Create(iWdt: iTargetWdt, iHgt: iTargetHgt);
202 lpDDraw->Blit(sfcSource: &srcSfc, fx: 0.0f, fy: 0.0f, fwdt: float(fctSource.Wdt), fhgt: float(fctSource.Hgt),
203 sfcTarget: &Face, tx: 0, ty: 0, twdt: iTargetWdt, thgt: iTargetHgt);
204 }
205 Set(nsfc: &Face, nx: 0, ny: 0, nwdt: Face.Wdt, nhgt: Face.Hgt);
206 return true;
207}
208
209void C4FacetExSurface::Grayscale(int32_t iOffset)
210{
211 if (!lpDDraw || !Surface || !Wdt || !Hgt) return;
212 lpDDraw->Grayscale(sfcSfc: Surface, iOffset);
213}
214