Help for LUA script

Have a question, suggestion, or comment about Aleph One's features and functionality (Lua, MML, the engine itself, etc)? Post such topics here.
Post Reply
User avatar
bernardo
Born on Board
Posts: 13
Joined: Aug 19th '21, 21:39

Hello everyone. I downloaded the rain.lua file from the internet but it doesn't work in my Aleph One maps. It gives me an error! How can I solve this problem? I would like the rain to fall on all the polygons of the map.

Here is the script. Who can correct it? Thanks in advance.

CollectionsUsed = { 22 }

precipitation_type = "rocks"
precipitation_count = 420
precipitation_phase = 1
precipitation_gravity = 1/4
precipitation_wind = 0
fog_timer = 100
thunder_timer = -1
StartOutsideLightIndex = 26

scenery_cleared = false

function build_pool()
Level._pool = {}

for i=1,1024 do
s = Scenery.new(0, 0, 0, 0, "gun")
s._fake = true
end

for i=1,precipitation_count do
local x, y, z, p = uniform.xyz_in_triangle_list(Level._triangles)
table.insert(Level._pool, Scenery.new(x, y, z, p, precipitation_type))
end

for s in Scenery() do
if s._fake then
s:delete()
end
end
end

Triggers = {}

function Triggers.init(restoring_game)
for s in Scenery() do
s:delete()
end
end

function Triggers.pattern_buffer()
for s in Scenery() do
s:delete()
end
scenery_cleared = true
end

function Triggers.init()
local polygon_list = {}
for p in Polygons() do
if p.ceiling.transfer_mode == "landscape" then
table.insert(polygon_list, p)
end
end
Level._triangles = uniform.build_triangle_list(polygon_list)
build_pool()
end

function Triggers.idle()
if scenery_cleared == true then
build_pool()
scenery_cleared = false
end
local pool = Level._pool
local position = pool[1].position
local phase = precipitation_phase
local gravity = phase * precipitation_gravity
local wind = phase * precipitation_wind
local phase_match = Game.ticks % phase
for i = 1,precipitation_count do
if i % phase == phase_match then
local e = pool
position(e, e.x - wind, e.y - wind, e.z - gravity, e.polygon)
if e.z < e.polygon.floor.height then
local x, y, p = uniform.xy_in_triangle_list(Level._triangles)
e:position(x, y, p.ceiling.height, p)
elseif e.z > e.polygon.ceiling.height then
local x, y, p = uniform.xy_in_triangle_list(Level._triangles)
e:position(x, y, p.floor.height, p)
end
end
end

lolfog()
lolthunder()
end

function lolfog()
fog_timer = fog_timer - 1
if fog_timer == 0 then
thunder_timer = 70

-- Make things bright to make it look like lightning
Level.fog.color.b = .9
Level.fog.color.g = .9
Level.fog.color.r = .9

for l in Lights() do
if l.index >= StartOutsideLightIndex then
l.active = false
end
end
end
if fog_timer == -3 then

-- Set everything back to their original brightness
Level.fog.color.b = .3
Level.fog.color.g = .3
Level.fog.color.r = .3

for l in Lights() do
if l.index >= StartOutsideLightIndex then
l.active = true
end
end

fog_timer = 100 + Game.random(400)
end
end

function lolthunder()
if thunder_timer > 0 then
thunder_timer = thunder_timer - 1
elseif thunder_timer == 0 then
pitch = .2 + (Game.random(10)/10)
for p in Players() do
p:play_sound("surface explosion", pitch)
end
thunder_timer = -1
end
end
User avatar
The Man
Vidmaster
Posts: 1204
Joined: Aug 6th '08, 05:23
Location: Sarasota, FL
Contact:

OK, there are a ton of things wrong here. Precipitation is a pretty advanced feature, and if you don’t know what you’re doing, you can screw things up pretty badly, so you may want to hold off on implementing it if you don’t yet understand Lua or have a decent amount of general programming knowledge – not to mention some specialised engine knowledge (and I don’t really have time to go over everything).

First off, the version you posted is very old; it's extremely inefficient compared to modern rain scripts, and it will cause lag on older computers that won’t be fixable if it’s embedded in a map. Worse still, it’s incompatible with any level that has a pattern buffer; it’ll crash when you try to load saved games. Here's a script that should probably function fine with saved games, and that users will be able to disable in their preferences if it causes problems for them:

Code: Select all

precipitation_collection = 26
precipitation_sequence = 21
precipitation_count = 1536
precipitation_gravity = 1/4
precipitation_wind = 0

function build_pool()
	Level._pool = {}
	for i=1,precipitation_count do
		local x, y, z, p, t = uniform.xyz_in_triangle_list(Level._triangles)
		local e = Ephemera.new(x, y, z, p, precipitation_collection, precipitation_sequence, 0)
		e._triangle = t
		table.insert(Level._pool, e)
	end
end

Triggers = {}

function Triggers.init(restoring)
	Game.proper_item_accounting = true

	if Game.version >= "20210222" and Ephemera.quality ~= "off" then
		-- we're using ephemera, so use a render random function
		uniform.random = Game.random_local

		-- modify amount of precipitation based on rendering settings
		if Ephemera.quality == "low" then
			precipitation_count = 512
		elseif Ephemera.quality == "medium" then
			precipitation_count = 1024
		elseif Ephemera.quality == "ultra" then
			precipitation_count = 2048
		end

		local polygon_list = {}
		for p in Polygons() do
			if p.ceiling.transfer_mode == "landscape" then
				table.insert(polygon_list, p)
			end
		end
		Level._triangles = uniform.build_triangle_list(polygon_list)
		build_pool()
	end
end

function Triggers.idle()
	if Game.version >= "20210222" and Ephemera.quality ~= "off" then
		local pool = Level._pool
		local position = pool[1].position

		for i = 1,precipitation_count do
			local e = pool[i]
			if e.rendered then
				local p = e.polygon

				-- to do wind, add it to e.x and e.y, and use p:find_polygon to find
				-- the new polygon. You'll also have to track the original polygon
				-- the ephemera was in to reset it when it goes out of bounds
				position(e, e.x, e.y, e.z - precipitation_gravity, p)
				if e.z < e.polygon.floor.height then
					local x, y = uniform.xy_in_triangle(e._triangle)
					e:position(x, y, p.ceiling.height, p)
				elseif e.z > e.polygon.ceiling.height then
					local x, y = uniform.xy_in_triangle(e._triangle)
					e:position(x, y, p.floor.height, p)
				end
				if e.polygon.media then
					if e.z < e.polygon.media.height then
						local x, y = uniform.xy_in_triangle(e._triangle)
						e:position(x, y, p.ceiling.height, p)
					end
				end
			end
		end
	end
end
A few notes on the above code:

It’s very important that the “precipitation_collection” and “precipitation_sequence” match the respective collection and sequence of the desired precipitation in your shapes file. This is a modified version of a script from Eternal 1.3, so the sequence it specifies will definitely not exist in a vanilla Infinity shapes file. I believe the version of the script you’ve been using may merge in a shapes patch; it looks like it’s the script from Imperium, although I didn’t double-check. If you’re going to use that patch, make sure you add it to the folder of every level that uses the script. Also, the Imperium patch will only really work with vanilla Infinity, so if you’re using this script for your own scenario, you’re almost certainly better off adding the sequence to your shapes.

A further note is that “precipitation_count” affects the number of precipitation objects on the map. I have it set to vary the amount of precipitation it places based on the “scripted effects quality” setting, so “ultra” gives you 2,048, “high” gives you 1,536, “medium” gives you 1,024, and “low” gives you 512.

A final note is that I didn’t include a lightning script; that’s a whole additional can of worms (the lightning script I use varies the light intensity on a level based on a specific tag, which can mess up the functionality of levels that use that specific tag; I thought about including a version that did add lightning, but it’d probably be too confusing).


The second thing wrong: The rain script requires Uniform.lua to function. This is, to my knowledge, the latest version. (The version of Uniform.lua included in Imperium or wherever you got your script won’t function with the script I used.)

Code: Select all

-- Uniform.lua by Gregory Smith
-- Random points in Aleph One's game world
--
-- you may freely redistribute this file and/or include it in your scripts
--
-- to find a random x,y,polygon or x,y,z,polygon in a list of polygons:
-- uniform.build_triangle_list(polygon_list)
-- uniform.xy_in_triangle_list(triangle_list)
-- uniform.xyz_in_triangle_list(triangle_list)
--
-- inefficient convenience functions:
-- uniform.xy_in_polygon(polygon)
-- uniform.xyz_in_polygon(polygon)

uniform = {}

function uniform.xy_in_polygon(polygon)
   return uniform.xy_in_triangle_list(build_triangle_list({polygon}))
end

function uniform.xyz_in_polygon(polygon)
   return uniform.xyz_in_triangle_list(build_triangle_list({polygon}))
end

function uniform.build_triangle_list(polygon_list)
   local triangles = {}
   local total_area = 0
   for _, p in pairs(polygon_list) do
      local polygon_triangles = uniform.split_polygon_into_triangles(p)
      for _, t in pairs(polygon_triangles) do
	 table.insert(triangles, t)
	 total_area = total_area + t.area
      end
   end

   local area = 0
   for _, t in pairs(triangles) do
      area = area + t.area / total_area
      t.weight = area
   end

   return triangles
end

function uniform.xy_in_triangle_list(triangles)
   local t = uniform.search(triangles, uniform.normalized_random())
   local x, y = uniform.xy_in_triangle(t)
   return x, y, t.polygon, t
end

function uniform.xyz_in_triangle_list(triangles)
   local x, y, p, t = uniform.xy_in_triangle_list(triangles)
   local z = p.floor.height + uniform.normalized_random() * (p.ceiling.height - p.floor.height)
   return x, y, z, p, t
end

function uniform.split_polygon_into_triangles(p)
   local triangles = {}
   local endpoints = p.endpoints
   local first = endpoints[0]
   for i = 1, # p.endpoints - 2 do
      local a = first
      local b = endpoints[i]
      local c = endpoints[i + 1]
      local triangle = {a, b, c}
      triangle.area = math.abs((a.x * b.y - b.x * a.y) + (b.x * c.y - c.x * b.y) + (c.x * a.y - a.x * c.y)) / 2
      triangle.polygon = p
      
      table.insert(triangles, triangle)
   end
   return triangles
end

function uniform.normalized_random()
   return uniform.random() / (2 ^ 32)
end

function uniform.xy_in_triangle(t)
   local a = uniform.normalized_random()
   local b = uniform.normalized_random()
   if a + b > 1 then
      a = 1 - a
      b = 1 - b
   end
   local c = 1 - a - b

   local ta = t[1]
   local tb = t[2]
   local tc = t[3]

   local x = ta.x * a + tb.x * b + tc.x * c
   local y = ta.y * a + tb.y * b + tc.y * c

   return x, y
end

function uniform.search(triangles, weight)
   local function search(low, high)
      local mid = math.floor((low + high) / 2)
      if low == high then
	 return high
      elseif triangles[mid].weight > weight then
	 return search(low, mid)
      else
	 return search(mid + 1, high)
      end
   end
   return triangles[search(1, # triangles)]
end
This means that you’ll either have to merge it in with the lightning script (because it’s not possible to run two solo Lua scripts; I’ve never tried merging them, but it may take some specialised programming knowledge to pull off) or else merge both of them in with your map. (I think it’s extremely unlikely that making a solo Lua version of the precipitation script will be at all practical or look at all good.)

Third, tweaking the wrong parts of the above code (either the rain script or Uniform.lua) has the potential to cause major desyncs; you need to know when to use what random function to avoid problems (which is why I specifically said it may take a lot of programming knowledge to implement this correctly).

Fourth, you’ll need some limit-removing MML alongside your script, or else the game will crash. This is what I’ve been using for Eternal:

Code: Select all

<marathon>
	<dynamic_limits>
		<objects value="3072"/>
		<rendered value="2048"/>
	</dynamic_limits>
</marathon>
Save this as Limits.mml and either put it in your scenario Scripts directory (if you’re using this for your own total conversion and people will be guaranteed to have the script) or else merge it in with each level that uses the precipitation script (if you’re using it for an Infinity map). It’s very important for “objects” to be substantially higher than the highest precipitation_count your script uses, and “rendered” should probably at least match the highest precipitation_count (It may make sense to make it even larger, possibly).

Fifth, note that this doesn’t add precipitation to all polygons, because that would be stupid. It only adds it to polygons whose ceilings use the “landscape” transfer mode. It’s possible to change the parameters for what polygons it includes, of course, and I’ve done this for some levels to exclude specific landscape textures (for Eternal, for example, we exclude a particular landscape because it’s meant to represent a forcefield that nothing penetrates – which required some rather intricate scripting for the level where you deactivate forcefields). I don’t think including non-landscape transfer modes would make sense.

Sixth, the above scripts require Aleph One 1.4 or higher, but you should be using that version anyway (except for AOPID, which IIRC only works correctly with 1.3.x, or EMR, which is completely unplayable on any modern version of Aleph One except for Treellama’s 1.3.x “compatibility branch”).

Seventh, even if you do all this stuff, it may not work right. It’s possible I screwed something up, but precipitation is also just very finicky and probably difficult to implement correctly without a lot of specialised engine and programming knowledge… and I’ve spent long enough writing this post already. :V
“People should not be afraid of their governments. Governments should be afraid of their people.” —V, V for Vendetta (Alan Moore)

“The trouble is that we have a bad habit, encouraged by pedants and sophisticates, of considering happiness as something rather stupid. Only pain is intellectual, only evil interesting. This is the treason of the artist: a refusal to admit the banality of evil and the terrible boredom of pain. If you can’t lick ’em, join ’em. If it hurts, repeat it. But to praise despair is to condemn delight, to embrace violence is to lose hold of everything else. We have almost lost hold; we can no longer describe happy man, nor make any celebration of joy.” —Ursula K. Le Guin, “The Ones Who Walk Away from Omelas”

“If others had not been foolish, we should be so.” —William Blake, The Marriage of Heaven and Hell

“The law cannot protect anyone unless it binds everyone; and it cannot bind anyone unless it protects everyone.” —Frank Wilhoit

Last.fm · Marathon Chronicles · Marathon Eternal 1.2 · Where Monsters Are in Dreams · YouTube Vidmaster’s Challenge
User avatar
bernardo
Born on Board
Posts: 13
Joined: Aug 19th '21, 21:39

tanks a lot for your script help. it runs
Post Reply