1-- Copyright (C) 2007, 2010 - Bit-Blot
2--
3-- This file is part of Aquaria.
4--
5-- Aquaria is free software; you can redistribute it and/or
6-- modify it under the terms of the GNU General Public License
7-- as published by the Free Software Foundation; either version 2
8-- of the License, or (at your option) any later version.
9--
10-- This program is distributed in the hope that it will be useful,
11-- but WITHOUT ANY WARRANTY; without even the implied warranty of
12-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13--
14-- See the GNU General Public License for more details.
15--
16-- You should have received a copy of the GNU General Public License
17-- along with this program; if not, write to the Free Software
18-- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19
20if not v then v = {} end
21if not AQUARIA_VERSION then dofile("scripts/entities/entityinclude.lua") end
22
23local STATE_EYESOPEN 		= 1000
24local STATE_DIE			= 1001
25local STATE_TENTACLES 	= 1002
26local STATE_SEED 			= 1003
27local STATE_DONE		= 1004
28local STATE_FIRESEEDS	= 1005
29local STATE_FIRESPIKY	= 1006
30local STATE_SINGNOTE	= 1007
31local STATE_VINES		= 1008
32local STATE_RAGE		= 1009
33
34v.curCurrent = 0
35v.c1 = 0
36v.c2 = 0
37
38v.delay = -3
39v.eyes = 0
40v.bone_bg = 0
41v.door = 0
42v.noteTimer = 0
43
44v.rageState = 0
45
46v.config = 0
47
48v.lastPlayerNote = 0
49
50
51-- NOTE: forest goddess has two health bars
52
53-- phase 1 health
54v.maxHits = 50
55v.hits = v.maxHits
56
57-- phase 2 health
58v.maxRageHits = 70
59v.rageHits = v.maxRageHits
60
61v.n = 0
62v.stage = 0
63v.seedDelay = 0
64v.seedNode = 0
65v.started = false
66v.enter = 0
67
68v.sungNote = -1
69
70v.noteQuad = 0
71
72v.vh1 = 0
73v.vh2 = 0
74v.vh3 = 0
75
76
77
78v.shotOff = 0
79v.rageShotMax = 1000
80v.rageShotStart = -v.rageShotMax
81v.rageShotPos = 0
82v.rageShotAdd = 100
83v.ba = 0
84
85v.b1 = 0
86v.b2 = 0
87v.b3 = 0
88v.b4 = 0
89
90v.bd = 1
91
92local function clearVines()
93	playSfx("vineshrink")
94	local e = getFirstEntity()
95	while e ~= 0 do
96		if eisv(e, EV_TYPEID, EVT_FORESTGODVINE) then
97			entity_delete(e, 0.3)
98		end
99		e = getNextEntity()
100	end
101end
102
103local function spawnVines(me, num)
104	local v1, v2, v3
105	if v.config == 0 then
106		v1 = getNode("V1")
107		v2 = getNode("V2")
108		v3 = getNode("V3")
109	elseif v.config == 1 then
110		v1 = getNode("V4")
111		v2 = getNode("V5")
112		v3 = getNode("V1")
113	elseif v.config == 2 then
114		v1 = getNode("V2")
115		v2 = getNode("V4")
116		v3 = getNode("V1")
117	end
118	v.config = v.config + 1
119	if v.config > 2 then
120		v.config = 0
121	end
122	if num >= 1 then
123		v.vh1 = createEntity("ForestGodVineHead", "", node_x(v1), node_y(v1))
124	end
125	if num >= 2 then
126		v.vh2 = createEntity("ForestGodVineHead", "", node_x(v2), node_y(v2))
127	end
128	if num >= 3 then
129		v.vh3 = createEntity("ForestGodVineHead", "", node_x(v3), node_y(v3))
130	end
131	playSfx("UberVineShrink")
132end
133
134function init(me)
135	-- NOTE: HEALTH IS SET IN HITS AND MAXHITS
136	setupBasicEntity(
137	me,
138	"",								-- texture
139	99,								-- health
140	1,								-- manaballamount
141	1,								-- exp
142	1,								-- money
143	128,							-- collideRadius (only used if hit entities is on)
144	STATE_IDLE,						-- initState
145	90,								-- sprite width
146	90,								-- sprite height
147	1,						-- particle "explosion" type, maps to particleEffects.txt -1 = none
148	0,								-- 0/1 hit other entities off/on (uses collideRadius)
149	6000,							-- updateCull -1: disabled, default: 4000
150	-3
151	)
152
153	entity_setCull(me, false)
154	entity_initSkeletal(me, "ForestGod")
155	entity_generateCollisionMask(me)
156
157	entity_animate(me, "idle", LOOP_INF)
158
159	entity_setDeathParticleEffect(me, "ForestGodExplode")
160
161
162
163	v.eyes = entity_getBoneByName(me, "eyes")
164	v.bone_bg = entity_getBoneByName(me, "bg")
165
166	bone_alpha(v.eyes, 0)
167
168	entity_setTargetPriority(me, 1)
169	entity_setTargetRange(me, 300)
170
171	--entity_scale(me, 5.5, 5.5)
172	entity_scale(me, 3.2, 3.2)
173
174	entity_setState(me, STATE_IDLE)
175	entity_setTarget(me, getNaija())
176
177	--entity_setDamageTarget(me, DT_AVATAR_ENERGYBLAST, false)
178	entity_setAllDamageTargets(me, false)
179
180	v.node_seed1 = entity_getNearestNode(me, "SEED1")
181	v.node_seed2 = entity_getNearestNode(me, "SEED2")
182	v.node_seed3 = entity_getNearestNode(me, "SEED3")
183	v.node_seed4 = entity_getNearestNode(me, "SEED4")
184
185
186	esetv(me, EV_ENTITYDIED, 1)
187
188	loadSound("ForestGod-Awaken")
189
190	entity_setDamageTarget(me, DT_ENEMY_ENERGYBLAST, false)
191
192	local cc1 = getNode("cc1")
193	local cc2 = getNode("cc2")
194
195	v.c1 = getNearestNodeByType(node_x(cc1), node_y(cc1), PATH_CURRENT)
196	v.c2 = getNearestNodeByType(node_x(cc2), node_y(cc2), PATH_CURRENT)
197
198	node_setActive(v.c1, true)
199	node_setActive(v.c2, false)
200
201	v.curCurrent = 1
202end
203
204function entityDied(me, ent)
205	if ent == v.vh1 then
206		v.vh1 = 0
207	end
208	if ent == v.vh2 then
209		v.vh2 = 0
210	end
211	if ent == v.vh3 then
212		v.vh3 = 0
213	end
214end
215
216function postInit(me)
217	v.door = entity_getNearestEntity(me, "VineDoor")
218	entity_setState(v.door, STATE_OPENED)
219	v.enter = getNode("NAIJAENTER")
220	if not isFlag(FLAG_BOSS_FOREST,0) then
221		entity_setState(me, STATE_DONE)
222		setMusicToPlay("")
223	end
224	v.n = getNaija()
225	entity_setTarget(me, v.n)
226
227	if entity_isFlag(me, 1) then
228		setControlHint(getStringBank(40), 0, 0, 0, 10, "", SONG_NATUREFORM)
229		entity_setFlag(me, 2)
230	end
231end
232
233v.inCutScene = false
234local function cutscene(me)
235	v.n = getNaija()
236	if not v.inCutScene then
237		v.inCutScene = true
238		pickupGem("Boss-PlantGoddess")
239		setFlag(FLAG_BOSS_FOREST,1)
240		local ent = getFirstEntity()
241		while ent ~= 0 do
242			--if entity_isName(ent, "UberVine")
243			if entity_isName(ent, "SpikyBall")
244			or entity_isName(ent, "SporeSeed") then
245				entity_setDieTimer(ent, 0.2)
246			end
247			ent = getNextEntity()
248		end
249		watch(1)
250		changeForm(FORM_NORMAL)
251		--fadeOutMusic(4)
252		entity_idle(v.n)
253		entity_flipToEntity(v.n, me)
254		local pn = getNode("NAIJADONE")
255		entity_animate(v.n, "agony", LOOP_INF)
256		watch(2)
257		learnSong(SONG_NATUREFORM)
258		entity_setPosition(v.n, node_x(pn), node_y(pn), 2, 0, 0, 1)
259		watch(2)
260		entity_setFlag(me, 1)
261		loadMap("ForestVision")
262		--[[
263		showImage("Visions/Forest/00")
264		watch(0.5)
265		voice("Naija_Vision_Forest")
266		watchForVoice()
267		hideImage()
268
269		watch(2)
270		entity_idle(v.n)
271		changeForm(FORM_NATURE)
272		voice("Naija_Song_NatureForm")
273		entity_idle(v.n)
274		entity_setState(door, STATE_OPEN)
275		--watchForVoice()
276		-- show help text
277		]]--
278	end
279end
280
281function update(me, dt)
282
283	if isNested() then
284		return
285	end
286
287	if entity_isState(me, STATE_DIE) or entity_isState(me, STATE_DONE) then
288		return
289	end
290
291	if v.noteTimer > 0 then
292		--debugLog(string.format("noteTimer: %f", v.noteTimer))
293		v.noteTimer = v.noteTimer - dt
294		if v.noteTimer <= 0 then
295			--debugLog(string.format("sungNote: %d, lastPlayerNote: %d", v.sungNote, v.lastPlayerNote))
296			if v.sungNote == v.lastPlayerNote then
297				entity_setState(me, STATE_EYESOPEN)
298			end
299		end
300	end
301
302	if v.started then
303		overrideZoom(0.4, 1)
304	end
305	if not entity_isState(me, STATE_DIE) and not entity_isState(me, STATE_DONE) then
306		entity_handleShotCollisions(me, dt)
307		if entity_isEntityInRange(me, v.n, 128) then
308			--entity_hurtTarget(me, 1)
309			entity_setTarget(me, v.n)
310			entity_pushTarget(me, 400)
311		end
312	end
313
314	if entity_isState(me, STATE_SEED) then
315		v.seedDelay = v.seedDelay + dt
316		if v.seedDelay > 2 then
317			local node = 0
318			if v.seedNode == 0 then
319				node = v.node_seed1
320			elseif v.seedNode == 1 then
321				node = v.node_seed2
322			elseif v.seedNode == 2 then
323				node = v.node_seed3
324			elseif v.seedNode == 3 then
325				node = v.node_seed4
326			end
327			createEntity("ForestGodSeed", "", node_x(node), node_y(node))
328			v.seedNode = v.seedNode + 1
329			if v.seedNode > 3 then
330				v.seedNode = 0
331			end
332			v.seedDelay = 0
333		end
334	end
335
336	if isFlag(FLAG_BOSS_FOREST, 0) and node_isEntityIn(v.enter, v.n) then
337		if not v.started then
338			v.started = true
339			entity_setState(v.door, STATE_CLOSE)
340			playMusic("ForestGod")
341		end
342	else
343		overrideZoom(0)
344	end
345
346	if entity_isState(me, STATE_TENTACLES) and not entity_isAnimating(me) then
347		entity_setState(me, STATE_IDLE)
348	end
349
350
351	if entity_isState(me, STATE_RAGE) and v.started then
352		v.delay = v.delay + dt
353
354		if v.delay >= 18 and v.rageState ~= 0 then
355			v.rageState = 0
356			v.delay = 0
357
358			clearVines()
359
360			fade2(1,0,1,1,1)
361			fade2(0,0.5,1,1,1)
362			v.bd = -v.bd
363
364		elseif v.delay >= 9 and v.rageState < 2 then
365			v.rageState = 2
366
367			spawnVines(me, 2)
368		elseif v.delay >= 3 and v.rageState < 1 then
369
370
371			v.rageState = 1
372
373			v.fireDelay = 999
374
375			v.rageShotPos = v.rageShotStart + v.shotOff
376			v.shotOff = math.random(50)-100
377		end
378		if v.rageState == 1 then
379
380			v.fireDelay = v.fireDelay + dt
381			if v.fireDelay > 0.3 then
382				v.fireDelay = 0
383
384
385				local s = createShot("ForestGod2", me, 0, entity_x(me)+v.rageShotPos, entity_y(me)-520)
386				shot_setAimVector(s, 0, 400)
387				v.rageShotPos = v.rageShotPos + v.rageShotAdd
388				if v.rageShotPos >= (v.rageShotMax+v.shotOff) then
389					v.fireDelay = -1000
390				end
391			end
392		end
393
394		v.ba = v.ba + 3.14*0.4*dt*v.bd
395		local off = 3.14/2
396		local out = 200
397		entity_setPosition(v.b1, entity_x(me) + out*math.sin(v.ba+off*0), entity_y(me) + out*math.cos(v.ba+off*0))
398		entity_setPosition(v.b2, entity_x(me) + out*math.sin(v.ba+off*1), entity_y(me) + out*math.cos(v.ba+off*1))
399		entity_setPosition(v.b3, entity_x(me) + out*math.sin(v.ba+off*2), entity_y(me) + out*math.cos(v.ba+off*2))
400		--entity_setPosition(v.b4, entity_x(me) + out*math.sin(ba+off*3), entity_y(me) + out*math.cos(ba+off*3))
401	end
402
403
404	if entity_isState(me, STATE_IDLE) and v.started then
405
406	--[[
407		if isLeftMouse() then
408			entity_setState(me, STATE_RAGE)
409		end
410	]]--
411
412		v.delay = v.delay + dt
413		if v.delay > 4 then
414			--debugLog("delay > 0!!")
415			v.delay = 0
416
417			local t = 3
418			local t2 = 10
419			if v.hits/v.maxHits < 0.5 then
420				t = 1.5
421				t2 = 5
422			end
423			--entity_setState(me, STATE_SEED, 9)
424			if v.stage == 0 then
425				--entity_setState(me, STATE_FIRESEEDS, t)
426				entity_setState(me, STATE_FIRESPIKY, t)
427
428			--elseif v.stage == 1 then
429
430			elseif v.stage == 1 then
431				entity_setState(me, STATE_VINES, t2)
432			elseif v.stage == 2 then
433				entity_setState(me, STATE_SINGNOTE, t2)
434			end
435			--[[
436			if v.stage == 0 then
437				entity_setState(me, STATE_TENTACLES)
438			elseif v.stage == 1 then
439				entity_setState(me, STATE_EYESOPEN, 2)
440			elseif v.stage == 2 then
441				entity_setState(me, STATE_TENTACLES)
442			elseif v.stage == 3 then
443				entity_setState(me, STATE_SEED, 9)
444			end
445			]]--
446			v.stage = v.stage + 1
447			if v.stage > 2 then
448				v.stage = 0
449			end
450		end
451	end
452end
453
454function enterState(me, state)
455	if entity_isState(me, STATE_EYESOPEN) then
456		playSfx("ForestGod-Awaken")
457
458		entity_setDamageTarget(me, DT_AVATAR_SHOCK, true)
459		entity_setDamageTarget(me, DT_AVATAR_ENERGYBLAST, true)
460		bone_alpha(v.eyes, 1, 0.1)
461
462		local shotSpd = 500
463		local maxa = 3.14 * 2
464		local a = 0
465		while a < maxa do
466			--entity_fireShot(me, 0, 0, math.sin(a)*shotSpd, math.cos(a)*shotSpd, 0, 500, "BlasterFire")
467			local s = createShot("ForestGod", me, 0)
468			shot_setAimVector(s, math.sin(a), math.cos(a))
469			a = a + (3.14*2)/16.0
470		end
471		entity_setStateTime(me, 5.5)
472
473		v.curCurrent = v.curCurrent + 1
474		if v.curCurrent > 2 then
475			v.curCurrent = 1
476		end
477
478		if v.curCurrent == 1 then
479			node_setActive(v.c1, true)
480			node_setActive(v.c2, false)
481		elseif v.curCurrent == 2 then
482			node_setActive(v.c1, false)
483			node_setActive(v.c2, true)
484		end
485
486	elseif entity_isState(me, STATE_RAGE) then
487		setSceneColor(1, 0.7, 0.7, 6)
488		shakeCamera(10, 3)
489		entity_color(me, 1, 0.5, 0.5, 3)
490		v.delay = 0
491		v.rageState = 0
492		bone_alpha(v.eyes, 1, 0.1)
493		entity_setDamageTarget(me, DT_AVATAR_SHOCK, true)
494		entity_setDamageTarget(me, DT_AVATAR_ENERGYBLAST, true)
495
496		v.b1 = createEntity("spikyblocker", "", entity_x(me), entity_y(me))
497		v.b2 = createEntity("spikyblocker", "", entity_x(me), entity_y(me))
498		v.b3 = createEntity("spikyblocker", "", entity_x(me), entity_y(me))
499		--v.b4 = createEntity("spikyblocker", "", entity_x(me), entity_y(me))
500	elseif entity_isState(me, STATE_FIRESEEDS) then
501		local shotSpd = 1000
502
503		local a = 0 + math.random(314)/10.0
504		local maxa = a + 3.14 * 2
505		while a < maxa do
506			local vx = math.sin(a)*shotSpd
507			local vy = math.cos(a)*shotSpd
508			local dx,dy = vector_setLength(vx, vy, 64)
509
510			local s = createShot("SeedUberVineUnlimited", me, 0, entity_x(me)+dx, entity_y(me)+dy)
511			shot_setAimVector(s, vx, vy)
512
513			--local ent = createEntity("SporeSeed", "", entity_x(me)+dx, entity_y(me)+dy)
514			--entity_setDieTimer(ent, 12)
515
516			--entity_setState(ent, STATE_CHARGE2)
517			--entity_addVel(ent, vx, vy)
518
519			local perc = v.hits/v.maxHits
520			--debugLog(string.format("perc: %f", perc))
521			if perc < 0.25 then
522				a = a + (3.14*2)/20.0
523			elseif perc < 0.5 then
524				a = a + (3.14*2)/12.0
525			else
526				a = a + (3.14*2)/6.0
527			end
528		end
529	elseif entity_isState(me, STATE_FIRESPIKY) then
530		local shotSpd = 800
531
532		local a = 0 + math.random(314)/10.0
533		local maxa = a + 3.14 * 2
534		while a < maxa do
535			local vx = math.sin(a)*shotSpd
536			local vy = math.cos(a)*shotSpd
537			local dx,dy = vector_setLength(vx, vy, 128)
538
539			local ent = createEntity("SpikyBall", "", entity_x(me)+dx, entity_y(me)+dy)
540
541			entity_setBounceType(ent, BOUNCE_REAL)
542			entity_setBounce(ent, 1)
543			entity_setDieTimer(ent, 6.5)
544			--entity_setLife(ent, 7)
545
546			entity_setState(ent, STATE_CHARGE1)
547			entity_addVel(ent, vx, vy)
548
549			local perc = v.hits/v.maxHits
550			if perc < 0.25 then
551				a = a + (3.14*2)/8.0
552			elseif perc < 0.75 then
553				a = a + (3.14*2)/6.0
554			else
555				a = a + (3.14*2)/4.0
556			end
557		end
558	elseif entity_isState(me, STATE_IDLE) then
559		entity_setDamageTarget(me, DT_AVATAR_SHOCK, false)
560		entity_setDamageTarget(me, DT_AVATAR_ENERGYBLAST, false)
561
562		bone_alpha(v.eyes, 0, 0.2)
563	elseif entity_isState(me, STATE_TENTACLES) then
564		entity_animate(me, "tentacles")
565	elseif entity_isState(me, STATE_SEED) then
566		v.delay = 0.5
567	elseif entity_isState(me, STATE_DIE) then
568		setSceneColor(0.7, 0.8, 1, 4)
569
570		shakeCamera(2, 4)
571		entity_setDamageTarget(me, DT_AVATAR_ENERGYBLAST, false)
572		entity_setDamageTarget(me, DT_AVATAR_SHOCK, false)
573		--debugLog("state die!!!")
574		entity_setColor(me, 0.5, 0.5, 0.8, 4)
575		--bone_setColor(v.eyes, 0.5, 0.5, 0.8, 4)
576		bone_alpha(v.eyes, 1)
577		bone_alpha(v.eyes, 0, 1)
578		entity_setStateTime(me, 4)
579
580		clearVines()
581
582		if v.b1 ~= 0 then entity_delete(v.b1) end
583		if v.b2 ~= 0 then entity_delete(v.b2) end
584		if v.b3 ~= 0 then entity_delete(v.b3) end
585		if v.b4 ~= 0 then entity_delete(v.b4) end
586
587		fadeOutMusic(4)
588
589	elseif entity_isState(me, STATE_SINGNOTE) then
590		v.sungNote = math.random(8)-1
591		entity_sound(me, string.format("Note%d", v.sungNote), 500, entity_getStateTime(me))
592		v.noteQuad = createQuad(string.format("Song/NoteSymbol%d", v.sungNote), 6)
593		quad_alpha(v.noteQuad, 0)
594		quad_alpha(v.noteQuad, 0.8, 2)
595		quad_scale(v.noteQuad, 3, 3, 2, 0, 0, 1)
596		quad_setBlendType(v.noteQuad, BLEND_ADD)
597
598		local r,g,b = getNoteColor(v.sungNote)
599		quad_color(v.noteQuad, r*0.8 + 0.2, g*0.8+0.2, b*0.8+0.2)
600
601		local x,y = entity_getPosition(me)
602		quad_setPosition(v.noteQuad, x, y+130)
603	elseif entity_isState(me, STATE_DONE) then
604
605		entity_setColor(me, 0.5, 0.5, 0.8)
606		--bone_setColor(v.eyes, 0.5, 0.5, 0.8)
607		bone_alpha(v.eyes, 0)
608		v.hits = 0
609
610		node_setActive(v.c1, false)
611		node_setActive(v.c2, false)
612	elseif entity_isState(me, STATE_VINES) then
613		local num = 1
614		local perc = v.hits/v.maxHits
615		if perc < 0.5 then
616			num = 3
617		elseif perc < 0.75 then
618			num = 2
619		end
620
621		spawnVines(me, num)
622	end
623end
624
625function damage(me, attacker, bone, damageType, dmg)
626	if attacker == me then
627		return false
628	end
629
630	if entity_isState(me, STATE_EYESOPEN) then
631		bone_damageFlash(v.bone_bg)
632		bone_damageFlash(v.eyes)
633		v.hits = v.hits - dmg
634		--debugLog(string.format("hits: %d", v.hits))
635		if v.hits <= 0 then
636			--debugLog("setting state die...")
637			--entity_setState(me, STATE_DIE)
638			entity_setState(me, STATE_RAGE)
639		else
640			--entity_setState(me, STATE_IDLE)
641		end
642		--return true
643	end
644	if entity_isState(me, STATE_RAGE) then
645		bone_damageFlash(v.bone_bg)
646		bone_damageFlash(v.eyes)
647		v.rageHits = v.rageHits - dmg
648		if v.rageHits <= 0 then
649			entity_setState(me, STATE_DIE)
650		end
651
652
653	end
654	return false
655end
656
657function exitState(me, state)
658	if entity_isState(me, STATE_EYESOPEN) then
659		clearShots()
660		entity_setState(me, STATE_IDLE)
661		shakeCamera(2, 3)
662	elseif entity_isState(me, STATE_SINGNOTE) then
663		v.sungNote = -1
664		entity_setState(me, STATE_IDLE)
665		if v.noteQuad ~= 0 then
666			quad_delete(v.noteQuad, 0.5)
667			v.noteQuad = 0
668		end
669	elseif entity_isState(me, STATE_VINES) then
670		entity_setState(v.vh1, STATE_OFF)
671		entity_setState(v.vh2, STATE_OFF)
672		entity_setState(v.vh3, STATE_OFF)
673		v.vh1 = 0
674		v.vh2 = 0
675		v.vh3 = 0
676		entity_setState(me, STATE_IDLE)
677	elseif entity_isState(me, STATE_SEED) then
678		entity_setState(me, STATE_IDLE)
679	elseif entity_isState(me, STATE_FIRESEEDS) or entity_isState(me, STATE_FIRESPIKY) then
680		entity_setState(me, STATE_IDLE)
681	elseif entity_isState(me, STATE_DIE) then
682		overrideZoom(0)
683		cutscene(me)
684		entity_setState(me, STATE_DONE)
685	end
686end
687
688function songNote(me, note)
689	v.lastPlayerNote = note
690	v.noteTimer = 0.5
691end
692
693function songNoteDone(me, note, t)
694	v.noteTimer = 0
695end
696