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