1/*
2 * LegacyClonk
3 *
4 * Copyright (c) 1998-2000, Matthes Bender (RedWolf Design)
5 * Copyright (c) 2017-2019, 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/* Special regions to extend the pathfinder */
18
19#include <C4Include.h>
20#include <C4TransferZone.h>
21
22#include <C4Game.h>
23#include <C4FacetEx.h>
24#include <C4Wrappers.h>
25
26C4TransferZone::C4TransferZone()
27{
28 Default();
29}
30
31C4TransferZone::~C4TransferZone()
32{
33 Clear();
34}
35
36void C4TransferZone::Default()
37{
38 Object = nullptr;
39 X = Y = Wdt = Hgt = 0;
40 Next = nullptr;
41 Used = false;
42}
43
44void C4TransferZone::Clear() {}
45
46C4TransferZones::C4TransferZones()
47{
48 Default();
49}
50
51C4TransferZones::~C4TransferZones()
52{
53 Clear();
54}
55
56void C4TransferZones::Default()
57{
58 First = nullptr;
59}
60
61void C4TransferZones::Clear()
62{
63 C4TransferZone *pZone, *pNext;
64 for (pZone = First; pZone; pZone = pNext) { pNext = pZone->Next; delete pZone; }
65 First = nullptr;
66}
67
68void C4TransferZones::ClearPointers(C4Object *pObj)
69{
70 // Clear object pointers
71 for (C4TransferZone *pZone = First; pZone; pZone = pZone->Next)
72 if (pZone->Object == pObj)
73 pZone->Object = nullptr;
74 // Remove cleared zones immediately
75 RemoveNullZones();
76}
77
78bool C4TransferZones::Set(int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt, C4Object *pObj)
79{
80 C4TransferZone *pZone;
81 // Empty zone: clear existing object zones
82 if (!iWdt || !iHgt) { ClearPointers(pObj); return true; }
83 // Update existing zone
84 if (pZone = Find(pObj))
85 {
86 pZone->X = iX; pZone->Y = iY;
87 pZone->Wdt = iWdt; pZone->Hgt = iHgt;
88 }
89 // Allocate and add new zone
90 else
91 Add(iX, iY, iWdt, iHgt, pObj);
92 // Success
93 return true;
94}
95
96bool C4TransferZones::Add(int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt, C4Object *pObj)
97{
98 C4TransferZone *pZone;
99 // Allocate and add new zone
100 pZone = new C4TransferZone;
101 pZone->X = iX; pZone->Y = iY;
102 pZone->Wdt = iWdt; pZone->Hgt = iHgt;
103 pZone->Object = pObj;
104 pZone->Next = First;
105 First = pZone;
106 // Success
107 return true;
108}
109
110void C4TransferZones::Synchronize()
111{
112 Clear();
113 Game.Objects.UpdateTransferZones();
114}
115
116C4TransferZone *C4TransferZones::Find(int32_t iX, int32_t iY)
117{
118 for (C4TransferZone *pZone = First; pZone; pZone = pZone->Next)
119 if (Inside<int32_t>(ival: iX - pZone->X, lbound: 0, rbound: pZone->Wdt - 1))
120 if (Inside<int32_t>(ival: iY - pZone->Y, lbound: 0, rbound: pZone->Hgt - 1))
121 return pZone;
122 return nullptr;
123}
124
125void C4TransferZones::Draw(C4FacetEx &cgo)
126{
127 for (C4TransferZone *pZone = First; pZone; pZone = pZone->Next)
128 pZone->Draw(cgo);
129}
130
131void C4TransferZone::Draw(C4FacetEx &cgo, bool fHighlight)
132{
133 if (Used) fHighlight = true;
134 lpDDraw->DrawFrame(sfcDest: cgo.Surface,
135 x1: cgo.X + X - cgo.TargetX, y1: cgo.Y + Y - cgo.TargetY,
136 x2: cgo.X + X - cgo.TargetX + Wdt - 1, y2: cgo.Y + Y - cgo.TargetY + Hgt - 1,
137 col: fHighlight ? CGreen : CRed);
138}
139
140bool C4TransferZone::At(int32_t iX, int32_t iY)
141{
142 return (Inside<int32_t>(ival: iX - X, lbound: 0, rbound: Wdt - 1) && Inside<int32_t>(ival: iY - Y, lbound: 0, rbound: Hgt - 1));
143}
144
145int32_t C4TransferZones::RemoveNullZones()
146{
147 int32_t iResult = 0;
148 C4TransferZone *pZone, *pNext, *pPrev = nullptr;
149 for (pZone = First; pZone; pZone = pNext)
150 {
151 pNext = pZone->Next;
152 if (!pZone->Object)
153 {
154 delete pZone;
155 if (pPrev) pPrev->Next = pNext;
156 else First = pNext;
157 iResult++;
158 }
159 else
160 pPrev = pZone;
161 }
162 return iResult;
163}
164
165void AdjustMoveToTarget(int32_t &rX, int32_t &rY, bool fFreeMove, int32_t iShapeHgt);
166
167bool C4TransferZone::GetEntryPoint(int32_t &rX, int32_t &rY, int32_t iToX, int32_t iToY)
168{
169 // Target inside zone: move outside horizontally
170 if (Inside<int32_t>(ival: iToX - X, lbound: 0, rbound: Wdt - 1) && Inside<int32_t>(ival: iToY - Y, lbound: 0, rbound: Hgt - 1))
171 if (iToX < X + Wdt / 2) iToX = X - 1; else iToX = X + Wdt;
172 // Get closest adjacent point
173 rX = BoundBy<int32_t>(bval: iToX, lbound: X - 1, rbound: X + Wdt);
174 rY = BoundBy<int32_t>(bval: iToY, lbound: Y - 1, rbound: Y + Hgt);
175 // Search around zone for free
176 int32_t iX1 = rX, iY1 = rY, iX2 = rX, iY2 = rY;
177 int32_t iXIncr1 = 0, iYIncr1 = -1, iXIncr2 = 0, iYIncr2 = +1;
178 int32_t cnt;
179 for (cnt = 0; cnt < 2 * Wdt + 2 * Hgt; cnt++)
180 {
181 // Found free
182 if (!GBackSolid(x: iX1, y: iY1)) { rX = iX1; rY = iY1; break; }
183 if (!GBackSolid(x: iX2, y: iY2)) { rX = iX2; rY = iY2; break; }
184 // Advance
185 iX1 += iXIncr1; iY1 += iYIncr1;
186 iX2 += iXIncr2; iY2 += iYIncr2;
187 // Corners
188 if (iY1 < Y - 1) { iY1 = Y - 1; iXIncr1 = +1; iYIncr1 = 0; }
189 if (iX1 > X + Wdt) { iX1 = X + Wdt; iXIncr1 = 0; iYIncr1 = +1; }
190 if (iY1 > Y + Hgt) { iY1 = Y + Hgt; iXIncr1 = -1; iYIncr1 = 0; }
191 if (iX1 < X - 1) { iX1 = X - 1; iXIncr1 = 0; iYIncr1 = -1; }
192 if (iY2 < Y - 1) { iY2 = Y - 1; iXIncr2 = -1; iYIncr2 = 0; }
193 if (iX2 > X + Wdt) { iX2 = X + Wdt; iXIncr2 = 0; iYIncr2 = -1; }
194 if (iY2 > Y + Hgt) { iY2 = Y + Hgt; iXIncr2 = +1; iYIncr2 = 0; }
195 if (iX2 < X - 1) { iX2 = X - 1; iXIncr2 = 0; iYIncr2 = +1; }
196 }
197 // No free found
198 if (cnt >= 2 * Wdt + 2 * Hgt) return false;
199 // Vertical walk-to adjust (only if at the side of zone)
200 if (!Inside<int32_t>(ival: rX - X, lbound: 0, rbound: Wdt - 1))
201 AdjustMoveToTarget(rX, rY, fFreeMove: false, iShapeHgt: 20);
202 // Success
203 return true;
204}
205
206void C4TransferZones::ClearUsed()
207{
208 for (C4TransferZone *pZone = First; pZone; pZone = pZone->Next)
209 pZone->Used = false;
210}
211
212C4TransferZone *C4TransferZones::Find(C4Object *pObj)
213{
214 for (C4TransferZone *pZone = First; pZone; pZone = pZone->Next)
215 if (pZone->Object == pObj)
216 return pZone;
217 return nullptr;
218}
219