1--------------------------------
2-- HIGHLANDER / HOGS OF WAR
3-- by mikade
4--------------------------------
5
6-------------------------
7-- ideas for the future
8-------------------------
9-- add structure
10-- allow switcher, resurrector
11-- nerf teleport
12-- balance weapon distribution across entire team / all teams
13-- add other inequalities/bonuses like... ???
14-- * some hogs start off with an extra 25 health?
15-- * some hogs start off poisoned?
16-- * some hogs start off with a rope and 2 drills but die after their turn?
17
18------------------
19-- script follows
20------------------
21
22HedgewarsScriptLoad("/Scripts/Locale.lua")
23HedgewarsScriptLoad("/Scripts/Tracker.lua")
24HedgewarsScriptLoad("/Scripts/Params.lua")
25
26-- These define weapons allowed by the script.
27local atkWeps = {
28	[amBazooka]=true, [amBee]=true, [amMortar]=true, [amDrill]=true, [amSnowball]=true,
29	[amGrenade]=true, [amClusterBomb]=true, [amMolotov]=true, [amWatermelon]=true,
30	[amHellishBomb]=true, [amGasBomb]=true, [amShotgun]=true, [amDEagle]=true,
31	[amFlamethrower]=true, [amSniperRifle]=true, [amSineGun]=true, [amMinigun]=true,
32	[amFirePunch]=true, [amWhip]=true, [amBaseballBat]=true, [amKamikaze]=true,
33	[amSeduction]=true, [amHammer]=true, [amMine]=true, [amDynamite]=true, [amCake]=true,
34	[amBallgun]=true, [amSMine]=true, [amRCPlane]=true, [amBirdy]=true, [amKnife]=true,
35	[amAirAttack]=true, [amMineStrike]=true, [amNapalm]=true, [amDrillStrike]=true, [amPiano]=true, [amAirMine] = true,
36	[amCreeper]=true,
37}
38
39local utilWeps =  {
40	[amBlowTorch]=true, [amPickHammer]=true, [amGirder]=true, [amPortalGun]=true,
41	[amRope]=true, [amParachute]=true, [amTeleport]=true, [amJetpack]=true,
42	[amInvulnerable]=true, [amLaserSight]=true, [amVampiric]=true,
43	[amLowGravity]=true, [amExtraDamage]=true, [amExtraTime]=true,
44	[amLandGun]=true, [amRubber]=true, [amIceGun]=true,
45}
46
47-- Intentionally left out:
48-- * Resurrector (guaranteed to screw up the game)
49-- * Time Box
50-- * Switch Hedgehog (not sure why)
51
52local wepArray = {}
53
54local atkChoices = {}
55local utilChoices = {}
56
57local currHog
58local lastHog
59local started = false
60local switchStage = 0
61
62local lastWep = amNothing
63local shotsFired = 0
64
65local probability = {1,2,5,10,20,50,200,500,1000000};
66local atktot = 0
67local utiltot = 0
68
69local teleportConverted = false -- used for special handling of teleport when gfPlaceHog is active
70
71-- Script parameter stuff
72
73--[[ Loyal Highlander.
74If true, killing hogs of your own clan doesn't give you their weapons.
75Otherwise, killing any hog gives you their weapons. ]]
76local loyal = false
77
78
79--[[ Multiple weapon usages.
80This is a bit tricky to explain.
81First, remind yourselves that hogs can never hold more than 1 of the same ammo type.
82
83This param changes how ammo will be restocked after killing a hog if you
84already owned this ammo.
85Basically this is about if you can use the same weapon multiple times in a
86turn by killing enemies in a clever way.
87We need to distinguish between your current inventory and the “reset inventory”,
88that is, the state to which your inventory will get reset in the next turn.
89
90No Multi-Use (default):
91	If you kill a hog who owns a weapon you currently have in your reset inventory,
92	but not your inventory, you DO NOT get this weapon again.
93
94Multi-Use:
95	If you kill a hog who owns a weapon you currently have in your reset inventory,
96	but not your inventory, you DO get this weapon.
97
98Example 1:
99	You have a ballgun, and use it to kill a hog who also owns a ballgun.
100	No Multi-Use: You will NOT get another ballgun, since it's in your
101	reset inventory.
102	Multi-Use: You get another ballgun.
103
104Example 2:
105	You have a grenade and a bazooka in your inventory. You use the bazooka
106	to kill a hedgehog who owns a grenade.
107	In both ammo limit modes, you do NOT win any ammo since you already have
108	a grenade in your inventory (not just your reset inventory), and the
109	rule “no more than 1 ammo per type” applies.
110]]
111local multiUse = false
112
113function onParameters()
114	parseParams()
115	multiUse = params["multiuse"] == "true"
116	loyal = params["loyal"] == "true"
117end
118
119function CheckForWeaponSwap()
120	if GetCurAmmoType() ~= lastWep then
121		shotsFired = 0
122	end
123	lastWep = GetCurAmmoType()
124end
125
126function onSlot()
127	CheckForWeaponSwap()
128end
129
130function onSetWeapon()
131	CheckForWeaponSwap()
132end
133
134function onHogAttack()
135	CheckForWeaponSwap()
136	shotsFired = shotsFired + 1
137end
138
139function StartingSetUp(gear)
140	for i = 0, AmmoTypeMax do
141		if i ~= amNothing then
142			setGearValue(gear,i,0)
143		end
144	end
145	for w,c in pairs(wepArray) do
146		if c == 9 and (atkWeps[w] or utilWeps[w])  then
147			setGearValue(gear,w,1)
148		end
149	end
150
151	setGearValue(gear,amSkip,100)
152
153	local r = 0
154	if atktot > 0 then
155		r = GetRandom(atktot)+1
156		for i = 0, AmmoTypeMax do
157			if i ~= amNothing then
158				if atkChoices[i] >= r then
159					setGearValue(gear,i,1)
160					break
161				end
162			end
163		end
164	end
165	if utiltot > 0 then
166		r = GetRandom(utiltot)+1
167		for i = 0, AmmoTypeMax do
168			if i ~= amNothing then
169				if utilChoices[i] >= r then
170					setGearValue(gear,i,1)
171					break
172				end
173			end
174		end
175	end
176end
177
178function ConvertValues(gear)
179	for w,c in pairs(wepArray) do
180		-- Add hog ammo loadout, but don't touch teleport if in hog placement phase.
181		-- If in hog placement phase, teleport will be touched later (see onNewTurn).
182		if not (GetGameFlag(gfPlaceHog) and TotalRounds == -1 and (w == amTeleport)) then
183			AddAmmo(gear, w, getGearValue(gear,w) )
184		end
185	end
186end
187
188-- this is called when a hog dies
189function TransferWeps(gear)
190
191	if CurrentHedgehog ~= nil and CurrentHedgehog ~= gear and (not loyal or (GetHogClan(CurrentHedgehog) ~= GetHogClan(gear))) then
192
193		local x,y,color
194		local vgear
195		local dspl = IsHogLocal(CurrentHedgehog)
196		local ammolist = ''
197
198		if dspl then
199			x,y = GetGearPosition(CurrentHedgehog)
200			color = GetClanColor(GetHogClan(CurrentHedgehog))
201		end
202
203		for w,c in pairs(wepArray) do
204			local val = getGearValue(gear,w)
205			if val ~= 0 and (multiUse or (wepArray[w] ~= 9 and getGearValue(CurrentHedgehog, w) == 0))  then
206				setGearValue(CurrentHedgehog, w, val)
207
208				-- if you are using multi-shot weapon, gimme one more
209				if (GetCurAmmoType() == w) and (shotsFired ~= 0) then
210					AddAmmo(CurrentHedgehog, w, val+1)
211				-- assign ammo as per normal
212				else
213					AddAmmo(CurrentHedgehog, w, val)
214				end
215				if dspl then
216					if ammolist == '' then
217						ammolist = GetAmmoName(w)
218					else
219						ammolist = ammolist .. ' • ' .. GetAmmoName(w)
220					end
221					x = x + 2
222					y = y + 32
223					vgear = AddVisualGear(x, y, vgtAmmo, 0, true)
224					if vgear ~= nil then
225						local vgtFrame = w
226						SetVisualGearValues(vgear,nil,nil,nil,nil,nil,vgtFrame)
227					end
228				end
229
230			end
231		end
232
233		if dspl and ammolist ~= '' then
234			PlaySound(sndShotgunReload);
235			AddCaption(ammolist, color, capgrpAmmoinfo)
236		end
237	end
238
239end
240
241function onGameInit()
242	EnableGameFlags(gfInfAttack, gfPerHogAmmo)
243	DisableGameFlags(gfResetWeps, gfSharedAmmo)
244	HealthCaseProb = 100
245	if loyal then
246		Goals = loc("Loyal Highlander: Eliminate enemy hogs to take their weapons") .. "|"
247	else
248		Goals = loc("Highlander: Eliminate hogs to take their weapons") .. "|"
249	end
250	Goals = Goals .. loc("Replenishment: Weapons are restocked on turn start of a new hog") .. "|" ..
251	loc("Ammo Limit: Hogs can’t have more than 1 ammo per type") .. "|"
252	if multiUse then
253		Goals = Goals .. loc("Multi-Use: You can take and use the same ammo type multiple times in a turn")
254	else
255		Goals = Goals .. loc("No Multi-Use: Once you used an ammo, you can’t take it again in this turn")
256	end
257end
258
259function onGameStart()
260	-- Remove air strikes in maps with border
261	if MapHasBorder() or GetGameFlag(gfBorder) then
262		atkWeps[amAirAttack] = nil
263		atkWeps[amMineStrike] = nil
264		atkWeps[amNapalm] = nil
265		atkWeps[amDrillStrike] = nil
266		atkWeps[amPiano] = nil
267	end
268	-- Disable redundant utilities
269	if GetGameFlag(gfVampiric) then
270		utilWeps[amVampiric] = nil
271	end
272	if GetGameFlag(gfLowGravity) then
273		utilWeps[amLowGravity] = nil
274	end
275	if GetGameFlag(gfLaserSight) then
276		utilWeps[amLaserSight] = nil
277	end
278	if GetGameFlag(gfInvulnerable) then
279		utilWeps[amInvulnerable] = nil
280	end
281	if TurnTime > 999000 then
282		utilWeps[amExtraTime] = nil
283	end
284	utilChoices[amSkip] = 0
285	local c = 0
286	for i = 0, AmmoTypeMax do
287		if i ~= amNothing then
288			atkChoices[i] = 0
289			utilChoices[i] = 0
290			if i ~= 7 then
291				wepArray[i] = 0
292				c = GetAmmo(i)
293				if c > 8 then
294					c = 9
295				end
296				wepArray[i] = c
297				if c < 9 and c > 0 then
298					if atkWeps[i] then
299						atktot = atktot + probability[c]
300						atkChoices[i] = atktot
301					elseif utilWeps[i] then
302						utiltot = utiltot + probability[c]
303						utilChoices[i] = utiltot
304					end
305				end
306			end
307		end
308	end
309
310	runOnGears(StartingSetUp)
311	runOnGears(ConvertValues)
312end
313
314function CheckForHogSwitch()
315
316	--[[ Restock the weapons of the hog on turn start, provided it is not the same hog as before.
317	This exception is done do avoid a single hog receiving tons of weapons when it is the only unfrozen
318	hog and takes consecutive turns. ]]
319
320	if (CurrentHedgehog ~= nil) then
321
322		currHog = CurrentHedgehog
323
324		if currHog ~= lastHog then
325
326			-- re-assign ammo to this guy, so that his entire ammo set will
327			-- be visible during another player's turn
328			if lastHog ~= nil and GetHealth(lastHog) then
329				ConvertValues(lastHog)
330			end
331
332			-- give the new hog what he is supposed to have, too
333			ConvertValues(CurrentHedgehog)
334
335		end
336
337		lastHog = currHog
338
339	end
340
341end
342
343function onNewTurn()
344	CheckForHogSwitch()
345
346	-- If hog placement phase is over, set the hog's actual teleport loadout
347	if GetGameFlag(gfPlaceHog) and TotalRounds == 0 and not teleportConverted then
348		runOnHogs(function(gear)
349			AddAmmo(gear, amTeleport, getGearValue(gear, amTeleport))
350		end)
351		-- This makes sure this code is only run once
352		teleportConverted = true
353	end
354end
355
356function onGearAdd(gear)
357
358	if (GetGearType(gear) == gtHedgehog) then
359		trackGear(gear)
360	end
361
362end
363
364function onGearDelete(gear)
365
366	if (GetGearType(gear) == gtHedgehog) then
367		TransferWeps(gear)
368		trackDeletion(gear)
369	end
370
371end
372