1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
Distance = function(x1, y1, x2, y2) -- Returns the distance between two points.
return math.floor(math.sqrt(math.pow(x1 - x2, 2) + math.pow(y1 - y2, 2)))
end
projectiles = {} -- Table to store ALL projectiles in game
CurrentProjectileSetting = "Grenades"
-- Please mind that other types of variables might be needed for certain projectiles to work.
function CreateProjectile(id,type,distance,ff) -- id of the player who creates projectile,type of projectile and the distance parameter for it.
	local projectile = {}
	---- GENERAL ----
	if type == "bullet" then -- Normal bullets. They don't work as CS2D bullets but one can create various stuff from these.
		projectile = { -- table for the projectile. A new table for each projectile.
		type = type, -- What type of projectile is it ?
		id=id, -- Who owns the projectile ? In most cases, you might want projectiles to belong to a player (since usually he's the one to create them)
		name = "bullet", -- The name of projectile (used for custom killing)
		x=player(id,"x"), -- Coordinate of projectile on X axis
		y=player(id,"y"), -- Coordinate of projectile on Y axis
		angle=player(id,"rot"), -- How is projectile rotated ?
		imageangle=player(id,"rot"), -- How is the image angle rotated ?
		state = "flying", -- State of projectile ?
		ff = ff, -- Friendly Fire projectile (can affect teammates) ?
		speed=12, -- How fast the bullet flies
		damage=15, -- How much damage the bullet will make. Note : in case you want to have differet damage, you can create another argument for function, check if it's a bullet and then apply damage as "damage=yourvariableofdamage"
		image=image("gfx/sprites/RainingMammoths/Jail/bullet.bmp",0,0,0) -- Creates image for your projectile.
		}
	elseif type == "bullet_bouncing" then -- Bouncing bullets. They are like bullets shown above except that they can bounce off walls.
		projectile = {
		type = type,
		id=id,
		name = "bouncing bullet",
		x=player(id,"x"),
		y=player(id,"y"),
		angle=player(id,"rot"),
		imageangle=player(id,"rot"),
		state = "flying",
		ff = ff,
		speed=12,
		damage=15,
		bounces = 15, -- How many bounces the bullet can make before it disappears. Note : the bullets disappear instantly if they hit a player.
		deceleration = 0.25, -- How much speed the bullet loses when it hits a wall ? Note : set this value to 0 if you don't want the speed to change.
		image=image("gfx/sprites/RainingMammoths/Jail/bullet.bmp",0,0,0)
		}
	elseif type == "grenade_sticky" then
		projectile = {
		type = type,
		id=id,
		name = "sticky grenade",
		x=player(id,"x"),
		y=player(id,"y"),
		endx = endx, -- Where the player threw grenade on X axis
		endy = endy, -- Where the player threw grenade on Y axis
		angle=player(id,"rot"),
		imageangle=player(id,"rot"),
		state = "flying",
		ff = ff,
		speed=5,
		damage=90,
		radius=320, -- area of effect (AOE)
		distance = distance,
		attached = nil, -- player it is attached to. 0 if it's a wall.
		time = Player[id].stickytimer, -- time until explosion
		image=image("gfx/cursor.bmp",0,0,0)
		}
	elseif type == "grenade_chill" then
		projectile = {
		type = type,
		id=id,
		name = "chill grenade",
		x=player(id,"x"),
		y=player(id,"y"),
		endx = endx,
		endy = endy,
		angle=player(id,"rot"),
		imageangle=player(id,"rot"),
		state = "flying",
		ff = ff,
		speed=5,
		damage=0,
		radius=80,
		lifetime = 30, -- How many seconds will the grenade work on ground before disappearing
		distance = distance,
		image=image("gfx/sprites/RainingMammoths/Grenades/"..type..".bmp<b>",0,0,0)
		}
	elseif type == "grenade_a" then
		projectile = {
		type = type,
		id=id,
		name = "grenade",
		x=player(id,"x"),
		y=player(id,"y"),
		endx = endx,
		endy = endy,
		angle=player(id,"rot"),
		imageangle=player(id,"rot"),
		state = "flying",
		speed=5,
		damage=15,
		radius=320,
		lifetime = 30,
		distance = distance,
		image=image("gfx/sprites/RainingMammoths/Jail/bullet.bmp",0,0,0)
		}
	---- Artefighters ----
	elseif type == "AF_a" then
		projectile = {
		type = type,
		id=id,
		name = "Artefact name",
		x=player(id,"x"),
		y=player(id,"y"),
		endx = endx,
		endy = endy,
		angle=player(id,"rot"),
		imageangle=player(id,"rot"),
		state = "flying",
		speed=5,
		damage=15,
		radius=320,
		lifetime = 30,
		distance = distance,
		image=image("gfx/sprites/RainingMammoths/Jail/bullet.bmp",0,0,0)
	end
	projectiles[#projectiles+1] = projectile -- Add the projectile inside the projectiles table so the information could be used later.
	imagepos(projectile.image,projectile.x,projectile.y,player(id,"rot")) -- Set the image of projectile at where the player stands as well as angle.
	return projectile -- End the creating function, returning the information of projectile.
end
function UnitAngle(rot) -- Change degrees to radians
	return math.rad(rot)
end
function FlyingProjectile(projectile) -- How the projectile flies.
	if projectile.state ~= "ground" then
		local newx = projectile.x + math.cos(UnitAngle(projectile.angle-90))*projectile.speed -- New position of projectile on X axis
		local newy = projectile.y + math.sin(UnitAngle(projectile.angle-90))*projectile.speed -- New position of projectile on Y axis
		if projectile.type == "bullet_bouncing" then -- See what kind of projectile is flying
			if tile(math.floor(newx/32),math.floor(newy/32),"wall") then -- Since it's a bouncing bullet, check if it would hit the wall
				if projectile.bounces > 0 then -- if it would see if it still has any bounces left
					projectile.bounces = projectile.bounces - 1 -- if it does, reduce them
					projectile.angle = -projectile.angle -- and make the bullet bounce
					local newx = projectile.x + math.cos(UnitAngle(projectile.angle-90))*projectile.speed -- calculate the new position of bullet on X axis
					local newy = projectile.y + math.sin(UnitAngle(projectile.angle-90))*projectile.speed -- calculate the new position of bullet on Y axis
					if tile(math.floor(newx/32),math.floor(newy/32),"wall") then -- see if it STILL would hit the wall
						projectile.angle = projectile.angle + 180 -- if it would, make it bounce
					end
					projectile.speed = projectile.speed - projectile.deceleration -- If the deceleration is above 0, make the bullet slower
					projectile.x = projectile.x + math.cos(UnitAngle(projectile.angle-90))*projectile.speed
					projectile.y = projectile.y + math.sin(UnitAngle(projectile.angle-90))*projectile.speed -- Finally, actually move the projectile so it could carry on, flying.
				else -- if the projectile doesn't have any bounces left
					freeimage(projectile.image) -- delete the image of the projectile
					local tablepos
					for k, v in pairs(projectiles) do -- remove the projectile from all since it's no longer needed.
						if projectile.image == v.image then
							tablepos = k
						end
					end
					table.remove(projectiles,tablepos)
				end
			else -- if it's not the case that it hits a wall, carry on flying.
				projectile.x = newx
				projectile.y = newy
			end
		end
		if projectile.type:sub(1,7)=="grenade" then
			if projectile.distance > 0 then
				if projectile.type ~= "grenade_cracker" or projectile.type ~= "grenade_sticky" then
					if tile(math.floor(newx/32),math.floor(newy/32),"wall") then -- Since grenades bounce, check if they hit the wall
						projectile.angle = -projectile.angle -- bounce
						local newx = projectile.x + math.cos(UnitAngle(projectile.angle-90))*projectile.speed -- calculate the new position of projectile on X axis
						local newy = projectile.y + math.sin(UnitAngle(projectile.angle-90))*projectile.speed -- calculate the new position of projectile on Y axis
						if tile(math.floor(newx/32),math.floor(newy/32),"wall") then -- see if it still hits the wall
							projectile.angle = projectile.angle + 180 -- if it does, make it bounce again
						end
						projectile.x = projectile.x + math.cos(UnitAngle(projectile.angle-90))*projectile.speed
						projectile.y = projectile.y + math.sin(UnitAngle(projectile.angle-90))*projectile.speed -- Finally, actually move the projectile so it could carry on, flying.
					else -- if it's not the case that it hits a wall, carry on flying.
						projectile.x = newx
						projectile.y = newy
					end
					projectile.distance = projectile.distance - projectile.speed
					if projectile.distance <= 0 then
						EndOfPath(projectile)
					end
				else
					if projectile.type == "grenade_sticky" then
						local newx = projectile.x + math.cos(UnitAngle(projectile.angle-90))*projectile.speed -- calculate the new position of projectile on X axis
						local newy = projectile.y + math.sin(UnitAngle(projectile.angle-90))*projectile.speed -- calculate the new position of projectile on Y axis
						if tile(math.floor(newx/32),math.floor(newy/32),"wall") then
							projectile.attached = 0
							projectile.state = "ground"
						end
						for k,v in pairs (player(0,"tableliving")) do
							if Distance(projectile.x,projectile.y,player(v,"x"),player(v,"y"))<16 and projectile.attached == nil then
								projectile.attached = v
								break
							end
						end
						if projectile.attached > 0 and projectile.attached ~= nil then
							projectile.x = player(projectile.attached,"x")
							projectile.y = player(projectile.attached,"y")
							projectile.distance = 0
						end
					elseif projectile.type == "grenade_cracker" then
						for k,v in pairs (player(0,"tableliving")) do
							if Distance(projectile.x,projectile.y,player(v,"x"),player(v,"y"))<16 and projectile.attached == nil then
								projectile.distance = 1
								EndOfPath(projectile)
								break
							end
						end
						if tile(math.floor(newx/32),math.floor(newy/32),"wall") then
							projectile.distance = 1
							EndOfPath(projectile)
							return
						end
					end
					projectile.x = newx
					projectile.y = newy
					projectile.distance = projectile.distance - projectile.speed
					if projectile.distance <= 0 then
						EndOfPath(projectile)
					end
				end
				projectile.imageangle = projectile.imageangle + 8
			else
				EndOfPath(projectile)
			end
		end
		imagepos(projectile.image,projectile.x,projectile.y,projectile.imageangle) -- Move the image of projectile.
	end
end
function EndOfPath(projectile)
	if projectile.type == "grenade_cracker" then
		projectile.state = "ground"
		Effect(projectile)
		DestroyProjectile(projectile)
	elseif projectile.type == "grenade_chill" then
		Effect(projectile)
	end
	for _,id in pairs (player(0,"tableliving")) do
		if Distance(projectile.x,projectile.y,player(id,"x"),player(id,"y"))<405 then
			parse("sv_sound2 "..id.." RainingMammoths/Grenades/"..projectile.type..".wav")
		end
	end
end
--[[Sticky Grenade. -- Sticks to a player or wall.
Chill Grenade. -- Freezes and damages
Paralyze Grenade. -- Stop movement for short amount of time
Poison Grenade. -- Does poison. Higher damage inside, when out, 5 seconds after damages less.
Confusion Grenade. -- Flash/Shake strong inside, weak when out
Unstable Grenade. -- Weak,small explosions as it flies, minor change of angle, high explosion when timer ends.
Ambush Grenade. -- works like a trap
Sharpnel Grenade. -- Spawns 4 to 6 bullet projectiles randomly when it explodes. 80 Damage explosion
Gravity Grenade. -- Creates a forcefield that traps player. The more players inside, the faster the effect ends. 2 modes. Ask pseudon about second cause forgot.
Cracker Grenade. -- Explodes on impact
Reaper Grenade. -- Marks player, if he's killed within 30 seconds, 50 HP is restored to killer
Light Grenade -- If a player comes nearby, a variable of his is increased, if that variable is above 0 it reduces itself and flashes player a bit ]]
function Effect(projectile)
	if projectile.type == ("grenade_sticky" or "grenade_cracker") then
		parse("explosion "..projectile.x.." "..projectile.y.." "..projectile.size.." "..projectile.damage.." "..projectile.id)
	elseif projectile.type == "grenade_chill" then
		projectile.state = "ground"
	end
end		
function DestroyProjectile(projectile)
	freeimage(projectile.image)
	local tablepos
	for k, v in pairs(projectiles) do
		if projectile.image == v.image then
			tablepos = k
		end
	end
	table.remove(projectiles,tablepos)
end
addhook("endround","ClearField") -- Make everything go woosh..
function ClearField()
	for k, v in pairs(projectiles) do
		DestroyProjectile(v)
	end
end
function OnHit(projectile,id)
	projectile = projectiles[projectile]
	if projectile.type == ("bullet" or "bullet_bouncing") then
		projectile = projectiles[projectile]
		if projectile and projectile.id ~= id then
			if Distance(projectile.x, projectile.y, player(id,"x"), player(id,"y"))<=16 then
				if player(id,"team")~=player(projectile.id,"team") then
					local damage = projectile.damage
					parse("sethealth "..id.." "..player(id,"health")-damage)
						if damage > player(id,"health") then
							parse("customkill "..projectile.id.." BouncingBullet "..id)
						end
					freeimage(projectile.image)
					local tablepos
					for k, v in pairs(projectiles) do
						if projectile.image == v.image then
							tablepos = k
						end
					end
					table.remove(projectiles,tablepos)
				end
			end
		end
	end
end
-- Remove this part and put in separate lua
addhook("spawn","_spawn")
function _spawn(id)
	Player[id] = {}
	Player[id].currenttimer = 5
	Player[id].onhudtimer = Player[id].currenttimer
	Player[id].stickytimer = 5
	Player[id].chillgrenade = 0
	Player[id].ineffectzone = false
end
addhook("join","_join")
function _join(id)
	Player[id] = {}
	Player[id].currenttimer = 5
	Player[id].onhudtimer = Player[id].currenttimer
	Player[id].stickytimer = 5
	Player[id].chillgrenade = 0
	Player[id].ineffectzone = false
end
addhook("leave","_leave")
function _leave(id)
	Player[id] = {}
	Player[id].currenttimer = 5
	Player[id].onhudtimer = Player[id].currenttimer
	Player[id].stickytimer = 5
	Player[id].ineffectzone = false
end
-- Until here
addhook("ms100","GroundProjectile") -- Frequent Projectile Behaviour
function GroundProjectile()
	for k,v in pairs (projectiles) do
		if v.state == "ground" then
			if v.type == "grenade_chill" then
				parse("effect \"colorsmoke\" "..v.x.." "..v.y.." 5 64 0 170 255")
			end
			v.lifetime = v.lifetime - 0.1 -- Take away one tenth of second
			if v.lifetime <= 0 then
				DestroyProjectile(v)
			end
		end
	end
end
addhook("second","CheckEffectZone") -- Infrequent Projectile Behaviour
function CheckEffectZone()
	for k,v in pairs (projectiles) do
		if v.state == "ground" then
			for _,id in pairs (player(0,"tableliving")) do
				if Distance(v.x,v.y,player(id,"x"),player(id,"y"))<v.radius then
					Player[id].ineffectzone = true
					if affectsEverybody == false then
						if player(id,"team")~=player(v.id,"team") then
							AffectPerson(v,id)
						end
					else
						AffectPerson(v,id)
					end
				else
					Player[id].ineffectzone = true
				end
			end
		end
	end
	if CurrentProjectileSetting == "Grenades" then
		for _,id in pairs (player(0,"tableliving")) do
			if Player[id].ineffectzone == false then
				Player[id].chillgrenade = 0
			end
		end
	end
end
function AffectPerson(projectile,id)
	if projectile.type == "grenade_chill" then
		Player[id].chillgrenade = Player[id].chillgrenade + 1
		parse("speedmod "..id.." "..-Player[id].chillgrenade)
		
		local chill = 7+Player[id].chillgrenade
		if player(id,"health")<chill then
			parse("customkill "..projectile.id.." freeze "..id)
		else
			parse("sethealth "..id.." "..player(id,"health")-chill)
		end
	end
end
addhook("always","UpdateWorld")
function UpdateWorld()
	for k,v in pairs (projectiles) do
		FlyingProjectile(v)
		for _,id in pairs (player(0,"tableliving")) do
			OnHit(k,id) -- KID
		end
	end
end
addhook("attack","test")
function test(id)
	reqcld(id,2)
end
addhook("clientdata","testB")
function testB(id,mode,d1,d2)
	if mode == 2 then
		CreateProjectile(id,"grenade_chill",Distance(player(id,"x"),player(id,"y"),d1,d2),d1,d2)
	end
end
print("\169000255000Projectiles added !")