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