1 2-- A spawner (NUKEBUTTON+3) of colored TRANSPORTERSTAR+4 sprites in a helical 3-- arrangement. 4 5local require = require 6local math = require("math") 7 8local con = require("con") 9local xmath = require("xmath") 10 11local gv = gv 12local actor = actor 13local player = player 14local sprite = sprite 15 16local gameactor = gameactor 17 18 19module(...) 20 21-- Dual-typed per-actor array: false if a broken nuke switch is not enabled, 22-- start game tic of when it was enabled otherwise. 23-- NOTE: for objects that are not supposed to be deleted such as this one, it 24-- would also be OK to use a plain table. 25local nukeswStart = con.actorvar(false) 26 27-- This one should be a per-actor variable because it holds info about 28-- "volatile" actors. 29local starPal = con.actorvar(0) 30 31-- Color per decasecond, can be changed from outside. 32COLOR = { 1, 2, 6, 7, 8 } 33 34require("end_gamevars") 35 36 37local bangvec = xmath.bangvec 38local angvec = xmath.angvec 39 40 41local D = require("CON.DEFS") 42local GTICSPERSEC = gv.GTICSPERSEC 43 44gameactor 45{ 46 D.TRANSPORTERSTAR+4, 47 48 flags = actor.FLAGS.NOCLIP, 49 50 move = con.move{100}, 51 movflags = actor.MOVFLAGS.geth, 52 53 func = function(aci) 54 local spr = sprite[aci] 55 -- NOTE: this is prettier than calling it 'a', even if 'act' is used to 56 -- denote an action in other places: 57 local act = actor[aci] 58 59 if (act:has_action(0)) then 60 act:set_action(1) -- TODO: actor constructors, i.e. 'init' callbacks 61 62 local decasec = math.floor((gv.gametic - nukeswStart[spr.owner])/(GTICSPERSEC*10)) % 12 63 64 local pal = COLOR[decasec+1] 65 if (pal ~= nil) then 66 starPal[aci] = pal 67 end 68 69 -- Every 2nd minute, we stop coloring the spawned stars. This tests 70 -- per-actor variable resetting to the default value. 71 spr.pal = starPal[aci] 72 end 73 74 if (act:checkbump()) then 75 con.killit() 76 end 77 78 -- Test spr:changesect() vs. sprite.changesect() 79 local sectnum = spr.sectnum 80 81 for i=0,gv.numsectors-1 do 82 if (spr.pal ~= 2 and spr.pal ~= 7) then 83 sprite.changesect(aci, i) -- noticeably faster... 84 else 85 spr:changesect(i) -- ...than this 86 end 87 end 88 89 sprite.changesect(aci, sectnum) 90 end 91} 92 93local CS = sprite.CSTAT 94local SPAWNSPERTIC = 10 --> 300/second --> 18000 per minute 95local TWOPI = 2*math.pi 96 97gameactor 98{ 99 D.NUKEBUTTON+3, -- destroyed end-of-level nuke switch 100 101 function(aci) 102 local spr = sprite[aci] 103 104 for pi in player.all() do 105 -- XXX: how to emulate "use switch" distance checking code, but in 106 -- an actor-centric fashion? 107 if (not nukeswStart[aci] and player.holdskey(pi, "OPEN") 108 and (player[pi].pos - spr):len2sq() < 256^2) then 109 -- Enable us. 110 nukeswStart[aci] = gv.gametic 111 spr.cstatbits:clear(CS.TRANS_BITMASK) 112 spr.cstatbits:set(CS.TRANS1) 113 break 114 end 115 end 116 117 local startgtic = nukeswStart[aci] 118 if (not startgtic) then 119 return 120 end 121 122 local hei, zofs = spr:getheightofs() 123 local radius = hei/2 124 125 for i=0,SPAWNSPERTIC-1 do 126 -- Make one second go once round the circle, spawning 127 -- SPAWNSPERTIC*GTICSPERSEC stars. 128 local ii = ((gv.gametic*SPAWNSPERTIC)%(GTICSPERSEC*SPAWNSPERTIC)) + i 129 local v = (radius/16)*angvec(ii*TWOPI/(GTICSPERSEC*SPAWNSPERTIC)) 130 local circvec = xmath.vec3(0, v.x, 16*v.y):rotate(spr.ang) 131 local pos = spr^(zofs + radius) + 256*bangvec(spr.ang) + circvec 132 133 con.insertsprite{D.TRANSPORTERSTAR+4, pos, spr.sectnum, actor.STAT.ACTOR, aci, 134 xrepeat=3, yrepeat=3, ang=spr.ang} 135 end 136 end 137} 138