1 /*
2 C-Dogs SDL
3 A port of the legendary (and fun) action/arcade cdogs.
4 Copyright (C) 1995 Ronny Wester
5 Copyright (C) 2003 Jeremy Chin
6 Copyright (C) 2003-2007 Lucas Martin-King
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21
22 This file incorporates work covered by the following copyright and
23 permission notice:
24
25 Copyright (c) 2014-2015, 2017 Cong Xu
26 All rights reserved.
27
28 Redistribution and use in source and binary forms, with or without
29 modification, are permitted provided that the following conditions are met:
30
31 Redistributions of source code must retain the above copyright notice, this
32 list of conditions and the following disclaimer.
33 Redistributions in binary form must reproduce the above copyright notice,
34 this list of conditions and the following disclaimer in the documentation
35 and/or other materials provided with the distribution.
36
37 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
38 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
39 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
40 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
41 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
43 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
44 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
45 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
46 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
47 POSSIBILITY OF SUCH DAMAGE.
48 */
49
50 #include <stdlib.h>
51 #include <string.h>
52 #include "triggers.h"
53 #include "map.h"
54 #include "net_util.h"
55 #include "objs.h"
56 #include "sounds.h"
57 #include "utils.h"
58
59 CArray gWatches; // of TWatch
60 static int watchIndex = 1;
61
62 // Number of frames to wait before repeating the "cannot activate" event
63 #define CANNOT_ACTIVATE_LOCK 50
64
65
TriggerNew(void)66 Trigger *TriggerNew(void)
67 {
68 Trigger *t;
69 CCALLOC(t, sizeof *t);
70 t->isActive = 1;
71 CArrayInit(&t->actions, sizeof(Action));
72 return t;
73 }
TriggerTerminate(Trigger * t)74 void TriggerTerminate(Trigger *t)
75 {
76 CArrayTerminate(&t->actions);
77 CFREE(t);
78 }
TriggerAddAction(Trigger * t)79 Action *TriggerAddAction(Trigger *t)
80 {
81 Action a;
82 memset(&a, 0, sizeof a);
83 CArrayPushBack(&t->actions, &a);
84 return CArrayGet(&t->actions, t->actions.size - 1);
85 }
86
WatchNew(void)87 TWatch *WatchNew(void)
88 {
89 TWatch t;
90 memset(&t, 0, sizeof(TWatch));
91 t.index = watchIndex++;
92 CArrayInit(&t.actions, sizeof(Action));
93 CArrayInit(&t.conditions, sizeof(Condition));
94 t.active = false;
95 CArrayPushBack(&gWatches, &t);
96 return CArrayGet(&gWatches, gWatches.size - 1);
97 }
WatchAddCondition(TWatch * w,const ConditionType type,const int counterMax,const struct vec2i pos)98 Condition *WatchAddCondition(
99 TWatch *w, const ConditionType type, const int counterMax,
100 const struct vec2i pos)
101 {
102 Condition c;
103 memset(&c, 0, sizeof c);
104 c.Type = type;
105 c.CounterMax = counterMax;
106 c.Pos = pos;
107 CArrayPushBack(&w->conditions, &c);
108 return CArrayGet(&w->conditions, w->conditions.size - 1);
109 }
WatchAddAction(TWatch * w)110 Action *WatchAddAction(TWatch *w)
111 {
112 Action a;
113 memset(&a, 0, sizeof a);
114 CArrayPushBack(&w->actions, &a);
115 return CArrayGet(&w->actions, w->actions.size - 1);
116 }
117
ActivateWatch(int idx)118 static void ActivateWatch(int idx)
119 {
120 CA_FOREACH(TWatch, w, gWatches)
121 if (w->index == idx)
122 {
123 w->active = true;
124
125 // Reset all conditions related to watch
126 for (int j = 0; j < (int)w->conditions.size; j++)
127 {
128 Condition *c = CArrayGet(&w->conditions, j);
129 c->Counter = 0;
130 }
131 return;
132 }
133 CA_FOREACH_END()
134 CASSERT(false, "Cannot find watch");
135 }
136
DeactivateWatch(int idx)137 static void DeactivateWatch(int idx)
138 {
139 CA_FOREACH(TWatch, w, gWatches)
140 if (w->index == idx)
141 {
142 w->active = false;
143 return;
144 }
145 CA_FOREACH_END()
146 CASSERT(false, "Cannot find watch");
147 }
148
WatchesInit(void)149 void WatchesInit(void)
150 {
151 CArrayInit(&gWatches, sizeof(TWatch));
152 }
WatchesTerminate(void)153 void WatchesTerminate(void)
154 {
155 CA_FOREACH(TWatch, w, gWatches)
156 CArrayTerminate(&w->conditions);
157 CArrayTerminate(&w->actions);
158 CA_FOREACH_END()
159 CArrayTerminate(&gWatches);
160 }
161
ActionRun(Action * a,CArray * mapTriggers)162 static void ActionRun(Action *a, CArray *mapTriggers)
163 {
164 switch (a->Type)
165 {
166 case ACTION_NULL:
167 return;
168
169 case ACTION_SETTRIGGER:
170 for (int i = 0; i < (int)mapTriggers->size; i++)
171 {
172 Trigger *tr = *(Trigger **)CArrayGet(mapTriggers, i);
173 if (tr->id == a->u.index)
174 {
175 tr->isActive = 1;
176 break;
177 }
178 }
179 break;
180
181 case ACTION_CLEARTRIGGER:
182 for (int i = 0; i < (int)mapTriggers->size; i++)
183 {
184 Trigger *tr = *(Trigger **)CArrayGet(mapTriggers, i);
185 if (tr->id == a->u.index)
186 {
187 tr->isActive = 0;
188 break;
189 }
190 }
191 break;
192
193 case ACTION_EVENT:
194 GameEventsEnqueue(&gGameEvents, a->u.Event);
195 break;
196
197 case ACTION_ACTIVATEWATCH:
198 ActivateWatch(a->u.index);
199 break;
200
201 case ACTION_DEACTIVATEWATCH:
202 DeactivateWatch(a->u.index);
203 break;
204 }
205 }
206
ConditionsMet(CArray * conditions,const int ticks)207 static bool ConditionsMet(CArray *conditions, const int ticks)
208 {
209 bool allConditionsMet = true;
210 for (int i = 0; i < (int)conditions->size; i++)
211 {
212 Condition *c = CArrayGet(conditions, i);
213 bool conditionMet = false;
214 switch (c->Type)
215 {
216 case CONDITION_TILECLEAR:
217 conditionMet = TileIsClear(MapGetTile(&gMap, c->Pos));
218 break;
219 }
220 if (conditionMet)
221 {
222 c->Counter += ticks;
223 allConditionsMet =
224 allConditionsMet && c->Counter >= c->CounterMax;
225 }
226 else
227 {
228 c->Counter = 0;
229 allConditionsMet = false;
230 }
231 }
232 return allConditionsMet;
233 }
234
TriggerTryActivate(Trigger * t,const int flags,const struct vec2i tilePos)235 bool TriggerTryActivate(Trigger *t, const int flags, const struct vec2i tilePos)
236 {
237 const bool canActivate =
238 t->isActive && (t->flags == 0 || (t->flags & flags));
239 if (canActivate)
240 {
241 GameEvent e = GameEventNew(GAME_EVENT_TRIGGER);
242 e.u.TriggerEvent.ID = t->id;
243 e.u.TriggerEvent.Tile = Vec2i2Net(tilePos);
244 GameEventsEnqueue(&gGameEvents, e);
245 }
246 else
247 {
248 if (t->cannotActivateLock > 0)
249 {
250 t->cannotActivateLock--;
251 }
252 }
253 return canActivate;
254 }
255
TriggerCannotActivate(const Trigger * t)256 bool TriggerCannotActivate(const Trigger *t)
257 {
258 return t->cannotActivateLock == 0;
259 }
260
TriggerSetCannotActivate(Trigger * t)261 void TriggerSetCannotActivate(Trigger *t)
262 {
263 t->cannotActivateLock = CANNOT_ACTIVATE_LOCK;
264 }
265
TriggerActivate(Trigger * t,CArray * mapTriggers)266 void TriggerActivate(Trigger *t, CArray *mapTriggers)
267 {
268 CA_FOREACH(Action, a, t->actions)
269 ActionRun(a, mapTriggers);
270 CA_FOREACH_END()
271 }
272
UpdateWatches(CArray * mapTriggers,const int ticks)273 void UpdateWatches(CArray *mapTriggers, const int ticks)
274 {
275 CA_FOREACH(TWatch, w, gWatches)
276 if (!w->active) continue;
277 if (ConditionsMet(&w->conditions, ticks))
278 {
279 for (int j = 0; j < (int)w->actions.size; j++)
280 {
281 ActionRun(CArrayGet(&w->actions, j), mapTriggers);
282 }
283 }
284 CA_FOREACH_END()
285 }
286