-- Forge milestone 2, genre one: the rail shooter. A camera on a rail that stops at encounters, -- zombies that spawn there and walk to it, a gun whose ray says which part it hit, ragdolls, and -- the rail moving on when the stop is clear. The pointer is aimed at each zombie's head through -- sceneProject, which is what a light gun would do with a person behind it. dofile("Forge/AuthorCompile.singe") local font = fontLoad("Singe/FreeSansBold.ttf", 18) fontSelect(font) fontQuality(FONT_QUALITY_BLENDED) dofile(authorBuild("testScripts/author/railshooter.game", singeGetDataPath() .. "railshooter.singe")) local gameUpdate = onOverlayUpdate local frames = 0 local shots = 0 local hits = 0 local heads = 0 local kills = 0 local stopped = 0 local ended = false local lastShot = 0 local GIVE_UP = 60 local realEvent = authorEvent function authorEvent(name, event) if name == "hit" then hits = hits + 1 if event.part == "head" then heads = heads + 1 end elseif name == "death" then kills = kills + 1 elseif name == "stopped" then stopped = stopped + 1 elseif name == "railEnd" then ended = true end return realEvent(name, event) end local function fire(x, y, on) authorSetPointer(1, x, y, on) authorSwitchDown(SWITCH_BUTTON3) authorSwitchUp(SWITCH_BUTTON3) end function onOverlayUpdate() local t = authorTime() local gun = authorEntity("gun1") frames = frames + 1 -- Aim at the nearest living zombie's head and fire, no faster than a person would; reload -- off the picture when empty. if gun and (t - lastShot > 0.25) then local nearest, best = nil, math.huge for _, zombie in ipairs(authorEach("zombie")) do if zombie.vars.state ~= "dead" then local far = authorDistance(zombie, authorEntity("camera")) if far < best then nearest, best = zombie, far end end end if gun.vars.ammo <= 0 then fire(-10, -10, false) lastShot = t elseif nearest then local head = nodeFind("b_Head_05", nearest.model) local hx, hy, hz = nodeGetWorldPosition(head) local sx, sy, _, inFront = sceneProject(hx, hy, hz) if inFront then fire(sx, sy, true) lastShot = t end end end local r = gameUpdate() colorForeground(255, 255, 255, 255) fontPrint(12, 450, string.format("authored rail shooter, %.1fs, zombies %d, kills %d, heads %d, stops %d", t, authorCount("zombie"), kills, heads, stopped)) if (shots == 0 and t > 5) or (shots == 1 and kills > 0) or (shots == 2 and stopped == 2) or (shots == 3 and ended) then shots = shots + 1 singeScreenshot() end if (ended and t > 1) or (t > GIVE_UP) then debugPrint(string.format("RAIL RESULT score=%d health=%d bites=%d stops=%d kills=%d hits=%d heads=%d ended=%s after=%.1fs", AUTHOR_VARS.score, AUTHOR_VARS.health, AUTHOR_VARS.bites, stopped, kills, hits, heads, tostring(ended), t)) if stopped < 2 then debugPrint("RAIL FAIL the rail did not reach both stops") end if kills < 7 then debugPrint("RAIL FAIL not every zombie was killed") end if heads == 0 then debugPrint("RAIL FAIL no shot was reported on the head") end if not ended then debugPrint("RAIL FAIL the rail never ended") end singeQuit() end return r end