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