1 /*
2 	C-Dogs SDL
3 	A port of the legendary (and fun) action/arcade cdogs.
4 
5 	Copyright (c) 2013-2020 Cong Xu
6 	All rights reserved.
7 
8 	Redistribution and use in source and binary forms, with or without
9 	modification, are permitted provided that the following conditions are met:
10 
11 	Redistributions of source code must retain the above copyright notice, this
12 	list of conditions and the following disclaimer.
13 	Redistributions in binary form must reproduce the above copyright notice,
14 	this list of conditions and the following disclaimer in the documentation
15 	and/or other materials provided with the distribution.
16 
17 	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 	AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 	ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 	LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 	CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 	POSSIBILITY OF SUCH DAMAGE.
28 */
29 #include "game_events.h"
30 
31 #include <string.h>
32 
33 #include "actors.h"
34 #include "net_client.h"
35 #include "net_server.h"
36 #include "pickup.h"
37 #include "utils.h"
38 
39 CArray gGameEvents;
40 
GameEventsInit(CArray * store)41 void GameEventsInit(CArray *store)
42 {
43 	CArrayInit(store, sizeof(GameEvent));
44 }
GameEventsTerminate(CArray * store)45 void GameEventsTerminate(CArray *store)
46 {
47 	CArrayTerminate(store);
48 }
49 
50 // Array indexed by GameEvent
51 static GameEventEntry sGameEventEntries[] = {
52 	{GAME_EVENT_NONE, false, false, false, false, NULL},
53 
54 	{GAME_EVENT_CLIENT_CONNECT, false, false, false, false, NULL},
55 	{GAME_EVENT_CLIENT_ID, false, false, false, false, NClientId_fields},
56 	{GAME_EVENT_CAMPAIGN_DEF, false, false, false, false, NCampaignDef_fields},
57 	{GAME_EVENT_PLAYER_DATA, true, false, true, false, NPlayerData_fields},
58 	{GAME_EVENT_PLAYER_REMOVE, true, false, true, false, NPlayerRemove_fields},
59 	{GAME_EVENT_TILE_SET, true, false, true, true, NTileSet_fields},
60 
61 	{GAME_EVENT_THING_DAMAGE, true, false, true, true, NThingDamage_fields},
62 	{GAME_EVENT_MAP_OBJECT_ADD, true, false, true, true, NMapObjectAdd_fields},
63 	{GAME_EVENT_MAP_OBJECT_REMOVE, true, false, true, true,
64 	 NMapObjectRemove_fields},
65 	{GAME_EVENT_CLIENT_READY, false, false, false, false, NULL},
66 	{GAME_EVENT_NET_GAME_START, false, false, false, false, NULL},
67 
68 	{GAME_EVENT_CONFIG, true, false, true, false, NConfig_fields},
69 	{GAME_EVENT_SCORE, true, true, true, true, NScore_fields},
70 	{GAME_EVENT_SOUND_AT, true, false, true, true, NSound_fields},
71 	{GAME_EVENT_SCREEN_SHAKE, false, false, true, true, NULL},
72 	{GAME_EVENT_SET_MESSAGE, false, false, true, true, NULL},
73 
74 	{GAME_EVENT_GAME_START, true, false, true, true, NULL},
75 	{GAME_EVENT_GAME_BEGIN, true, false, true, true, NGameBegin_fields},
76 
77 	{GAME_EVENT_ACTOR_ADD, true, false, true, true, NActorAdd_fields},
78 	{GAME_EVENT_ACTOR_MOVE, true, true, true, true, NActorMove_fields},
79 	{GAME_EVENT_ACTOR_STATE, true, true, true, true, NActorState_fields},
80 	{GAME_EVENT_ACTOR_DIR, true, true, true, true, NActorDir_fields},
81 	{GAME_EVENT_ACTOR_SLIDE, true, true, true, true, NActorSlide_fields},
82 	{GAME_EVENT_ACTOR_IMPULSE, true, false, true, true, NActorImpulse_fields},
83 	{GAME_EVENT_ACTOR_SWITCH_GUN, true, true, true, true,
84 	 NActorSwitchGun_fields},
85 	{GAME_EVENT_ACTOR_PICKUP_ALL, false, true, true, true,
86 	 NActorPickupAll_fields},
87 	{GAME_EVENT_ACTOR_REPLACE_GUN, true, false, true, true,
88 	 NActorReplaceGun_fields},
89 	{GAME_EVENT_ACTOR_HEAL, true, false, true, true, NActorHeal_fields},
90 	{GAME_EVENT_ACTOR_ADD_AMMO, true, false, true, true, NActorAddAmmo_fields},
91 	{GAME_EVENT_ACTOR_USE_AMMO, true, true, true, true, NActorUseAmmo_fields},
92 	{GAME_EVENT_ACTOR_DIE, true, false, true, true, NActorDie_fields},
93 	{GAME_EVENT_ACTOR_MELEE, true, true, true, true, NActorMelee_fields},
94 	{GAME_EVENT_ACTOR_PILOT, true, true, true, true, NActorPilot_fields},
95 
96 	{GAME_EVENT_ADD_PICKUP, true, false, true, true, NAddPickup_fields},
97 	{GAME_EVENT_REMOVE_PICKUP, true, false, true, true, NRemovePickup_fields},
98 
99 	{GAME_EVENT_BULLET_BOUNCE, true, false, true, true, NBulletBounce_fields},
100 	{GAME_EVENT_REMOVE_BULLET, true, false, true, true, NRemoveBullet_fields},
101 	{GAME_EVENT_PARTICLE_REMOVE, false, false, true, true, NULL},
102 	{GAME_EVENT_GUN_FIRE, true, true, true, true, NGunFire_fields},
103 	{GAME_EVENT_GUN_RELOAD, true, true, true, true, NGunReload_fields},
104 	{GAME_EVENT_GUN_STATE, true, true, true, true, NGunState_fields},
105 	{GAME_EVENT_ADD_BULLET, true, false, true, true, NAddBullet_fields},
106 	{GAME_EVENT_ADD_PARTICLE, false, false, true, true, NULL},
107 	{GAME_EVENT_TRIGGER, true, false, true, true, NTrigger_fields},
108 	{GAME_EVENT_EXPLORE_TILES, true, false, true, true, NExploreTiles_fields},
109 	{GAME_EVENT_RESCUE_CHARACTER, true, false, true, true,
110 	 NRescueCharacter_fields},
111 	{GAME_EVENT_OBJECTIVE_UPDATE, true, false, true, true,
112 	 NObjectiveUpdate_fields},
113 	{GAME_EVENT_ADD_KEYS, true, false, true, true, NAddKeys_fields},
114 
115 	{GAME_EVENT_MISSION_COMPLETE, true, false, true, true,
116 	 NMissionComplete_fields},
117 
118 	{GAME_EVENT_MISSION_INCOMPLETE, true, false, true, true, NULL},
119 	{GAME_EVENT_MISSION_PICKUP, true, false, true, true, NULL},
120 	{GAME_EVENT_MISSION_END, true, false, true, true, NMissionEnd_fields}};
GameEventGetEntry(const GameEventType e)121 GameEventEntry GameEventGetEntry(const GameEventType e)
122 {
123 	return sGameEventEntries[(int)e];
124 }
125 
GameEventsEnqueue(CArray * store,GameEvent e)126 void GameEventsEnqueue(CArray *store, GameEvent e)
127 {
128 	if (store->elemSize == 0)
129 	{
130 		return;
131 	}
132 	// If we're the server, broadcast any events that clients need
133 	// If we're the client, pass along to server, but only if it's for a local
134 	// player Otherwise we'd ping-pong the same updates from the server
135 	const GameEventEntry gee = sGameEventEntries[e.Type];
136 	if (gee.Broadcast)
137 	{
138 		NetServerSendMsg(&gNetServer, NET_SERVER_BCAST, gee.Type, &e.u);
139 	}
140 	if (gee.Submit)
141 	{
142 		int actorUID = -1;
143 		bool actorIsLocal = false;
144 		switch (e.Type)
145 		{
146 		case GAME_EVENT_ACTOR_MOVE:
147 			actorUID = e.u.ActorMove.UID;
148 			break;
149 		case GAME_EVENT_ACTOR_STATE:
150 			actorUID = e.u.ActorState.UID;
151 			break;
152 		case GAME_EVENT_ACTOR_DIR:
153 			actorUID = e.u.ActorDir.UID;
154 			break;
155 		case GAME_EVENT_ACTOR_SLIDE:
156 			actorUID = e.u.ActorSlide.UID;
157 			break;
158 		case GAME_EVENT_ACTOR_SWITCH_GUN:
159 			actorUID = e.u.ActorSwitchGun.UID;
160 			break;
161 		case GAME_EVENT_ACTOR_PICKUP_ALL:
162 			actorUID = e.u.ActorPickupAll.UID;
163 			break;
164 		case GAME_EVENT_ACTOR_USE_AMMO:
165 			actorUID = e.u.UseAmmo.UID;
166 			break;
167 		case GAME_EVENT_ACTOR_MELEE:
168 			actorUID = e.u.Melee.UID;
169 			break;
170 		case GAME_EVENT_ACTOR_PILOT:
171 			actorUID = e.u.Pilot.UID;
172 			break;
173 		case GAME_EVENT_GUN_FIRE:
174 			if (e.u.GunFire.IsGun)
175 			{
176 				actorUID = e.u.GunFire.ActorUID;
177 			}
178 			break;
179 		case GAME_EVENT_GUN_RELOAD:
180 			actorIsLocal = PlayerIsLocal(e.u.GunReload.PlayerUID);
181 			break;
182 		case GAME_EVENT_GUN_STATE:
183 			actorUID = e.u.GunState.ActorUID;
184 			break;
185 		default:
186 			break;
187 		}
188 		if (actorUID >= 0)
189 		{
190 			actorIsLocal = ActorIsLocalPlayer(actorUID);
191 		}
192 		if (actorIsLocal)
193 		{
194 			NetClientSendMsg(&gNetClient, gee.Type, &e.u);
195 		}
196 	}
197 
198 	CArrayPushBack(store, &e);
199 }
200 static bool EventComplete(const void *elem);
GameEventsClear(CArray * store)201 void GameEventsClear(CArray *store)
202 {
203 	CArrayRemoveIf(store, EventComplete);
204 }
EventComplete(const void * elem)205 static bool EventComplete(const void *elem)
206 {
207 	return ((const GameEvent *)elem)->Delay < 0;
208 }
209 
GameEventNew(GameEventType type)210 GameEvent GameEventNew(GameEventType type)
211 {
212 	GameEvent e;
213 	memset(&e, 0, sizeof e);
214 	e.Type = type;
215 	switch (type)
216 	{
217 	case GAME_EVENT_MAP_OBJECT_REMOVE:
218 		e.u.MapObjectRemove.ActorUID = -1;
219 		break;
220 	case GAME_EVENT_ADD_PICKUP:
221 		e.u.AddPickup.UID = PickupsGetNextUID();
222 		e.u.AddPickup.SpawnerUID = -1;
223 		e.u.AddPickup.has_Pos = true;
224 		break;
225 	case GAME_EVENT_ACTOR_ADD:
226 		e.u.ActorAdd.PilotUID = -1;
227 		e.u.ActorAdd.VehicleUID = -1;
228 		e.u.ActorAdd.PlayerUID = -1;
229 		e.u.ActorAdd.Direction = rand() % DIRECTION_COUNT;
230 		e.u.ActorAdd.has_Pos = true;
231 		break;
232 	case GAME_EVENT_ACTOR_ADD_AMMO:
233 		e.u.AddAmmo.has_Ammo = true;
234 		break;
235 	case GAME_EVENT_ACTOR_IMPULSE:
236 		e.u.ActorImpulse.has_Vel = true;
237 		e.u.ActorImpulse.has_Pos = true;
238 		break;
239 	case GAME_EVENT_ACTOR_MOVE:
240 		e.u.ActorMove.has_Pos = true;
241 		e.u.ActorMove.has_MoveVel = true;
242 		break;
243 	case GAME_EVENT_ACTOR_SLIDE:
244 		e.u.ActorSlide.has_Vel = true;
245 		break;
246 	case GAME_EVENT_ACTOR_USE_AMMO:
247 		e.u.UseAmmo.has_Ammo = true;
248 		break;
249 	case GAME_EVENT_ADD_BULLET:
250 		e.u.AddBullet.ActorUID = -1;
251 		e.u.AddBullet.has_MuzzlePos = true;
252 		break;
253 	case GAME_EVENT_ADD_KEYS:
254 		e.u.AddKeys.has_Pos = true;
255 		break;
256 	case GAME_EVENT_BULLET_BOUNCE:
257 		e.u.BulletBounce.has_Vel = true;
258 		e.u.BulletBounce.has_Pos = true;
259 		e.u.BulletBounce.has_BouncePos = true;
260 		break;
261 	case GAME_EVENT_GUN_FIRE:
262 		e.u.GunFire.has_MuzzlePos = true;
263 		break;
264 	case GAME_EVENT_GUN_RELOAD:
265 		e.u.GunReload.has_Pos = true;
266 		break;
267 	case GAME_EVENT_MAP_OBJECT_ADD:
268 		e.u.MapObjectAdd.has_Pos = true;
269 		e.u.MapObjectAdd.has_Mask = true;
270 		break;
271 	case GAME_EVENT_SOUND_AT:
272 		e.u.SoundAt.has_Pos = true;
273 		break;
274 	case GAME_EVENT_THING_DAMAGE:
275 		e.u.ThingDamage.has_Vel = true;
276 		break;
277 	case GAME_EVENT_TILE_SET:
278 		e.u.TileSet.has_Pos = true;
279 		break;
280 	case GAME_EVENT_TRIGGER:
281 		e.u.TriggerEvent.has_Tile = true;
282 		break;
283 	case GAME_EVENT_PLAYER_DATA:
284 		e.u.PlayerData.has_Colors = true;
285 		e.u.PlayerData.has_Stats = true;
286 		e.u.PlayerData.has_Totals = true;
287 		break;
288 	default:
289 		break;
290 	}
291 	return e;
292 }
GameEventNewActorAdd(const struct vec2 pos,const Character * c,const bool isNPC)293 GameEvent GameEventNewActorAdd(const struct vec2 pos, const Character *c, const bool isNPC)
294 {
295 	GameEvent e = GameEventNew(GAME_EVENT_ACTOR_ADD);
296 	e.u.ActorAdd.Pos = Vec2ToNet(pos);
297 	e.u.ActorAdd.UID = ActorsGetNextUID();
298 	if (!c->Class->Vehicle)
299 	{
300 		e.u.ActorAdd.PilotUID = e.u.ActorAdd.UID;
301 	}
302 	e.u.ActorAdd.Health = CharacterGetStartingHealth(c, isNPC && !c->Class->Vehicle);
303 	return e;
304 }
305