rockbandcheeseman

GEOCoords

Sep 12th, 2014
354
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 104.50 KB | None | 0 0
  1. -- GEO Coordinates of Interest
  2.  
  3. --[[
  4.  
  5.     This is a list of GEOs that people may find useful.
  6.     This list includes places commonly blocked, places people shouldn't be able to get to, etc.
  7.     In order to make these useable, you need the GEO library, which is included at the bottom of this script or under Nuggets' Releases 2.0 at phasor.proboards.com.
  8.     Beneath some GEO creations is a suggested action (such as killzone, damagezone, etc).  These are mere suggestions; you can do whatever you want.
  9.  
  10. --]]
  11.  
  12. required_version = 200
  13.  
  14. -- It's usually best to create GEOs OnNewGame.
  15. function OnNewGame(map)
  16.  
  17.     -- Useful GEOs for each map --
  18.  
  19.     -- Battle Creek --
  20.     if map == "beavercreek" then
  21.  
  22.         red_ladder = GEO.newRectPrism("red_ladder", 0.5, 1.0, 4.0, 17.85, 6.45, 1.65)  -- Ladder to Sniper Rifle near Red base
  23.         red_ladder:killzone(true, 10, "Stop blocking the ladders.")  -- Suggested action:  Kill the player after being on the ladder for 10 seconds and send them a message.
  24.        
  25.         blue_ladder = GEO.newRectPrism("blue_ladder", 0.5, 1.0, 3.7, 10.95, 20.8, 1.35)  -- Ladder to Sniper Rifle near Blue base
  26.         blue_ladder:killzone(true, 10, "Stop blocking the ladders.")  -- Suggested action:  Kill the player after being on the ladder for 10 seconds and send them a message.
  27.        
  28.         red_base_roof = GEO.newRectPrism("red_roof", 9.6, 8, 5.8, 26.15, 13.75, 4.2)  -- On top of the Red Base roof
  29.         red_roof_anti = GEO.newRectPrism("red_roof_anti", 4, 8.25, 1.75, 29.5, 13.75, 1.5)  -- Suggested action:  Create an AntiGEO to disallow players from entering this GEO from the room with the CTF flag.
  30.         red_roof_anti:antigeo(true, red_base_roof)  -- Make red_roof_anti an AntiGEO to red_base_roof.
  31.        
  32.         blue_base_roof = GEO.newRectPrism("blue_roof", 10, 8.2, 5.8, 1.8, 13.9, 4.2)  -- On top of the Blue Base roof
  33.         blue_base_anti = GEO.newRectPrism("blue_roof_anti", 2.9, 8.45, 1.75, -0.7, 13.9, 0.9)  -- Suggested action:  Create an AntiGEO to disallow players from entering this GEO from the room with the CTF flag.
  34.         blue_base_anti:antigeo(true, blue_base_roof)  -- Make blue_roof_anti an AntiGEO to blue_base_roof.
  35.        
  36.     -- Bloodgulch --
  37.     elseif map == "bloodgulch" then
  38.    
  39.         red_base_inside = GEO.newCylinder("in_red_base", 5, 2, 95.5, -159.5, 0.5)  -- Inside of Red Base
  40.         red_base_ramp1 = GEO.newRectPrism("red_base_ramp1", 2.4, 0.8, 1.6, 99.5, -159.5, 0.9)  -- Suggested action:  Create two AntiGEOs to subtract out the area from the ramps leading to the top of the base.
  41.         red_base_ramp2 = GEO.newRectPrism("red_base_ramp2", 2.4, 0.8, 1.6, 91.5, -159.5, 0.9)
  42.         red_base_ramp1:antigeo(true, red_base_inside)  -- Make red_base_ramp1 an AntiGEO to red_base_inside.
  43.         red_base_ramp2:antigeo(true, red_base_inside)  -- Make red_base_ramp2 an AntiGEO to red_base_inside.
  44.        
  45.         blue_base_inside = GEO.newCylinder("in_blue_base", 5, 2, 40.15, -79.1, 0.3)  -- Inside of Blue Base
  46.         blue_base_ramp1 = GEO.newRectPrism("blue_base_ramp1", 2.4, 0.8, 1.6, 36, -79.1, 0.8)  -- Suggested action:  Create two AntiGEOs to subtract out the area from the ramps leading to the top of the base.
  47.         blue_base_ramp2 = GEO.newRectPrism("blue_base_ramp2", 2.4, 0.8, 1.6, 44.2, -79.1, 0.8)
  48.         blue_base_ramp1:antigeo(true, blue_base_inside)  -- Make blue_base_ramp1 an AntiGEO to blue_base_inside.
  49.         blue_base_ramp2:antigeo(true, blue_base_inside)  -- Make blue_base_ramp2 an AntiGEO to blue_base_inside.
  50.        
  51.     -- Boarding Action --
  52.     elseif map == "boardingaction" then
  53.    
  54.         blue_level_1 = GEO.newRectPrism("level1blue", 9, 45, 2.75, 19, 0, -4.5)  -- Level 1 Blue
  55.         blue_level_1:damagezone(true, 60, 4, "GET OUT OF THE LAVA", function(geo, player) return getteam(player) == 0 end)  -- Suggested action:  Make this a damagezone for "Lava Zombies" where humans are damaged for being on certain levels for too long.
  56.        
  57.         blue_level_2 = GEO.newRectPrism("level2blue", 9, 45, 2.75, 19, 0, -1.75)  -- Level 2 Blue
  58.         blue_level_2:damagezone(true, 50, 8, "GET OUT OF THE LAVA", function(geo, player) return getteam(player) == 0 end)  -- Suggested action:  Make this a damagezone for "Lava Zombies" where humans are damaged for being on certain levels for too long.
  59.  
  60.         blue_level_3 = GEO.newRectPrism("level3blue", 9, 45, 2.75, 19, 0, 1)  -- Level 3 Blue
  61.         blue_level_3:damagezone(true, 40, 12, "GET OUT OF THE LAVA", function(geo, player) return getteam(player) == 0 end)  -- Suggested action:  Make this a damagezone for "Lava Zombies" where humans are damaged for being on certain levels for too long.
  62.        
  63.         blue_level_4 = GEO.newRectPrism("level4blue", 9, 45, 2.75, 19, 0, 3.75)  -- Level 4 Blue
  64.         blue_level_4:damagezone(true, 30, 16, "GET OUT OF THE LAVA", function(geo, player) return getteam(player) == 0 end)  -- Suggested action:  Make this a damagezone for "Lava Zombies" where humans are damaged for being on certain levels for too long.
  65.        
  66.         blue_level_5 = GEO.newRectPrism("level5blue", 9, 45, 2.75, 19, 0, 6.5)  -- Level 5 Blue
  67.         blue_level_5:damagezone(true, 20, 20, "GET OUT OF THE LAVA", function(geo, player) return getteam(player) == 0 end)  -- Suggested action:  Make this a damagezone for "Lava Zombies" where humans are damaged for being on certain levels for too long.
  68.        
  69.         red_level_1 = GEO.newRectPrism("level1red", 9, 45, 2.75, 2, 0, -4.5)  -- Level 1 Red
  70.         red_level_1:damagezone(true, 60, 4, "GET OUT OF THE LAVA", function(geo, player) return getteam(player) == 0 end)  -- Suggested action:  Make this a damagezone for "Lava Zombies" where humans are damaged for being on certain levels for too long.
  71.        
  72.         red_level_2 = GEO.newRectPrism("level2red", 9, 45, 2.75, 2, 0, -1.75)  -- Level 2 Red
  73.         red_level_2:damagezone(true, 50, 8, "GET OUT OF THE LAVA", function(geo, player) return getteam(player) == 0 end)  -- Suggested action:  Make this a damagezone for "Lava Zombies" where humans are damaged for being on certain levels for too long.
  74.        
  75.         red_level_3 = GEO.newRectPrism("level3red", 9, 45, 2.75, 2, 0, 1)  -- Level 3 Red
  76.         red_level_3:damagezone(true, 40, 12, "GET OUT OF THE LAVA", function(geo, player) return getteam(player) == 0 end)  -- Suggested action:  Make this a damagezone for "Lava Zombies" where humans are damaged for being on certain levels for too long.
  77.        
  78.         red_level_4 = GEO.newRectPrism("level4red", 9, 45, 2.75, 2, 0, 3.75)  -- Level 4 Red
  79.         red_level_4:damagezone(true, 30, 16, "GET OUT OF THE LAVA", function(geo, player) return getteam(player) == 0 end)  -- Suggested action:  Make this a damagezone for "Lava Zombies" where humans are damaged for being on certain levels for too long.
  80.        
  81.         red_level_5 = GEO.newRectPrism("level5red", 9, 45, 2.75, 2, 0, 6.5)  -- Level 5 Red
  82.         red_level_5:damagezone(true, 20, 20, "GET OUT OF THE LAVA", function(geo, player) return getteam(player) == 0 end)  -- Suggested action:  Make this a damagezone for "Lava Zombies" where humans are damaged for being on certain levels for too long.
  83.    
  84.     elseif map == "carousel" then
  85.    
  86.         red_base = GEO.newRectPrism("red_base", 6.5, 4.5, 3, 5.9, -13.25, -3.3)  -- Inside of Red Base
  87.         red_base_anti = GEO.newCylinder("red_base_antigeo", 1, 3, 9.09, -11.4, -3.3)  -- Suggested Action:  Since there is a 45° angle at one opening of this base and GEOs cannot be rotated to 45° angles, I use an AntiGEO Cylinder to approximate a 45° angle.
  88.         red_base_anti:antigeo(true, red_base)  -- Make red_base_anti an AntiGEO to red_base.
  89.        
  90.         blue_base = GEO.newRectPrism("blue_base", 6.5, 4.5, 3, -5.9, 13.25, -3.3)  -- Inside of blue Base
  91.         blue_base_anti = GEO.newCylinder("blue_base_antigeo", 1, 3, -9.05, 11.4, -3.3)  -- Suggested Action:  Since there is a 45° angle at one opening of this base and GEOs cannot be rotated to 45° angles, I use an AntiGEO Cylinder to approximate a 45° angle.
  92.         blue_base_anti:antigeo(true, blue_base)  -- Make blue_base_anti an AntiGEO to blue_base.
  93.        
  94.         base_anti = GEO.newCylinder("base_antigeo", 12.8, 3, 0, 0, -2.5)  -- Suggested Action:  This AntiGEO covers the bottom floor of the map and allows for a more accurate GEO for both red and blue base.
  95.         base_anti:antigeo(true, red_base, blue_base)  -- Make base_anti an AntiGEO to both red_base and blue_base.
  96.        
  97.         first_floor = GEO.newCylinder("first_floor", 17, 3, 0, 0, -2.75)  -- First floor of Carousel
  98.         second_floor = GEO.newCylinder("second_floor", 17, 5, 0, 0, 1.25)  -- Second floor of Carousel
  99.  
  100.    
  101.     end
  102. end
  103.  
  104.  
  105.  
  106. --[[
  107.  
  108.     This stuff down here just makes this script a better tool for creating your own GEOs.
  109.  
  110. --]]
  111.  
  112. -- Geo Event Functions
  113.  
  114. function OnGeoEnter(geo, player, objId)
  115.  
  116.     -- This will print "Enter:" then the name of the GEO you have entered unless it's an AntiGEO.
  117.     if not geo:antigeo() then
  118.         say("Enter: " .. geo:name())
  119.     end
  120.    
  121.     return true
  122. end
  123.  
  124. function OnGeoExit(geo, player, objId)
  125.    
  126.     -- This will print "Exit:" then the name of the GEO you have exited unless it's an AntiGEO.
  127.     if not geo:antigeo() then
  128.         say("Exit: " .. geo:name())
  129.     end
  130.    
  131.     return true
  132. end
  133.  
  134. -- Disallows interaction with full-spectrum vision and overshield for testing purposes.
  135. function OnObjectInteraction(player, objId, mapId)
  136.  
  137.     local tagname, tagtype = gettaginfo(mapId)
  138.     if tagname == "powerups\\full-spectrum vision" or tagname == "powerups\\over shield" then
  139.         return false
  140.     end
  141. end
  142.  
  143. -- The below code is useful for finding coordinates and resetting the map.  
  144. -- Just type "coords" into the chat, and your coordinates will be printed in the console for you to record.
  145. function OnServerChat(player, type, message)
  146.    
  147.     if player then
  148.         local cmd, args = cmdsplit(message)
  149.         cmd = string.lower(cmd)
  150.        
  151.         if cmd == "coords" then
  152.             local m_player = getplayer(player)
  153.             local objId = readdword(m_player, 0x34)
  154.             local m_object = getobject(objId)
  155.             if m_object then
  156.                 local x, y, z = getobjectcoords(objId)
  157.                 hprintf("x: " .. x .. " | y: " .. y .. " | z: " .. z)
  158.             end
  159.         end
  160.     end
  161.    
  162.     return true
  163. end
  164.  
  165. function cmdsplit(str)
  166.  
  167.     local subs = {}
  168.     local sub = ""
  169.     local ignore_quote, inquote, endquote
  170.     for i = 1,string.len(str) do
  171.         local bool
  172.         local char = string.sub(str, i, i)
  173.         if char == " " then
  174.             if (inquote and endquote) or (not inquote and not endquote) then
  175.                 bool = true
  176.             end
  177.         elseif char == "\\" then
  178.             ignore_quote = true
  179.         elseif char == "\"" then
  180.             if not ignore_quote then
  181.                 if not inquote then
  182.                     inquote = true
  183.                 else
  184.                     endquote = true
  185.                 end
  186.             end
  187.         end
  188.        
  189.         if char ~= "\\" then
  190.             ignore_quote = false
  191.         end
  192.        
  193.         if bool then
  194.             if inquote and endquote then
  195.                 sub = string.sub(sub, 2, string.len(sub) - 1)
  196.             end
  197.            
  198.             if sub ~= "" then
  199.                 table.insert(subs, sub)
  200.             end
  201.             sub = ""
  202.             inquote = false
  203.             endquote = false
  204.         else
  205.             sub = sub .. char
  206.         end
  207.        
  208.         if i == string.len(str) then
  209.             if string.sub(sub, 1, 1) == "\"" and string.sub(sub, string.len(sub), string.len(sub)) == "\"" then
  210.                 sub = string.sub(sub, 2, string.len(sub) - 1)
  211.             end
  212.             table.insert(subs, sub)
  213.         end
  214.     end
  215.    
  216.     local cmd = subs[1]
  217.     local args = subs
  218.     table.remove(args, 1)
  219.    
  220.     return cmd, args
  221. end
  222.  
  223. -- GEO 2.3.2 (Geometric Environment Objects)
  224.  
  225. --[[
  226.     Documentation:
  227.    
  228.     This script allows the creation of environmental volumes in the shapes of spheres, rectangular prisms, cylinders, rectangles, or circles.
  229.     These environmental volumes allow you to create complex environments with just a few lines of code, including the ability to make a volume a killzone, in which players will immediately die upon entering the volume or any user-defined actions as defined by the functions OnGeoEnter and OnGeoExit.
  230.     GEOs can be returned and manipulated as objects in Lua; this means you can set a variable to be equal to a GEO, making it easy to manipulate in various areas of code.
  231.    
  232.     Changes from GEOs for Phasor 058 and GEOs for 2.0:
  233.    
  234.         -- GEO:damagezone now has the following function header:  GEO:damagezone(boolean, damage).
  235.         -- GEO:freeze has been changed to GEO:unfollow.
  236.        
  237.     Changes in GEO 2.2:
  238.    
  239.         -- Players are now automatically monitored for every GEO.  If you want a GEO to monitor other objects, you still need to use GEO:monitor.
  240.         -- Support for cylinder orientation.  You can now orient your cylinder along the x, y, or z axis.
  241.         -- Added GEO.clear() to destroy all GEOs.
  242.         -- Added GEO:antigeo(); an AntiGEO subtracts from the volume of another GEO.  This allows for you to create complex geometry beyond only spheres, cylinders, and rectangular prisms.  Read docs for more info.
  243.         -- GEO:killzone() improved; now takes three arguments:  <boolean> <delay> <kill_message>
  244.         -- GEO:surface() now works; use this to show 3D representations of your GEOs.
  245.         -- GEO:perimeter() and GEO:surface create objects which are unaffected by physics.
  246.         -- Returning false in OnGeoEnter and OnGeoExit now works on non-player objects.
  247.         -- Various bug fixes
  248.        
  249.     Changes in GEO 2.3.1:
  250.    
  251.         -- GEO:killzone() further improved; now takes infinite arguments:  <boolean> <delay> <kill_message> <function> <args...>
  252.         -- GEO:damagezone() improved; now takes infinite arguments:  <boolean> <damage> <delay> <damage_message> <function> <args...>
  253.         -- GEO:surface() fixed for rectangular prisms (performance should be better too); also fixed for spheres (the rings are now more uniform)
  254.         -- Added GEO:vectorintersect(); returns the points at which a vector (given by two points) intersects the given GEO (does not take AntiGEOs into account yet).  Arguments:  <point1>, <point2>
  255.         -- Added GEO:containspoint(); returns a boolean (true or false) if the specified GEO contains the specified point.  Arguments:  <point>
  256.         -- Added GEO:randomcoords(); returns random coordinates within the specified GEO (does not take AntiGEOs into account yet).  No arguments.
  257.         -- Fixed a terrible handling of AntiGEOs (now uses GEO:contains() like it should have in the first place)
  258.    
  259.     GEO Creation Functions:
  260.  
  261.     GEO.newSphere(name, radius, x_coord, y_coord, z_coord)
  262.    
  263.         -- name: <string> Name of this new GEO (must be unique).
  264.         -- radius: <float> Radius of this sphere.
  265.         -- x_coord: <float> X-coordinate of the center of this sphere.
  266.         -- y_coord: <float> Y-coordinate of the center of this sphere.
  267.         -- z_coord: <float> Z-coordinate of the center of this sphere.
  268.        
  269.         Example:
  270.            
  271.             local sphere = GEO.newSphere("Sphere1", 5, 0, 0, 0)
  272.            
  273.         Notes:
  274.        
  275.             GEO.newSphere creates a spherical GEO with the specified name (keep in mind GEO names must be unique from each other), radius, and x, y, and z-coordinates of the center of the sphere. Returns the sphere as an object.
  276.  
  277.     GEO.newRectPrism(name, x_length, y_length, z_length, x_coord, y_coord, z_coord)
  278.    
  279.         -- name: <string> Name of this new GEO (must be unique).
  280.         -- x_length: <float> Length of this rectangular prism in the X direction.
  281.         -- y_length: <float> Length of this rectangular prism in the Y direction.
  282.         -- z_length: <float> Length of this rectangular prism in the Z direction.
  283.         -- x_coord: <float> X-coordinate of the center of this rectangular prism.
  284.         -- y_coord: <float> Y-coordinate of the center of this rectangular prism.
  285.         -- z_coord: <float> Z-coordinate of the center of this rectangular prism.
  286.    
  287.         Example:
  288.  
  289.             local rectprism = GEO.newRectPrism("RectPrism1", 3, 3, 10, 0, 0, 5)
  290.            
  291.         Notes:
  292.            
  293.             GEO.newRectPrism creates a rectangular prism GEO with the specified name (keep in mind GEO names must be unique from each other), x, y, and z lengths, and x, y, and z-coordinates of the center of the rectangular prism. Returns the rectangular prism as an object.
  294.  
  295.     GEO.newCylinder(name, radius, height, x_coord, y_coord, z_coord, [orientation])
  296.    
  297.         -- name: <string> Name of this new GEO (must be unique).
  298.         -- radius: <float> Radius of this cylinder.
  299.         -- height: <float> Height of this cylinder.
  300.         -- x_coord: <float> X-coordinate of the center of this cylinder.
  301.         -- y_coord: <float> Y-coordinate of the center of this cylinder.
  302.         -- z_coord: <float> Z-coordinate of the center of this cylinder.
  303.         -- orientation: <string> Axis of orientation ("x", "y", or "z") (default = "z").
  304.    
  305.         Example:
  306.        
  307.             local cylinder = GEO.newCylinder("Cylinder1", 3, 10, 0, 0, 0)
  308.    
  309.         Notes:
  310.            
  311.             GEO.newCylinder creates a cylindrical GEO with the specified name (keep in mind GEO names must be unique from each other), radius, height, and x, y, and z-coordinates of the center of the cylinder. Returns the cylinder as an object.
  312.  
  313.     GEO.newRect(name, width, height, x_coord, y_coord, z_coord, [orientation])
  314.    
  315.         -- name: <string> Name of this new GEO (must be unique).
  316.         -- width: <float> Width of this rectangle.
  317.         -- height: <float> Height of this rectangle.
  318.         -- x_coord: <float> X-coordinate of the center of this rectangle.
  319.         -- y_coord: <float> Y-coordinate of the center of this rectangle.
  320.         -- z_coord: <float> Z-coordinate of the center of this rectangle.
  321.         -- orientation: <string> Axis of orientation ("x", "y", or "z") (default = "z").
  322.        
  323.         Example:
  324.  
  325.             local rectangle = GEO.newRect("Rect1", 5, 10, 0, 0, 5, "x")
  326.            
  327.         Notes:
  328.        
  329.             GEO.newRect creates a rectangular GEO with the specified name (keep in mind GEO names must be unique from each other), width, height, x, y, and z-coordinates of the center of the rectangle, and orientation. The orientation specifies which axis ("x", "y", or "z") has a length of 0 in the rectangle. For example, by default, orientation is "z". This means you would see the rectangle if you were looking down on top of it (or up from underneath). You can also think of the orientation axis as being the axis that punches through the center of the object.
  330.  
  331.     GEO.newCircle(name, radius, x_coord, y_coord, z_coord, [orientation])
  332.    
  333.         -- name: <string> Name of this new GEO (must be unique).
  334.         -- radius: <float> Radius of this circle.
  335.         -- x_coord: <float> X-coordinate of the center of this circle.
  336.         -- y_coord: <float> Y-coordinate of the center of this circle.
  337.         -- z_coord: <float> Z-coordinate of the center of this circle.
  338.         -- orientation: <string> Axis of orientation ("x", "y", or "z") (default = "z")
  339.        
  340.         Example:
  341.  
  342.             local circle = GEO.newCircle("Circle1", 10, 0, 0, 5, "x")
  343.  
  344.         Notes:
  345.        
  346.             GEO.newCircle creates a circular GEO with the specified name (keep in mind GEO names must be unique from each other), radius, x, y, and z-coordinates of the center of the circle, and orientation. The orientation specifies which axis ("x", "y", or "z") has a length of 0 in the circle. For example, by default, orientation is "z". This means you would see the circle if you were looking down on top of it (or up from underneath). You can also think of the orientation axis as being the axis that punches through the center of the object.
  347.  
  348.  
  349.     Once you've created your GEO, you want to make it do something. The following are GEO-editing functions.
  350.     Note that the following functions are members of the GEO metatable. This means to call the function, you specify the GEO variable before the function (separated by a colon). See the functions below for more specific examples.
  351.    
  352.     GEO Editing Functions:
  353.  
  354.     GEO:delete()
  355.    
  356.         Example:
  357.  
  358.             local sphere = GEO.newSphere("Sphere", 5, 0, 0, 0)
  359.             sphere:delete()
  360.  
  361.         Notes:
  362.            
  363.             Deletes the specified GEO.
  364.    
  365.     GEO:move(x_coord, y_coord, z_coord)
  366.    
  367.         -- x_coord: <float> New x-coordinate of the center of this GEO.
  368.         -- y_coord: <float> New y-coordinate of the center of this GEO.
  369.         -- z_coord: <float> New z-coordinate of the center of this GEO.
  370.    
  371.         Example:
  372.  
  373.             local sphere = GEO.newSphere("Sphere", 5, 0, 0, 0)
  374.             sphere:move(0, 0, 10)
  375.        
  376.         Notes:
  377.    
  378.             Moves the center of the specified GEO to the specified x, y, and z-coordinates.
  379.  
  380.     GEO:radius([new_radius])
  381.    
  382.         -- new_radius: <float> New radius of specified GEO (if no radius is specified, the radius of the GEO is returned).
  383.    
  384.         Example:
  385.  
  386.             local cylinder = GEO.newCylinder("Cylinder", 10, 20, 0, 0, 0)
  387.             local cyl_radius = cylinder:radius()
  388.             if cyl_radius < 20 then
  389.                 cylinder:radius(20)
  390.             end
  391.            
  392.         Notes:
  393.  
  394.             If a new_radius is specified, changes the radius of the specified GEO to the value of new_radius. Otherwise, returns the radius of the specified GEO. Note that this can only be used for spheres, circles, and cylinders.
  395.  
  396.     GEO:size([x_length], [y_length], [z_length])
  397.    
  398.         -- x_length: <float> New length of GEO in the x direction.
  399.         -- y_length: <float> New length of GEO in the y direction.
  400.         -- z_length: <float> New length of GEO in the z direction.
  401.         -- Read the notes for descriptions about how the optional parameters work in this function.
  402.    
  403.         Example:
  404.  
  405.             -- Create three GEOs
  406.             local rectprism = GEO.newRectPrism("RectPrism", 5, 5, 10, 0, 0, 5)
  407.             local rectangle = GEO.newRect("Rect", 5, 10, 0, 0, 5, "y")
  408.             local cylinder = GEO.newCylinder("Cylinder", 5, 20, 0, 0, 10)
  409.  
  410.             -- Get sizes of all three shapes
  411.             local rpx, rpy, rpz = rectprism:size()
  412.             local rw, rh = rectangle:size()
  413.             local cyl_height = cylinder:size()
  414.  
  415.             -- Double their sizes
  416.             rectprism:size(rpx * 2, rpy * 2, rpz * 2)
  417.             rectangle:size(rw * 2, 0, rh * 2)
  418.             cylinder:size(cyl_height * 2)
  419.  
  420.         Notes:
  421.    
  422.             If any coordinate is specified, changes the GEO's size in the x, y, and z directions respectively. If no coordinate is specified in any direction, this returns the size of the object. Note this can only be used for rectangular prisms, rectangles, and cylinders. Also note if you're attempting to retrieve the size of an object, rectangular prisms will return three values (x, y, and z), rectangles will return two values (width and height), and cylinders will return one value (height). Also note that when changing the height of a cylinder, you need only specify one number (since you're only changing the height). If you're trying to change the size of a rectangle, keep its orientation in mind when changing the x, y, and z lengths (changes in length of the axis of which the rectangle is oriented will be ignored).
  423.    
  424.     GEO:contains(objId)
  425.    
  426.         objId: <object id> Object being tested to see if it is inside of the specfied GEO.
  427.    
  428.         Example:
  429.  
  430.             local sphere = GEO.newSphere("Sphere", 5, 0, 0, 0)
  431.  
  432.             function OnPlayerSpawn(player)
  433.            
  434.                 local m_player = getplayer(player)
  435.                 local objId = readdword(m_player, 0x34)
  436.  
  437.                 if sphere:contains(objId) then
  438.                     say(getname(player) .. " has spawned inside of the sphere")
  439.                 end
  440.             end
  441.            
  442.         Notes:
  443.    
  444.             Checks if the specified objId is within the specified GEO. Returns true or false.
  445.            
  446.     GEO:containspoint(point)
  447.    
  448.         point: <table> Coordinates being checked.
  449.        
  450.         Example:
  451.        
  452.             local sphere = GEO.newSphere("Sphere", 5, 0, 0, 0)
  453.             local point = {10, 10, 0}
  454.             local insphere = GEO:containspoint(point)
  455.             if insphere(point) then
  456.                 say("(10, 10, 0) is in the sphere!")
  457.             end
  458.            
  459.         Notes:
  460.        
  461.             Make sure the table passed is in {x, y, z} format.
  462.            
  463.     GEO:vectorintersect(pointA, pointB)
  464.    
  465.         pointA: <table> First point in vector.
  466.         pointB: <table> Second point in vector.
  467.        
  468.         Example:
  469.        
  470.             -- This is a poor example that shows simply functionality
  471.             sphere = GEO.newSphere("sphere", 5, 0, 0, 0)
  472.            
  473.             function OnPlayerSpawn(player)
  474.                
  475.                 local m_player = getplayer(player)
  476.                 local objId = readdword(m_player, 0x34)
  477.                 local x, y, z = getobjectcoords(objId)
  478.                 local point1, point2 = sphere:vectorintersect({x, y, z}, {x, y, z + 10})  -- This will check if the line between where the player is standing and 10 units above them ever intersects the sphere.
  479.                 if point1 then
  480.                     say("x: " .. point1[1] .. " y: " .. point1[2] .. " z: " .. point1[3])
  481.                 end
  482.                
  483.                 if point2 then
  484.                     say("x: " .. point2[1] .. " y: " .. point2[2] .. " z: " .. point2[3])
  485.                 end
  486.             end
  487.            
  488.         Notes:
  489.        
  490.             This function can return as many as two intersection points and as few as zero.  If a point does not exist, this function returns nil for that point.  This function does not yet take AntiGEOs into account.  Make sure the tables for the points passed are in {x, y, z} format.
  491.  
  492.     GEO:follow(objId)
  493.    
  494.         objId: <object id> Object this GEO should follow.
  495.    
  496.         Example:
  497.  
  498.             function OnPlayerSpawn(player)
  499.  
  500.                 local m_player = getplayer(player)
  501.                 local objId = readdword(m_player, 0x34)
  502.                
  503.                 -- Create a sphere with the objId in the name so you know it is unique
  504.                 local sphere = GEO.newSphere("Sphere" .. objId, 5, 0, 0, 0)
  505.                 sphere:follow(objId)
  506.             end
  507.            
  508.         Notes:
  509.        
  510.             Sets a GEO to follow the specified objId. The GEO will be automatically deleted if the object it is following has also been deleted.
  511.  
  512.  
  513.     GEO:unfollow()
  514.    
  515.         Example:
  516.  
  517.             function OnPlayerSpawn(player)
  518.            
  519.                 local m_player = getplayer(player)
  520.                 local objId = readdword(m_player, 0x34)
  521.  
  522.                 -- Create a sphere with the objId in the name so you know it is unique
  523.                 local sphere = GEO.newSphere("Sphere" .. objId, 5, 0, 0, 0)
  524.                 sphere:follow(objId)
  525.                 sphere:unfollow()
  526.             end
  527.  
  528.         Notes:
  529.        
  530.             Tells the specified GEO to stop following the objId it is currently following.
  531.  
  532.     GEO:velocity(x_velocity, y_velocity, z_velocity)
  533.    
  534.         x_velocity: <float> GEO's velocity in the x direction.
  535.         y_velocity: <float> GEO's velocity in the y direction.
  536.         z_velocity: <float> GEO's velocity in the z direction.
  537.    
  538.         Example:
  539.  
  540.             local rectangle = GEO.newRectPrism("RectPrism", math.inf, math.inf, 100, 0, 0, -50)
  541.             rectangle:velocity(0, 0, 0.1)
  542.  
  543.         Notes:
  544.    
  545.             Sets the velocity of the specified GEO in the x, y, and z directions.
  546.    
  547.     GEO:killzone([boolean], [delay], [kill_message], [func], [args...])
  548.    
  549.         boolean: <boolean> The ability of this GEO to be a killzone (if no boolean is specified, the current killzone boolean is returned).
  550.         delay: <int> The time (in seconds) the GEO should wait to kill a player after they enter.
  551.         kill_message: <string> The message a player will receive when they are killed by this GEO.
  552.         func: <function> A special function that MUST return true or false and MUST pass the GEO, then player as its first two arguments.
  553.         args...: <data> Additional arguments to be passed into the function.
  554.    
  555.         Example:
  556.  
  557.             -- Kill a player immediately for entering a sphere
  558.             local killsphere = GEO.newSphere("Sphere", 10, 0, 0, 0)
  559.             killsphere:killzone(true)
  560.            
  561.             function isredwednesday(geo, player, day)
  562.            
  563.                 if getteam(player) == 0 then
  564.                     if day == "Wednesday" then
  565.                         return true
  566.                     end
  567.                 end
  568.                
  569.                 return false
  570.             end
  571.            
  572.             -- Kill a player after being in a cube for 5 seconds if they're on the Red Team and today is Wednesday.
  573.             local killcube = GEO.newRectPrism("Cube", 5, 5, 5, 10, -10, 5)
  574.             killcube:killzone(true, 5, "Since you're on the red team and were in this cube for five seconds and today is a Wednesday, you shall now die.", isredwednesday, os.date("%A"))
  575.  
  576.         Notes:
  577.    
  578.             Toggles the ability for a GEO to be a killzone. If a function is specified, the function header's first two arguments MUST be the geo, then the player (such as in the example above) and returns true if the player should be killed and false if the player should not be. If a GEO is a killzone and no delay is specified or the delay = 0, any player to enter the GEO will automatically die. If a delay is specified, the player will be killed after the amount of seconds specified by delay. If a kill_message is specified, the player will receive that message when they die. Otherwise, no message will be sent.
  579.    
  580.     GEO:damagezone([boolean], [damage], [delay], [damage_message], [func], [args...])
  581.    
  582.         boolean: <boolean> Indicates whether or not this GEO is a damagezone (if no boolean is specified, the current damagezone boolean is returned).
  583.         damage: <float> Amount of damage per second this GEO does to a player within it.
  584.         delay: <int> The time (in seconds) the GEO should wait to damage a player after they enter.
  585.         damage_message: <string> The message a player will receive when they are initially damaged by this GEO.
  586.         func: <function> A special function that MUST return true or false and MUST pass the GEO, then player as its first two arguments.
  587.         args...: <data> Additional arguments to be passed into the function.
  588.            
  589.         Example:
  590.  
  591.             -- Damage a player as soon as they enter this sphere.
  592.             local damagesphere = GEO.newSphere("Sphere", 10, 0, 0, 0)
  593.             damagesphere:damagezone(true, 10)
  594.            
  595.             -- Damage a player after 10 seconds if they are named Nuggets and there is a GEO called "Sphere".
  596.             function isnuggetssphere(geo, player, sphere_exists)
  597.            
  598.                 if getname(player) == "Nuggets" then
  599.                     if sphere_exists then
  600.                         return true
  601.                     end
  602.                 end
  603.                
  604.                 return false
  605.             end
  606.            
  607.             local damagecylinder = GEO.newCylinder("Cylinder", 5, 10, 0, 0, 5)
  608.             damagecylinder:damagezone(true, 50, 10, "Your name is Nuggets and there is a sphere somewhere around here.", isnuggetssphere, GEO.get("Sphere"))
  609.  
  610.         Notes:
  611.            
  612.             Toggles the ability for a GEO to be a damagezone. If a GEO is a damagezone, the player will be damaaged at the rate you specify.
  613.  
  614.     GEO:face(orientation, direction)
  615.    
  616.         orientation: <string> The axis on which the face will be created ("x", "y", or "z").
  617.         direction: <string> The specification of the "top" or "bottom" face in the specified orientation ("+" or "-").
  618.    
  619.         Example:
  620.  
  621.             local cylinder = GEO.newCylinder("Cylinder", 5, 10, 0, 0, 5)
  622.             local topcircle = cylinder:face("z", "+")
  623.             local bottomcircle = cylinder:face("z", "-")
  624.  
  625.         Notes:
  626.    
  627.             Cuts the face of the specified three-dimensional GEO off to create a two-dimensional GEO given the orientation ("x", "y", or "z") and direction ("+" or "-"). For example, if the orientation is "z" and the direction is "+", and you're passing a rectangular prism, you will end up with the a rectangle with the same position and size as the top face of your rectangular prism. Note you can only use this for rectangular prisms and cylinders. Also note that for cylinders, you may only use the "z" orientation. Returns the new two-dimensional GEO as an object.
  628.    
  629.     GEO:extend(orientation, direction, amount)
  630.    
  631.         orientation: <string> The axis on which the face will be extended ("x", "y", or "z").
  632.         direction: <string> The specification of the "top" or "bottom" face in the specified orientation ("+" or "-").
  633.         amount: <float> Distance the specified face will be extended.
  634.    
  635.         Example:
  636.  
  637.             local cylinder = GEO.newCylinder("Cylinder", 5, 10, 0, 0, 5)
  638.             cylinder:extend("z", "+", 10)
  639.  
  640.         Notes:
  641.    
  642.             Extends the specified face by the specified amount (see the function face for how to specify orientation and direction). Note that this may only be used on rectangular prisms, rectangles, and cylinders. Also note that extending a face does not change the object's center. If you use GEO:surface() after using GEO:extend(), you will not see the changes GEO:extend() has made.
  643.            
  644.     GEO:antigeo([boolean, geos...])
  645.    
  646.         boolean: <boolean> Toggles whether or not this is an AntiGEO.
  647.         geos...: <data> Defines for which GEOs this is an AntiGEO (infinite number of argumnets).
  648.        
  649.         Example:
  650.            
  651.             -- Battle Creek Blue Base Roof
  652.             blue_base_roof = GEO.newRectPrism("blue_roof", 9.5, 7.8, 1.3, 1.95, 13.9, 1.85)
  653.             blue_roof_anti = GEO.newRectPrism("blue_roof_anti", 10, 10, 2, 28.75, 14, 1.5)
  654.             -- Cut out the section of the blue_base_roof GEO that is inside of the room where the CTF flag is
  655.             blue_roof_anti:antigeo(true, blue_base_roof)
  656.            
  657.         Notes:
  658.        
  659.             This function works like object subtraction in CAD programs. If you are unfamiliar with this, this link should help describe it: http://codevisually.com/wp-content/uploads/2011/12/cvdec1_06.jpg (look at the a.subtract(b) picture). If you are within a GEO and enter one of its AntiGEOs, it will count as exiting the first GEO. Keep in mind that AntiGEOs are also GEOs and may have their own AntiGEOs. If no boolean or GEOs are specified, this returns a boolean (true or false) describing if this GEO is an AntiGEO. When using GEO:surface() or GEO:perimeter on a GEO, all of that GEO's AntiGEOs will be shown as well with Overshield powerups.
  660.    
  661.     GEO:copy([name])
  662.    
  663.         name: <string> Name given to the copy of the specified GEO (if no name is specified, a default name is given).
  664.        
  665.         Example:
  666.  
  667.             local sphere = GEO.newSphere("Sphere", 5, 0, 0, 0)
  668.             local sphere2 = sphere:copy()
  669.  
  670.         Notes:
  671.        
  672.             Returns a copy of the specified GEO with the specified name. If no name is specified, a default name will be given.
  673.    
  674.     GEO:monitor(objId)
  675.    
  676.         objId: <object id> The object this GEO should monitor for entering/exiting.
  677.    
  678.         Example:
  679.  
  680.             local rectprism = GEO.newRectPrism("RectPrism", math.inf, math.inf, 100, 0, 0, -50)
  681.  
  682.             function OnObjectCreation(objId)
  683.            
  684.                 local m_object = getobject(objId)
  685.                 local mapId = readdword(m_object)
  686.                 local _, tagtype = gettaginfo(mapId)
  687.                
  688.                 if tagtype == "vehi" then
  689.                     rectprism:monitor()  -- Monitor all vehicles
  690.                 end
  691.             end
  692.        
  693.         Notes:
  694.    
  695.             Tells the specified GEO to monitor when objId enters and exits it. Note that this must be used in order for OnGeoEnter and OnGeoExit to work for the specified GEO and objId. This is for the sake of the script not having to check the coordinates of every single object and comparing them to the volumes of every single GEO every 1/100 of a second. Also note that as of GEO 2.2, it is not necessary to use this function on players; GEOs will automatically monitor players.
  696.  
  697.     GEO:coords()
  698.    
  699.         Example:
  700.  
  701.             local sphere = GEO.newSphere("Sphere", 10, 0, 0, 0)
  702.             local x, y, z = sphere:coords()
  703.  
  704.         Notes:
  705.    
  706.             Returns the x, y, and z coordinates of the center of the specified GEO.
  707.    
  708.  
  709.     GEO:high(orientation)
  710.    
  711.         orientation: <string> Axis of which you're attempting to find the highest point ("x", "y", or "z").
  712.        
  713.         Example:
  714.  
  715.             local sphere = GEO.newSphere("Sphere", 10, 0, 0, 0)
  716.             local zhigh = sphere:high("z")
  717.  
  718.         Notes:
  719.    
  720.             Returns the highest coordinate in the specified orientation (i.e. geo:high("z") would return the highest z coordinate still considered to be inside of the GEO).
  721.  
  722.     GEO:low(orientation)
  723.    
  724.         orientation: <string> Axis of which you're attempting to find the lowest point ("x", "y", or "z")
  725.    
  726.         Example:
  727.  
  728.             local sphere = GEO.newSphere("Sphere", 10, 0, 0, 0)
  729.             local zlow = sphere:low("z")
  730.  
  731.         Notes:
  732.    
  733.             Returns the lowest coordinate in the specified orientation (i.e. geo:low("z") would return the lowest z coordinate still considered to be inside of the GEO).
  734.  
  735.     GEO:randomcoords()
  736.    
  737.         Example:
  738.  
  739.             local cylinder = GEO.newCylinder("Cylinder", 5, 10, 0, 0, 0)
  740.             local x, y, z = cylinder:randomcoords()
  741.  
  742.         Notes:
  743.    
  744.             Returns random x, y, and z coordinates within the specified GEO.
  745.  
  746.     GEO:type()
  747.    
  748.         Example:
  749.  
  750.             local cylinder = GEO.newCylinder("Cylinder", 5, 10, 0, 0, 0)
  751.             local type = cylinder:type()
  752.            
  753.         Notes:
  754.    
  755.             Returns the type of the specified GEO as a string.
  756.  
  757.     GEO:orientation()
  758.    
  759.         Example:
  760.  
  761.             local cylinder = GEO.newCylinder("Cylinder", 5, 10, 0, 0, 0)
  762.             local orientation = cylinder:orientation()
  763.            
  764.         Notes:
  765.    
  766.             Returns the orientation of a 2-dimensional GEO or Cylinder. Note that this cannot be used on Spheres or RectPrisms.
  767.  
  768.     GEO:name()
  769.    
  770.         Example:
  771.  
  772.             local sphere = GEO.newSphere("Sphere", 5, 0, 0, 0)
  773.             local name = sphere:name()
  774.  
  775.         Notes:
  776.    
  777.             Returns the name of the specified GEO.
  778.  
  779.  
  780.     GEO:perimeter([density], [mapId])
  781.    
  782.         density: <int> Amount of objects that should spawn around the perimeter of the GEO.
  783.         mapId: <tag id> The tag of the objects that should spawn around the GEO's perimeter (default = Full-Spectrum Vision)
  784.    
  785.         Example:
  786.  
  787.             local sphere = GEO.newSphere("sphere", 5, 0, 0, 0.5)
  788.             sphere:perimeter(20)
  789.             local rectprism = GEO.newRectPrism("rectprism", 3, 4, 5, 0, 0, 0.5)
  790.             rectprism:perimeter(7, gettagid("weap", "weapons\\flag\\flag"))
  791.            
  792.         Notes:
  793.    
  794.             Spawns the specified object (given via mapId) with the specified density (default 10) about the perimeter of the Z-center of the specified GEO. For spheres and cylinders, the amount of objects spawned will be equal to the density. For rectangular prisms, the density specifies the amount of objects spawned per edge (so a density of 10 will spawn a total of 40 objects). You may want to make sure the GEO's center is slightly above the ground so the objects don't spawn under the map. Also be careful when this function is used, as it does create a lot of objects. If you aren't careful, you'll reach Halo's object limit.
  795.            
  796.     GEO:surface([density], [mapId])
  797.    
  798.         density: <int> Amount of objects that should spawn to create a 3D representation of the GEO.
  799.         mapId: <tag id> The tag of the objects that should spawn to create the 3D representation of the GEO (default = Full-Spectrum Vision)
  800.        
  801.         Example:
  802.            
  803.             local sphere = GEO.newSphere("sphere", 5, 0, 0, 1)
  804.             sphere:surface()
  805.             local cylinder = GEO.newCylinder("cylinder", 4, 7, 0, 0, 1, "x")
  806.             cylinder:surface(20, gettagid("weap", "weapons\\plasma grenade\\plasma grenade"))
  807.            
  808.         Notes:
  809.        
  810.             Spawns the specified object (given via mapId) with the specified density (default 12) to create a 3D representation of the specified GEO. Cannot be used on Circles or Rectangles. The density of these approximate an appropriate amount of objects to be made in order to get a good idea of where a GEO is. It is highly suggested that you use this function only as a means to aid in visualization during testing. This function creates a lot of objects and it is very easy to hit the object limit using it.
  811.  
  812.  
  813.     OnGeoEnter and OnGeoExit Event Functions:
  814.  
  815.     OnGeoEnter(geo, player, objId)
  816.        
  817.         geo: <GEO object> The GEO the player or object is entering.
  818.         player: <player memory id> The player entering the GEO.
  819.         objId: <object id> The objId entering the GEO.
  820.    
  821.         Example:
  822.  
  823.             sphere = GEO.newSphere("sphere", 5, 0, 0, 0)
  824.  
  825.             function OnGeoEnter(geo, player, objId)
  826.  
  827.                 if geo == sphere then
  828.                     privatesay(player, "You may not enter this GEO.")
  829.                     return false
  830.                 end
  831.                
  832.                 return true
  833.             end
  834.        
  835.         Notes:
  836.    
  837.             Called when an object that is being monitored by a GEO enters that GEO. Return 1 to allow objects to enter, return 0 to disallow this.
  838.  
  839.     OnGeoExit(geo, player, objId)
  840.    
  841.         geo: <GEO object> The GEO the player or object is exiting.
  842.         player: <player memory id> The player exiting the GEO.
  843.         objId: <object id> The objId exiting the GEO.
  844.    
  845.         Example:
  846.  
  847.             sphere = GEO.newSphere("sphere", 5, 0, 0, 0)
  848.            
  849.             function OnGeoExit(geo, player, objId)
  850.  
  851.                 if geo == sphere then
  852.                     privatesay(player, "You may not exit this GEO.")
  853.                     return false
  854.                 end
  855.                
  856.                 return true
  857.             end
  858.  
  859.         Notes:
  860.    
  861.             Called when an object that is being monitored by a GEO exits that GEO. Return 1 to allow objects to exit, return 0 to disallow this.
  862.  
  863.  
  864.     Miscellaneous GEO functions:
  865.  
  866.     GEO.get(name)
  867.    
  868.         name: <string> Name of the GEO you're trying to access.
  869.    
  870.         Example:
  871.  
  872.             function OnPlayerSpawn(player)
  873.            
  874.                 local m_player = getplayer(player)
  875.                 local objId = readdword(m_player, 0x34)
  876.                 local sphere = GEO.newSphere("Sphere" .. objId, 5, 0, 0, 0)
  877.                 sphere:follow(objId)
  878.             end
  879.  
  880.             function OnPlayerKill(killer, victim, mode)
  881.  
  882.                 local m_victim = getplayer(victim)
  883.                 local v_objectId = readdword(m_victim, 0x34)
  884.                 local victim_sphere = GEO.get("Sphere" .. v_objectId)
  885.                 victim_sphere:unfollow()
  886.             end
  887.  
  888.         Notes:
  889.    
  890.             Returns a GEO given the specified name.
  891.  
  892.     GEO.followedBy(objId)
  893.    
  894.         objId: <object id> Object being followed by GEOs.
  895.    
  896.         Example:
  897.  
  898.             function OnPlayerKill(killer, victim, mode)
  899.  
  900.                 local geos = GEO.followedBy(victim)
  901.                 for k,v in ipairs(geos) do
  902.                     v:unfollow()
  903.                 end
  904.             end
  905.  
  906.         Notes:
  907.    
  908.             Returns a list of GEOs following the specified objId.
  909.    
  910.     GEO.cleanup(player)
  911.    
  912.         player: <player memory id> Player whose following of GEOs you want to delete.
  913.    
  914.         Example:
  915.  
  916.             -- Note that this is here only for the sake of example; this is not necessary, as the GEOTimer takes care of this automatically.
  917.             function OnPlayerKill(killer, victim, mode)
  918.  
  919.                 GEO.cleanup(victim)
  920.             end
  921.  
  922.         Notes:
  923.    
  924.             Deletes all of the GEOs currently following the specified player.
  925.            
  926.     GEO.clear()
  927.    
  928.         Example:
  929.        
  930.             function OnNewGame(map)
  931.            
  932.                 -- Global variable for map name
  933.                 cur_map = map
  934.             end
  935.            
  936.             function OnServerChat(player, message, type)
  937.                
  938.                 if message == "reset" then
  939.                     svcmd("sv_map_reset")
  940.                     GEO.clear()
  941.                     OnNewGame(cur_map)
  942.                 end
  943.             end
  944.            
  945.         Notes:
  946.        
  947.             Deletes all GEOs currently active on the map.
  948.  
  949.    
  950.     If you have any questions about how GEOs work, PM me (Nuggets) at phasor.proboards.com.
  951. --]]
  952.  
  953. -- GEO --
  954.  
  955. -- Create Metatable
  956. GEO = {}
  957. GEO.__index = GEO
  958.  
  959. -- Set random seed and define infinity
  960. math.randomseed(os.time())
  961. math.inf = 1 / 0
  962.  
  963. function inSphere(objId, x, y, z, r)
  964.  
  965.     local ox, oy, oz = getobjectcoords(objId)
  966.     if ox then
  967.         -- Pythagorean
  968.         local dist = math.sqrt((x - ox) ^ 2 + (y - oy) ^ 2 + (z - oz) ^ 2)
  969.         if dist <= r then
  970.             return true
  971.         end
  972.     end
  973.        
  974.     return false
  975. end
  976.  
  977. function inCircle(objId, x, y, z, r, orientation)
  978.  
  979.     local ox, oy, oz = getobjectcoords(objId)
  980.     if ox then
  981.         -- Default orientation to "z"
  982.         orientation = orientation or "z"
  983.         -- Pythagorean based on circle's orientation
  984.         if orientation == "z" then
  985.             local dist = math.sqrt((x - ox) ^ 2 + (y - oy) ^ 2)
  986.             if dist <= r and oz == z then
  987.                 return true
  988.             end
  989.         elseif orientation == "y" then
  990.             local dist = math.sqrt((x - ox) ^ 2 + (z - oz) ^ 2)
  991.             if dist <= r and oy == y then
  992.                 return true
  993.             end
  994.         elseif orientation == "x" then
  995.             local dist = math.sqrt((y - oy) ^ 2 + (z - oz) ^ 2)
  996.             if dist <= r and ox == x then
  997.                 return true
  998.             end
  999.         end
  1000.     end
  1001.        
  1002.     return false
  1003. end
  1004.  
  1005. function inCylinder(objId, x, y, z, hlow, hhigh, r, orientation)
  1006.  
  1007.     local ox, oy, oz = getobjectcoords(objId)
  1008.     if ox then
  1009.         if orientation == "z" then
  1010.             -- Pythagorean to see if object is within radius of circle
  1011.             local dist = math.sqrt((x - ox) ^ 2 + (y - oy) ^ 2)
  1012.             -- Make sure the object is also within the height of the cylinder
  1013.             if dist <= r and oz >= hlow and oz <= hhigh then
  1014.                 return true
  1015.             end
  1016.         elseif orientation == "x" then
  1017.             local dist = math.sqrt((y - oy) ^ 2 + (z - oz) ^ 2)
  1018.             if dist <= r and ox >= hlow and ox <= hhigh then
  1019.                 return true
  1020.             end
  1021.         elseif orientation == "y" then
  1022.             local dist = math.sqrt((z - oz) ^ 2 + (x - ox) ^ 2)
  1023.             if dist <= r and oy >= hlow and oy <= hhigh then
  1024.                 return true
  1025.             end
  1026.         end
  1027.     end
  1028.        
  1029.     return false
  1030. end
  1031.  
  1032. function inRectangle(objId, xlow, xhigh, ylow, yhigh, zlow, zhigh)
  1033.  
  1034.     -- These functions are essentially the same
  1035.     return inRectPrism(objId, xlow, xhigh, ylow, yhigh, zlow, zhigh)
  1036. end
  1037.  
  1038. function inRectPrism(objId, xlow, xhigh, ylow, yhigh, zlow, zhigh)
  1039.  
  1040.     local x, y, z = getobjectcoords(objId)
  1041.     if x then
  1042.         -- Make sure the coordinates are inside of each extreme of the rectangular prism
  1043.         if x <= xhigh and x >= xlow and y <= yhigh and y >= ylow and z <= zhigh and z >= zlow then
  1044.             return true
  1045.         end
  1046.     end
  1047.        
  1048.     return false
  1049. end
  1050.  
  1051. function randomInSphere(x, y, z, r)
  1052.  
  1053.     -- Increase precision
  1054.     x = math.floor(x * 100)
  1055.     y = math.floor(y * 100)
  1056.     z = math.floor(z * 100)
  1057.     r = math.floor(r * 100)
  1058.        
  1059.     -- Find random values inside of the sphere.
  1060.     return math.random(x - r, x + r + 1) / 100, math.random(y - r, y + r + 1) / 100, math.random(z - r, z + r + 1) / 100
  1061. end
  1062.  
  1063. function randomInCircle(x, y, z, r, orientation)
  1064.  
  1065.     -- Increase precision
  1066.     r = math.floor(r * 100)
  1067.        
  1068.     -- Possible values depend on circle's orientation.
  1069.     if orientation == "z" then
  1070.         x = math.floor(x * 100)
  1071.         y = math.floor(y * 100)
  1072.         return math.random(x - r, x + r + 1) / 100, math.random(y - r, y + r + 1) / 100, z    
  1073.     elseif orientation == "x" then
  1074.         y = math.floor(y * 100)
  1075.         z = math.floor(z * 100)
  1076.         return x, math.random(y - r, y + r + 1) / 100, math.random(z - r, z + r + 1) / 100
  1077.     elseif orientation == "y" then
  1078.         x = math.floor(x * 100)
  1079.         z = math.floor(z * 100)
  1080.         return math.random(x - r, x + r + 1) / 100, y, math.random(z - r, z + r + 1) / 100
  1081.     end
  1082. end
  1083.  
  1084. function randomInCylinder(x, y, z, hlow, hhigh, r, orientation)
  1085.  
  1086.     -- Increase precision
  1087.     x = math.floor(x * 100)
  1088.     y = math.floor(y * 100)
  1089.     z = math.floor(z * 100)
  1090.     hlow = math.floor(hlow * 100)
  1091.     hhigh = math.floor(hhigh * 100)
  1092.     r = math.floor(r * 100)
  1093.        
  1094.     -- Find random values inside of the cylinder depending on its orientation.
  1095.     if orientation == "z" then
  1096.         return math.random(x - r, x + r + 1) / 100, math.random(y - r, y + r + 1) / 100, math.random(hlow, hhigh + 1) / 100
  1097.     elseif orientation == "y" then
  1098.         return math.random(x - r, x + r + 1) / 100, math.random(hlow, hhigh + 1) / 100, math.random(z - r, z + r + 1) / 100
  1099.     elseif orientation == "x" then
  1100.         return math.random(hlow, hhigh + 1) / 100, math.random(y - r, r + r + 1) / 100, math.random(z - r, z + r + 1) / 100
  1101.     end
  1102. end
  1103.  
  1104. function randomInRectPrism(xlow, xhigh, ylow, yhigh, zlow, zhigh)
  1105.  
  1106.     -- Increase precision
  1107.     xlow = math.floor(xlow * 100)
  1108.     xhigh = math.floor(xhigh * 100)
  1109.     ylow = math.floor(ylow * 100)
  1110.     yhigh = math.floor(yhigh * 100)
  1111.     zlow = math.floor(zlow * 100)
  1112.     zhigh = math.floor(zhigh * 100)
  1113.        
  1114.     -- Find random values inside of the rectangular prism.
  1115.     return math.random(xlow, xhigh + 1) / 100, math.random(ylow, yhigh + 1) / 100, math.random(zlow, zhigh)
  1116. end
  1117.  
  1118. function sphereintersect(sphere, pointA, pointB)
  1119.  
  1120.     local points = {}
  1121.  
  1122.     local a = (pointA.x - pointB.x) ^ 2 + (pointA.y - pointB.y) ^ 2 + (pointA.z - pointB.z) ^ 2
  1123.     local c = (pointA.x - sphere.x) ^ 2 + (pointA.y - sphere.y) ^ 2 + (pointA.z - sphere.z) ^ 2 - sphere.r ^ 2
  1124.     local b = (pointB.x - sphere.x) ^ 2 + (pointB.y - sphere.y) ^ 2 + (pointB.z - sphere.z) ^ 2 - c - a - sphere.r ^ 2
  1125.  
  1126.     -- Make sure square root is not imaginary.
  1127.     local q = b ^ 2 - 4 * a * c
  1128.  
  1129.     if q > 0 then
  1130.        
  1131.         -- Quadratic Formula:
  1132.         local t1 = (-b + math.sqrt(q)) / (2 * a)
  1133.         local t2 = (-b - math.sqrt(q)) / (2 * a)
  1134.  
  1135.         local xt1 = pointA.x * (1 - t1) + t1 * pointB.x
  1136.         local yt1 = pointA.y * (1 - t1) + t1 * pointB.y
  1137.         local zt1 = pointA.z * (1 - t1) + t1 * pointB.z
  1138.  
  1139.         local xt2 = pointA.x * (1 - t2) + t2 * pointB.x
  1140.         local yt2 = pointA.y * (1 - t2) + t2 * pointB.y
  1141.         local zt2 = pointA.z * (1 - t2) + t2 * pointB.z
  1142.  
  1143.         -- Make sure the intersection is on this line.
  1144.         local ipoint1, ipoint2
  1145.         if t1 >= 0 and t1 <= 1 then
  1146.             table.insert(points, {x = xt1, y = yt1, z = zt1})
  1147.         end
  1148.  
  1149.         -- Make sure the intersection is on this line.
  1150.         if t2 >= 0 and t2 <= 1 then
  1151.             table.insert(points, {x = xt2, y = yt2, z = zt2})
  1152.         end
  1153.     end
  1154.  
  1155.     return points
  1156. end
  1157.  
  1158. function cylinderintersect(cylinder, pointA, pointB)
  1159.  
  1160.     -- Change x, y and z depending on orientation.
  1161.     local x, y, z
  1162.  
  1163.     if cylinder.o == "z" then
  1164.         x, y, z = "x", "y", "z"
  1165.     elseif cylinder.o == "x" then
  1166.         x, y, z = "z", "y", "x"
  1167.     elseif cylinder.o == "y" then
  1168.         x, y, z = "x", "z", "y"
  1169.     end
  1170.  
  1171.     local points = {}
  1172.  
  1173.     -- Determine if line intersects cylinder's edges.
  1174.     local a = (pointA[x] - pointB[x]) ^ 2 + (pointA[y] - pointB[y]) ^ 2
  1175.     local c = (pointA[x] - cylinder[x]) ^ 2 + (pointA[y] - cylinder[y]) ^ 2 - cylinder.r ^ 2
  1176.     local b = (pointB[x] - cylinder[x]) ^ 2 + (pointB[y] - cylinder[y]) ^ 2 - a - c - cylinder.r ^ 2
  1177.  
  1178.     local q = b ^ 2 - 4 * a * c
  1179.  
  1180.     if q > 0 then
  1181.  
  1182.         local t1 = (-b + math.sqrt(q)) / (2 * a)
  1183.         local t2 = (-b - math.sqrt(q)) / (2 * a)
  1184.  
  1185.         local xt1 = pointA[x] * (1 - t1) + t1 * pointB[x]
  1186.         local yt1 = pointA[y] * (1 - t1) + t1 * pointB[y]
  1187.         local zt1 = pointA[z] * (1 - t1) + t1 * pointB[z]
  1188.  
  1189.         local xt2 = pointA[x] * (1 - t2) + t2 * pointB[x]
  1190.         local yt2 = pointA[y] * (1 - t2) + t2 * pointB[y]
  1191.         local zt2 = pointA[z] * (1 - t2) + t2 * pointB[z]
  1192.  
  1193.         -- Make sure the intersection is on this line and within the cylinder's height.
  1194.         if t1 >= 0 and t1 <= 1 and zt1 >= cylinder.hlow and zt1 <= cylinder.hhigh then
  1195.             table.insert(points, {[x] = xt1, [y] = yt1, [z] = zt1})
  1196.         end
  1197.  
  1198.         -- Make sure the intersection is on this line and within the cylinder's height.
  1199.         if t2 >= 0 and t2 <= 1 and zt2 >= cylinder.hlow and zt2 <= cylinder.hhigh then
  1200.             table.insert(points, {[x] = xt2, [y] = yt2, [z] = zt2})
  1201.         end
  1202.  
  1203.         -- Check end caps
  1204.         -- If either point on the line is lower than or equal to the lowest point on the cylinder...
  1205.         if pointA[z] <= cylinder.hlow or pointB[z] <= cylinder.hlow then
  1206.             local tzlow = (cylinder.hlow - pointA[z]) / (pointA[z] + pointB[z])
  1207.  
  1208.             local xtlow = pointA[x] * (1 - tzlow) + tzlow * pointB[x]
  1209.             local ytlow = pointA[y] * (1 - tzlow) + tzlow * pointB[y]
  1210.  
  1211.             local dist = math.sqrt((cylinder[x] - xtlow) ^ 2 + (cylinder[y] - ytlow) ^ 2)
  1212.  
  1213.             if dist < cylinder.r then
  1214.                 table.insert(points, {[x] = xtlow, [y] = ytlow, [z] = cylinder.hlow})
  1215.             end
  1216.         end
  1217.  
  1218.         -- If either point on the line is higher than or equal to the lowest point on the cylinder...
  1219.         if pointA[z] >= cylinder.hhigh or pointB[z] >= cylinder.hhigh then
  1220.             local tzhigh = (cylinder.hhigh - pointA[z]) / (pointA[z] + pointB[z])
  1221.  
  1222.             local xthigh = pointA[x] * (1 - tzhigh) + tzhigh * pointB[x]
  1223.             local ythigh = pointA[y] * (1 - tzhigh) + tzhigh * pointB[y]
  1224.  
  1225.             local dist = math.sqrt((cylinder[x] - xthigh) ^ 2 + (cylinder[y] - ythigh) ^ 2)
  1226.  
  1227.             if dist < cylinder.r then
  1228.                 table.insert(points, {[x] = xthigh, [y] = ythigh, [z] = cylinder.hhigh})
  1229.             end
  1230.         end
  1231.     end
  1232.  
  1233.     return points
  1234. end
  1235.  
  1236. function rectprismintersect(rectprism, pointA, pointB)
  1237.  
  1238.     local points = {}
  1239.  
  1240.     -- Get the time "t" on the line where intersection with each coordinate's extreme occurs.
  1241.     local txlow = (rectprism.xlow - pointA.x) / (pointA.x + pointB.x)
  1242.     local txhigh = (rectprism.xhigh - pointA.x) / (pointA.x + pointB.x)
  1243.     local tylow = (rectprism.ylow - pointA.y) / (pointA.y + pointB.y)
  1244.     local tyhigh = (rectprism.yhigh - pointA.y) / (pointA.y + pointB.y)
  1245.     local tzlow = (rectprism.zlow - pointA.z) / (pointA.z + pointB.z)
  1246.     local tzhigh = (rectprism.zhigh - pointA.z) / (pointA.z + pointB.z)
  1247.  
  1248.     -- Find other coordinates given t.
  1249.     local xlowy = pointA.y * (1 - txlow) + txlow * pointB.y
  1250.     local xlowz = pointA.z * (1 - txlow) + txlow * pointB.z
  1251.  
  1252.     -- Ensure that this intersection is within the bounds of the rectangular prism.
  1253.     if xlowy <= rectprism.yhigh and xlowy >= rectprism.ylow and xlowz <= rectprism.zhigh and xlowz >= rectprism.zlow then
  1254.         table.insert(points, {x = rectprism.xlow, y = xlowy, z = xlowz})
  1255.     end
  1256.  
  1257.     -- Find other coordinates given t.
  1258.     local xhighy = pointA.y * (1 - txhigh) + txhigh * pointB.y
  1259.     local xhighz = pointA.z * (1 - txhigh) + txhigh * pointB.z
  1260.  
  1261.     -- Ensure that this intersection is within the bounds of the rectangular prism.
  1262.     if xhighy <= rectprism.yhigh and xhighy >= rectprism.ylow and xhighz <= rectprism.zhigh and xhighz >= rectprism.zlow then
  1263.         table.insert(points, {x = rectprism.xhigh, y = xhighy, z = xhighz})
  1264.     end
  1265.  
  1266.     -- Find other coordinates given t.
  1267.     local ylowx = pointA.x * (1 - tylow) + tylow * pointB.x
  1268.     local ylowz = pointA.z * (1 - tylow) + tylow * pointB.z
  1269.  
  1270.     -- Ensure that this intersection is within the bounds of the rectangular prism.
  1271.     if ylowx <= rectprism.xhigh and ylowx >= rectprism.xlow and ylowz <= rectprism.zhigh and ylowz >= rectprism.zlow then
  1272.         table.insert(points, {x = ylowx, y = rectprism.ylow, z = ylowz})
  1273.     end
  1274.  
  1275.     -- Find other coordinates given t.
  1276.     local yhighx = pointA.x * (1 - tyhigh) + tyhigh * pointB.x
  1277.     local yhighz = pointA.z * (1 - tyhigh) + tyhigh * pointB.z
  1278.  
  1279.     -- Ensure that this intersection is within the bounds of the rectangular prism.
  1280.     if yhighx <= rectprism.xhigh and yhighx >= rectprism.xlow and yhighz <= rectprism.zhigh and yhighz >= rectprism.zlow then
  1281.         table.insert(points, {x = yhighx, y = rectprism.yhigh, z = yhighz})
  1282.     end
  1283.  
  1284.     -- Find other coordinates given t.
  1285.     local zlowx = pointA.x * (1 - tzlow) + tzlow * pointB.x
  1286.     local zlowy = pointA.y * (1 - tzlow) + tzlow * pointB.y
  1287.  
  1288.     -- Ensure that this intersection is within the bounds of the rectangular prism.
  1289.     if zlowx <= rectprism.xhigh and zlowx >= rectprism.xlow and zlowy <= rectprism.yhigh and zlowy >= rectprism.ylow then
  1290.         table.insert(points, {x = zlowx, y = zlowy, z = rectprism.zlow})
  1291.     end
  1292.  
  1293.     -- Find other coordinates given t.
  1294.     local zhighx = pointA.x * (1 - tzhigh) + tzhigh * pointB.x
  1295.     local zhighy = pointA.y * (1 - tzhigh) + tzhigh * pointB.y
  1296.  
  1297.     -- Ensure that this intersection is within the bounds of the rectangular prism.
  1298.     if zhighx <= rectprism.xhigh and zhighx >= rectprism.xlow and zhighy <= rectprism.yhigh and zhighy >= rectprism.ylow then
  1299.         table.insert(points, {x = zhighx, y = zhighy, z = rectprism.zhigh})
  1300.     end
  1301.  
  1302.     -- Corner and edge check
  1303.     if #points > 2 then
  1304.         -- Remove entries from the table while we loop through it.
  1305.         local i = 0
  1306.         while i <= #points do
  1307.             -- There can be a total of three copies of the same coordinates (if the intersection happens at a corner).
  1308.             local rem1, rem2
  1309.             for k,v in ipairs(points) do
  1310.                 if points[i].x == v.x and points[i].y == v.y and points[i].z == v.z then
  1311.                     if rem1 then
  1312.                         rem2 = i - 1  -- Subtract 1 from the key since rem1 will be removed first and this key will be lowered by 1 when that happens.
  1313.                     else
  1314.                         rem1 = i
  1315.                     end
  1316.                 end
  1317.             end
  1318.  
  1319.             -- Remove the two keys.
  1320.             if rem1 or rem2 then
  1321.                 if rem1 then
  1322.                     table.remove(points, rem1)
  1323.                 elseif rem2 then
  1324.                     table.remove(points, rem2)
  1325.                 end
  1326.             else
  1327.                 i = i + 1  -- Increment i only if an entry has not been deleted.
  1328.             end
  1329.         end
  1330.     end
  1331.  
  1332.     return points
  1333. end
  1334.  
  1335. function circleintersect(circle, pointA, pointB)
  1336.  
  1337.     -- Change x, y and z depending on orientation.
  1338.     local x, y, z
  1339.  
  1340.     if circle.o == "z" then
  1341.         x, y, z = "x", "y", "z"
  1342.     elseif circle.o == "x" then
  1343.         x, y, z = "z", "y", "x"
  1344.     elseif circle.o == "y" then
  1345.         x, y, z = "x", "z", "y"
  1346.     end
  1347.  
  1348.     local points = {}
  1349.  
  1350.     -- Find the time "t" when the line intersects the plane at which the circle is oriented.
  1351.     local tz = (circle[z] - pointA[z]) / (pointA[z] + pointB[z])
  1352.     local xtz = pointA[x] * (1 - tz) + tz * pointB[x]
  1353.     local ytz = pointA[y] * (1 - tz) + tz * pointB[y]
  1354.  
  1355.     -- Find the distance from this intersection point to the center of the circle and compare it against the radius.
  1356.     local dist = math.sqrt((xtz - circle[x]) ^ 2 + (ytz - circle[y]) ^ 2)
  1357.     if dist <= circle.r then
  1358.         table.insert(points, {[x] = xtz, [y] = ytz, [z] = circle[z]})
  1359.     end
  1360.  
  1361.     return points
  1362. end
  1363.  
  1364. function rectangleintersect(rectangle, pointA, pointB)
  1365.  
  1366.     -- Change x, y and z depending on orientation.
  1367.     local x, y, z
  1368.  
  1369.     if rectangle.o == "z" then
  1370.         x, y, z = "x", "y", "z"
  1371.     elseif rectangle.o == "x" then
  1372.         x, y, z = "z", "y", "x"
  1373.     elseif rectangle.o == "y" then
  1374.         x, y, z = "x", "z", "y"
  1375.     end
  1376.  
  1377.     local points = {}
  1378.    
  1379.     -- Find the time "t" when the line intersects the plane at which the rectangle is oriented.
  1380.     local tz = (rectangle[z] - pointA[z]) / (pointA[z] + pointB[z])
  1381.     local xtz = pointA[x] * (1 - tz) + tz * pointB[x]
  1382.     local ytz = pointA[y] * (1 - tz) + tz * pointB[y]
  1383.  
  1384.     -- Find if the intersection point is within the bounds of the rectangle.
  1385.     if xtz >= rectangle[x .. "low"] and xtz <= rectangle[x .. "high"] and ytz >= rectangle[y .. "low"] and ytz <= rectangle[y .. "high"] then
  1386.         table.insert(points, {[x] = xtz, [y] = ytz, [z] = rectangle[z]})
  1387.     end
  1388.  
  1389.     return points
  1390. end
  1391.  
  1392. -- Object Creation Comments (refer to this function for questions on how the other ones work)
  1393. function GEO.newSphere(name, r, x, y, z)
  1394.  
  1395.     -- Check to see if there is already a GEO with this name.
  1396.     if not GEO[name] then
  1397.         -- Create new table
  1398.         GEO[name] = {}
  1399.         GEO[name].t = "sphere"  -- type
  1400.         GEO[name].n = name  -- name
  1401.         GEO[name].r = r or 1  -- radius (default value of 1)
  1402.         GEO[name].x = x or 0  -- x coordinate of center (default value of 0)
  1403.         GEO[name].y = y or 0  -- y coordinate of center (default value of 0)
  1404.         GEO[name].z = z or 0  -- z coordinate of center (default value of 0)
  1405.            
  1406.         -- Initialize monitor and contain tables
  1407.         GEO[name].m = {}
  1408.         GEO[name].c = {}
  1409.            
  1410.         -- Initialize list of antigeos
  1411.         GEO[name].anti = {}
  1412.            
  1413.         -- Notify the console that a sphere has been created.
  1414.         hprintf("Sphere \"" .. name .. "\" created.")
  1415.         setmetatable(GEO[name], GEO)  -- Add this object to the GEO metatable to allow GEO-editing functions to work on it.
  1416.         return GEO[name]  -- Return the object
  1417.     end
  1418.        
  1419.     -- If no object was returned, the name was invalid; notify the console.
  1420.     hprintf("Invalid name: \"" .. name .. "\"")
  1421. end
  1422.  
  1423. function GEO.newCircle(name, r, x, y, z, orientation)
  1424.  
  1425.     if not GEO[name] then
  1426.         GEO[name] = {}
  1427.         GEO[name].t = "circle"
  1428.         GEO[name].n = name
  1429.         GEO[name].o = orientation or "z"  -- orientation
  1430.         GEO[name].r = r or 0
  1431.         GEO[name].x = x or 0
  1432.         GEO[name].y = y or 0
  1433.         GEO[name].z = z or 0
  1434.            
  1435.         -- Initialize monitor and contain tables
  1436.         GEO[name].m = {}
  1437.         GEO[name].c = {}
  1438.            
  1439.         -- Initialize list of antigeos
  1440.         GEO[name].anti = {}
  1441.            
  1442.         hprintf("Circle \"" .. name .. "\" created.")
  1443.         setmetatable(GEO[name], GEO)
  1444.         return GEO[name]
  1445.     end
  1446.        
  1447.     hprintf("Invalid name: \"" .. name .. "\"")
  1448. end
  1449.  
  1450. function GEO.newCylinder(name, r, h, x, y, z, orientation)
  1451.  
  1452.     if not GEO[name] then
  1453.         GEO[name] = {}
  1454.         GEO[name].t = "cylinder"
  1455.         GEO[name].n = name
  1456.         x = x or 0
  1457.         y = y or 0
  1458.         z = z or 0
  1459.         r = r or 1
  1460.         h = h or 1
  1461.         orientation = orientation or "z"
  1462.         GEO[name].x = x
  1463.         GEO[name].y = y
  1464.         GEO[name].z = z
  1465.         GEO[name].r = r
  1466.         GEO[name].h = h  -- height
  1467.         GEO[name].o = orientation  -- orientation "x", "y", or "z"
  1468.         GEO[name].hlow = GEO[name][orientation] - h / 2  -- lowest orientation coordinate still within the cylinder
  1469.         GEO[name].hhigh = GEO[name][orientation] + h / 2  -- highest orientation coordinate still within the cylinder
  1470.            
  1471.         -- Initialize monitor and contain tables
  1472.         GEO[name].m = {}
  1473.         GEO[name].c = {}
  1474.            
  1475.         -- Initialize list of antigeos
  1476.         GEO[name].anti = {}
  1477.            
  1478.         hprintf("Cylinder \"" .. name .. "\" created.")
  1479.         setmetatable(GEO[name], GEO)
  1480.         return GEO[name]
  1481.     end
  1482. end
  1483.  
  1484. function GEO.newRectPrism(name, lx, ly, lz, x, y, z)
  1485.  
  1486.     if not GEO[name] then
  1487.         GEO[name] = {}
  1488.         GEO[name].t = "rectprism"
  1489.         GEO[name].n = name
  1490.         lx = lx or 1
  1491.         ly = ly or 1
  1492.         lz = lz or 1
  1493.         x = x or 0
  1494.         y = y or 0
  1495.         z = z or 0
  1496.         GEO[name].lx = lx  -- x length
  1497.         GEO[name].ly = ly  -- y length
  1498.         GEO[name].lz = lz  -- z length
  1499.         GEO[name].x = x
  1500.         GEO[name].y = y
  1501.         GEO[name].z = z
  1502.         GEO[name].xlow = x - lx / 2  -- lowest x-coordinate still within the rectangular prism
  1503.         GEO[name].xhigh = lx / 2 + x  -- highest x-coordinate still within in the rectangular prism
  1504.         GEO[name].ylow = y - ly / 2  -- lowest y-coordinate still within the rectangular prism
  1505.         GEO[name].yhigh = ly / 2 + y  -- highest y-coordinate still within the rectangular prism
  1506.         GEO[name].zlow = z - lz / 2  -- lowest z-coordinate still within the rectangular prism
  1507.         GEO[name].zhigh = lz / 2 + z  -- highest z-coordinate still within the rectangular prism
  1508.            
  1509.         -- Initialize monitor and contain tables
  1510.         GEO[name].m = {}
  1511.         GEO[name].c = {}
  1512.            
  1513.         -- Initialize list of antigeos
  1514.         GEO[name].anti = {}
  1515.            
  1516.         hprintf("Rectangular Prism \"" .. name .. "\" created.")
  1517.         setmetatable(GEO[name], GEO)
  1518.         return GEO[name]
  1519.     end
  1520.        
  1521.     hprintf("Invalid name: \"" .. name .. "\"")
  1522. end
  1523.  
  1524. function GEO.newRect(name, width, height, x, y, z, orientation)
  1525.  
  1526.     if not GEO[name] then
  1527.         GEO[name] = {}
  1528.         GEO[name].t = "rectangle"
  1529.         GEO[name].n = name
  1530.         width = width or 1
  1531.         height = height or 1
  1532.         x = x or 0
  1533.         y = y or 0
  1534.         z = z or 0
  1535.         orientation = orientation or "z"
  1536.         GEO[name].width = width
  1537.         GEO[name].height = height
  1538.         GEO[name].x = x
  1539.         GEO[name].y = y
  1540.         GEO[name].z = z
  1541.         GEO[name].o = orientation
  1542.            
  1543.         -- Coordinates' highs and lows depend on orientation
  1544.         if orientation == "z" then
  1545.             GEO[name].xlow = x - width / 2
  1546.             GEO[name].xhigh = x + width / 2
  1547.             GEO[name].ylow = y - height / 2
  1548.             GEO[name].yhigh = y + height / 2
  1549.             GEO[name].zlow = z
  1550.             GEO[name].zhigh = z
  1551.         elseif orientation == "x" then
  1552.             GEO[name].xlow = x
  1553.             GEO[name].xhigh = x
  1554.             GEO[name].ylow = y - width / 2
  1555.             GEO[name].yhigh = y + width / 2
  1556.             GEO[name].zlow = z - height / 2
  1557.             GEO[name].zhigh = z + height / 2
  1558.         elseif orientation == "y" then
  1559.             GEO[name].xlow = x - width / 2
  1560.             GEO[name].xhigh = x + width / 2
  1561.             GEO[name].ylow = y
  1562.             GEO[name].yhigh = y
  1563.             GEO[name].zlow = z - height / 2
  1564.             GEO[name].zhigh = z + height / 2
  1565.         end
  1566.            
  1567.         -- Initialize monitor and contain tables
  1568.         GEO[name].m = {}
  1569.         GEO[name].c = {}
  1570.            
  1571.         -- Initialize list of antigeos
  1572.         GEO[name].anti = {}
  1573.            
  1574.         hprintf("Rectangle \"" .. name .. "\" created.")
  1575.         setmetatable(GEO[name], GEO)
  1576.         return GEO[name]
  1577.     end
  1578.        
  1579.     hprintf("Invalid name: \""  .. name .. "\"")
  1580. end
  1581.  
  1582. function GEO.get(name)
  1583.  
  1584.     return GEO[name]
  1585. end
  1586.  
  1587. function GEO.clear()
  1588.  
  1589.     for k,v in pairs(GEO) do
  1590.         if type(v) == "table" and k ~= "__index" then  -- make sure we're actually getting a GEO object
  1591.             v:delete()  -- Delete every GEO
  1592.         end
  1593.     end
  1594. end
  1595.  
  1596. function GEO:delete()
  1597.  
  1598.     -- Get rid of perimeter and surface objects
  1599.     self:hide()
  1600.        
  1601.     -- Make sure any GEO that this is an AntiGEO of is aware that this GEO no longer exists
  1602.     for k,v in pairs(GEO) do
  1603.         if type(v) == "table" and k ~= "__index" then
  1604.             for name,bool in pairs(v.anti) do
  1605.                 if name == self.n then
  1606.                     GEO[v.n].anti[name] = nil
  1607.                     break
  1608.                 end
  1609.             end
  1610.         end
  1611.     end
  1612.        
  1613.     -- Nullify GEO
  1614.     GEO[self.n] = nil
  1615.     hprintf("Geo \"" .. self.n .. "\" deleted.")
  1616. end
  1617.  
  1618. function GEO:move(x, y, z)
  1619.  
  1620.     -- Move the center of the object
  1621.     -- Default to GEO's current coordinates
  1622.     GEO[self.n].x = x or GEO[self.n].x
  1623.     GEO[self.n].y = y or GEO[self.n].y
  1624.     GEO[self.n].z = z or GEO[self.n].z
  1625.        
  1626.     -- If this is a rectangular prism...
  1627.     if self.t == "rectprism" then
  1628.         -- Change the x, y, and z lows and highs accordingly to adjust to the new center
  1629.         GEO[self.n].xlow = x - GEO[self.n].lx / 2  
  1630.         GEO[self.n].xhigh = GEO[self.n].lx / 2 + x
  1631.         GEO[self.n].ylow = y - GEO[self.n].ly / 2
  1632.         GEO[self.n].yhigh = GEO[self.n].ly / 2 + y
  1633.         GEO[self.n].zlow = z - GEO[self.n].lz / 2
  1634.         GEO[self.n].zhigh = GEO[self.n].lz / 2 + z
  1635.        
  1636.     -- If this is a rectangle...
  1637.     elseif self.t == "rectangle" then
  1638.         -- Change the x, y, and z lows and highs accordingly to adjust to the new center (depends on orientation)
  1639.         if self.o == "z" then
  1640.             GEO[self.n].xlow = self.x - self.width / 2
  1641.             GEO[self.n].xhigh = self.x + self.width / 2
  1642.             GEO[self.n].ylow = self.y - self.height / 2
  1643.             GEO[self.n].yhigh = self.y + self.height / 2
  1644.             GEO[self.n].zlow = self.z
  1645.             GEO[self.n].zhigh = self.z
  1646.         elseif self.o == "x" then
  1647.             GEO[self.n].xlow = self.x
  1648.             GEO[self.n].xhigh = self.x
  1649.             GEO[self.n].ylow = self.y - self.width / 2
  1650.             GEO[self.n].yhigh = self.y + self.width / 2
  1651.             GEO[self.n].zlow = self.z - self.height / 2
  1652.             GEO[self.n].zhigh = self.z + self.height / 2
  1653.         elseif self.o == "y" then
  1654.             GEO[self.n].xlow = self.x - self.width / 2
  1655.             GEO[self.n].xhigh = self.x + self.width / 2
  1656.             GEO[self.n].ylow = self.y
  1657.             GEO[self.n].yhigh = self.y
  1658.             GEO[self.n].zlow = self.z - self.height / 2
  1659.             GEO[self.n].zhigh = self.z + self.height / 2
  1660.         end
  1661.        
  1662.     -- If this is a cylinder...
  1663.     elseif self.t == "cylinder" then
  1664.         GEO[self.n].hlow = self[self.o] - self.h / 2
  1665.         GEO[self.n].hhigh = self[self.o] + self.h / 2
  1666.     end
  1667. end
  1668.  
  1669. function GEO:radius(new)
  1670.  
  1671.     if self.t == "sphere" or self.t == "circle" or self.t == "cylinder" then
  1672.         if new then  -- If "new" is defined...
  1673.             GEO[self.n].r = new  -- Change the radius to its value.
  1674.         else  -- If not...
  1675.             return GEO[self.n].r  -- Return its current radius.
  1676.         end
  1677.     end
  1678. end
  1679.  
  1680. function GEO:size(x, y, z)
  1681.  
  1682.     -- If this is a rectangular prism...
  1683.     if self.t == "rectprism" then
  1684.         if x or y or z then  -- If any of these variables have been defined...
  1685.             -- Adjust lengths and x, y, and z highs and lows accordingly.
  1686.             GEO[self.n].lx = x or GEO[self.n].lx
  1687.             GEO[self.n].ly = y or GEO[self.n].ly
  1688.             GEO[self.n].lz = z or GEO[self.n].lz
  1689.             GEO[self.n].xlow = GEO[self.n].x - x / 2
  1690.             GEO[self.n].xhigh = x / 2 + GEO[self.n].x
  1691.             GEO[self.n].ylow = GEO[self.n].y - y / 2
  1692.             GEO[self.n].yhigh = y / 2 + GEO[self.n].y
  1693.             GEO[self.n].zlow = GEO[self.n].z - z / 2
  1694.             GEO[self.n].zhigh = z / 2 + GEO[self.n].z
  1695.         else  -- Otherwise...
  1696.             return GEO[self.n].lx, GEO[self.n].ly, GEO[self.n].lz  -- Return the x, y, and z lengths.
  1697.         end
  1698.        
  1699.     -- If this is a rectangle...
  1700.     elseif self.t == "rectangle" then
  1701.         if x or y or z then  -- If any of these variables are defined...
  1702.             -- Adjust width, height, and x, y, and z highs and lows accordingly (depends on orientation).
  1703.             if self.o == "z" then
  1704.                 GEO[self.n].width = x
  1705.                 GEO[self.n].height = y
  1706.                 GEO[self.n].xlow = self.x - self.width / 2
  1707.                 GEO[self.n].xhigh = self.x + self.width / 2
  1708.                 GEO[self.n].ylow = self.y - self.height / 2
  1709.                 GEO[self.n].yhigh = self.y + self.height / 2
  1710.                 GEO[self.n].zlow = self.z
  1711.                 GEO[self.n].zhigh = self.z
  1712.             elseif self.o == "x" then
  1713.                 GEO[self.n].width = y
  1714.                 GEO[self.n].height = z
  1715.                 GEO[self.n].xlow = self.x
  1716.                 GEO[self.n].xhigh = self.x
  1717.                 GEO[self.n].ylow = self.y - self.width / 2
  1718.                 GEO[self.n].yhigh = self.y + self.width / 2
  1719.                 GEO[self.n].zlow = self.z - self.height / 2
  1720.                 GEO[self.n].zhigh = self.z + self.height / 2
  1721.             elseif self.o == "y" then
  1722.                 GEO[self.n].width = x
  1723.                 GEO[self.n].height = z
  1724.                 GEO[self.n].xlow = self.x - self.width / 2
  1725.                 GEO[self.n].xhigh = self.x + self.width / 2
  1726.                 GEO[self.n].ylow = self.y
  1727.                 GEO[self.n].yhigh = self.y
  1728.                 GEO[self.n].zlow = self.z - self.height / 2
  1729.                 GEO[self.n].zhigh = self.z + self.height / 2
  1730.             end
  1731.         else  -- Otherwise...
  1732.             return GEO[self.n].width, GEO[self.n].height  -- Return the width and height of the rectangle.
  1733.         end
  1734.        
  1735.     -- If this is a cylinder...
  1736.     elseif self.t == "cylinder" then
  1737.         local h = x or y or z  -- Whichever variable is defined, it is taken as the height.
  1738.         if h then  -- If a height is specified...
  1739.             -- Adjust height and z high and low accordingly.
  1740.             GEO[self.n].h = h
  1741.             GEO[self.n].hlow = self[self.o] - h / 2
  1742.             GEO[self.n].hhigh = self[self.o] + h / 2
  1743.         else  -- Otherwise...
  1744.             return GEO[self.n].h  -- Return the height.
  1745.         end
  1746.     end
  1747. end
  1748.  
  1749. function GEO:extend(orientation, direction, amount)
  1750.  
  1751.     -- Change the direction from "+" or "-" to "high" or "low".
  1752.     local dir
  1753.     dir = string.gsub(direction, "-", "low")
  1754.     dir = string.gsub(direction, "+", "high")
  1755.        
  1756.     -- Get the face we're trying to extend (i.e. "xhigh")
  1757.     local face = string.lower(orientation) .. direction
  1758.        
  1759.     -- If this is a rectangular prism or a rectangle...
  1760.     if self.t == "rectprism" or self.t == "rectangle" then
  1761.         -- Make sure "face" is actually a valid face (and not something like "cheesederp")
  1762.         if self[face] then
  1763.             -- Use "GEO[self.n]" when you want to actually permanently change the value of something within the object; use "self" for reading information from the object.
  1764.             -- Change the length of the GEO in the orientation specified.
  1765.             GEO[self.n]["l" .. string.lower(orientation)] = self["l" .. string.lower(orientation)] + amount
  1766.                
  1767.             -- Figure out if the positive or negative face is being extended.
  1768.             if direction == "+" then
  1769.                 GEO[self.n][face] = self[face] + amount
  1770.                 GEO[self.n][string.lower(orientation)] = self[string.lower(orientation)] + amount / 2
  1771.             else
  1772.                 GEO[self.n][face] = self[face] - amount
  1773.                 GEO[self.n][string.lower(orientation)] = self[string.lower(orientation)] - amount / 2
  1774.             end
  1775.         end
  1776.        
  1777.     -- If this is a cylinder...
  1778.     elseif self.t == "cylinder" then
  1779.         -- The orientation must be the orientation of the cylinder
  1780.         if orientation == self.o then
  1781.             if self[face] then
  1782.                 GEO[self.n].h = self.h + amount
  1783.                    
  1784.                 -- Figure out if the top or bottom face is being extended.
  1785.                 if direction == "+" then
  1786.                     GEO[self.n].hhigh = self.hhigh + amount
  1787.                     GEO[self.n][self.o] = self[self.o] + amount / 2
  1788.                 else
  1789.                     GEO[self.n].hhigh = self.hhigh - amount
  1790.                     GEO[self.n][self.o] = self[self.o] - amount / 2
  1791.                 end
  1792.             end
  1793.         end
  1794.     end
  1795. end
  1796.  
  1797. function GEO:antigeo(boolean, ...)
  1798.  
  1799.     GEO[self.n].geolist = GEO[self.n].geolist or {}
  1800.  
  1801.     -- Get all GEOs from args.
  1802.     local geos = {...}
  1803.        
  1804.     if boolean == true then
  1805.         for _,geo in ipairs(geos) do
  1806.             local name = geo:name()
  1807.             -- Insert AntiGEO into the specified GEOs' AntiGEO table.
  1808.             GEO[name].anti[self.n] = true
  1809.             -- Insert GEO into list of AntiGEO's GEOs.
  1810.             GEO[self.n].geolist[name] = true
  1811.         end
  1812.     elseif boolean == false then
  1813.         for _,geo in ipairs(geos) do
  1814.             local name = geo:name()
  1815.             -- Remove AntiGEO from table.
  1816.             GEO[name].anti[self.n] = false
  1817.             -- Remove GEO from AntiGEO table.
  1818.             GEO[self.n].geolist[name] = false
  1819.         end
  1820.     elseif boolean == nil then
  1821.         if GEO[self.n].a == true then
  1822.             return true
  1823.         else
  1824.             return false
  1825.         end
  1826.     end
  1827.        
  1828.     local count = 0
  1829.     for k,v in pairs(GEO[self.n].geolist) do
  1830.         count = count + 1
  1831.     end
  1832.        
  1833.     if count > 0 then
  1834.         GEO[self.n].a = true
  1835.     else
  1836.         GEO[self.n].a = false
  1837.     end
  1838. end
  1839.  
  1840. function GEO:coords()
  1841.  
  1842.     return self.x, self.y, self.z
  1843. end
  1844.  
  1845. function GEO:high(orientation)
  1846.  
  1847.     if self.t == "sphere" or self.t == "circle" then
  1848.         return self[orientation] + self.r
  1849.     elseif self.t == "rectprism" or self.t == "rectangle" then
  1850.         return self[orientation .. "high"]
  1851.     elseif self.t == "cylinder" then
  1852.         if orientation == self.o then
  1853.             return self.hhigh
  1854.         else
  1855.             return self[orientation] + self.r
  1856.         end
  1857.     end        
  1858. end
  1859.  
  1860. function GEO:low(orientation)
  1861.  
  1862.     if self.t == "sphere" or self.t == "circle" then
  1863.         return self[orientation] - self.r
  1864.     elseif self.t == "rectprism" or self.t == "rectangle" then
  1865.         return self[orientation .. "low"]
  1866.     elseif self.t == "cylinder" then
  1867.         if orientation == self.o then
  1868.             return self.hlow
  1869.         else
  1870.             return self[orientation] - self.r
  1871.         end
  1872.     end        
  1873. end
  1874.  
  1875. function GEO:randomcoords()
  1876.  
  1877.     if self.t == "sphere" then
  1878.         return randomInSphere(self.x, self.y, self.z, self.r)
  1879.     elseif self.t == "circle" then
  1880.         return randomInCircle(self.x, self.y, self.z, self.r, self.o)
  1881.     elseif self.t == "cylinder" then
  1882.         return randomInCylinder(self.x, self.y, self.z, self.hlow, self.hhigh, self.r, self.o)
  1883.     elseif self.t == "rectprism" or self.t == "rectangle" then
  1884.         return randomInRectPrism(self.xlow, self.xhigh, self.ylow, self.yhigh, self.zlow, self.zhigh)
  1885.     end
  1886. end
  1887.  
  1888. function GEO:type()
  1889.  
  1890.     return self.t
  1891. end
  1892.  
  1893. function GEO:orientation()
  1894.  
  1895.     return self.o
  1896. end
  1897.  
  1898. function GEO:name()
  1899.  
  1900.     return self.n
  1901. end
  1902.  
  1903. function GEO:perimeter(density, mapId)
  1904.        
  1905.     -- Default density to 10
  1906.     density = density or 10
  1907.        
  1908.     -- Default tagtype and tagname to Full-Spectrum Visions
  1909.     mapId = mapId or gettagid("eqip", "powerups\\full-spectrum vision")
  1910.        
  1911.     tagname, tagtype = gettaginfo(mapId)
  1912.        
  1913.     -- Store all of the perimeter objects in a table
  1914.     GEO[self.n].p = GEO[self.n].p or {}
  1915.        
  1916.     -- Find the change in angle per point from 0 - 2pi (0° - 360°)
  1917.     local angle_increment = 2 * math.pi / density
  1918.     if self.t == "sphere" then
  1919.         for i = 1,density do
  1920.             -- Use trigonometry to find the outer edge of the circle (for a sphere, this will be at the z-center -- the widest part of the sphere).
  1921.             local x = self.r * math.cos(angle_increment * i)
  1922.             local y = self.r * math.sin(angle_increment * i)
  1923.             local z = self.z
  1924.             local objId = createobject(mapId, 0, nil, false, self.x + x, self.y + y, self.z)
  1925.             GEO[self.n].p[objId] = {self.x + x, self.y + y, self.z}
  1926.         end
  1927.     elseif self.t == "circle" or self.t == "cylinder" then
  1928.         if self.o == "z" then
  1929.             for i = 1,density do
  1930.                 -- Use trigonometry to find the outer edge of the circle.
  1931.                 local x = self.r * math.cos(angle_increment * i)
  1932.                 local y = self.r * math.sin(angle_increment * i)
  1933.                 local z = self.z
  1934.                 local objId = createobject(mapId, 0, nil, false, self.x + x, self.y + y, self.z)
  1935.                 GEO[self.n].p[objId] = {self.x + x, self.y + y, self.z}
  1936.             end
  1937.         elseif self.o == "x" then
  1938.             for i = 1,density do
  1939.                 -- Use trigonometry to find the outer edge of the circle.
  1940.                 local x = self.x
  1941.                 local y = self.r * math.cos(angle_increment * i)
  1942.                 local z = self.r * math.sin(angle_increment * i)
  1943.                 local objId = createobject(mapId, 0, nil, false, self.x, self.y + y, self.z + z)
  1944.                 GEO[self.n].p[objId] = {self.x, self.y + y, self.z}
  1945.             end
  1946.         elseif self.o == "y" then
  1947.             for i = 1,density do
  1948.                 -- Use trigonometry to find the outer edge of the circle.
  1949.                 local x = self.r * math.cos(angle_increment * i)
  1950.                 local y = self.y
  1951.                 local z = self.r * math.sin(angle_increment * i)
  1952.                 hprintf("x: " .. (self.x + x) .. " y: " .. (self.y) .. " z: " .. (self.z + z))
  1953.                 local objId = createobject(mapId, 0, nil, false, self.x + x, self.y, self.z + z)
  1954.                 GEO[self.n].p[objId] = {self.x + x, self.y, self.z + z}
  1955.             end
  1956.         end
  1957.     elseif self.t == "rectprism" or self.t == "rectangle" then
  1958.         if self.t == "rectangle" then
  1959.             if self.o ~= "z" then return end
  1960.         end
  1961.         -- Create points at four corners of the rectangle
  1962.         local o1 = createobject(mapId, 0, nil, false, self.xhigh, self.yhigh, self.z)
  1963.         local o2 = createobject(mapId, 0, nil, false, self.xhigh, self.ylow, self.z)
  1964.         local o3 = createobject(mapId, 0, nil, false, self.xlow, self.yhigh, self.z)
  1965.         local o4 = createobject(mapId, 0, nil, false, self.xlow, self.ylow, self.z)
  1966.         GEO[self.n].p[o1] = {self.xhigh, self.yhigh, self.z}
  1967.         GEO[self.n].p[o2] = {self.xhigh, self.yhigh, self.z}
  1968.         GEO[self.n].p[o3] = {self.xhigh, self.yhigh, self.z}
  1969.         GEO[self.n].p[o4] = {self.xhigh, self.yhigh, self.z}
  1970.            
  1971.         for i = 1,density do
  1972.             local herp = createobject(mapId, 0, nil, false, self.xhigh - (i * self.lx / density), self.yhigh, self.z)
  1973.             local derp = createobject(mapId, 0, nil, false, self.xhigh, self.yhigh - (i * self.ly / density), self.z)
  1974.             local weee = createobject(mapId, 0, nil, false, self.xhigh - (i * self.lx / density), self.ylow, self.z)
  1975.             local cheese = createobject(mapId, 0, nil, false, self.xlow, self.ylow + (i * self.ly / density), self.z)
  1976.             GEO[self.n].p[herp] = {self.xhigh - (i * self.lx / density), self.yhigh, self.z}
  1977.             GEO[self.n].p[derp] = {self.xhigh, self.yhigh - (i * self.ly / density), self.z}
  1978.             GEO[self.n].p[weee] = {self.xhigh - (i * self.lx / density), self.ylow, self.z}
  1979.             GEO[self.n].p[cheese] = {self.xlow, self.ylow + (i * self.ly / density), self.z}
  1980.         end
  1981.     end
  1982.        
  1983.     for objId,_ in pairs(GEO[self.n].p) do
  1984.         writebit(getobject(objId) + 0x10, 5, true)  -- Ignore pyhsics
  1985.     end
  1986.        
  1987.     -- Also show the surface or perimeter of AntiGEOs with Overshield powerups
  1988.     for name,bool in pairs(GEO[self.n].anti) do
  1989.         if bool then
  1990.             local geo = GEO.get(name)
  1991.             if geo:type() == "sphere" or geo:type() == "cylinder" or geo:type() == "rectprism" then
  1992.                 geo:surface(density, gettagid("eqip", "powerups\\over shield"))
  1993.             else
  1994.                 geo:perimeter(density, gettagid("eqip", "powerups\\over shield"))
  1995.             end
  1996.         end
  1997.     end
  1998. end
  1999.  
  2000. function GEO:surface(density, mapId)
  2001.  
  2002.     -- Default density to 12
  2003.     density = density or 12
  2004.    
  2005.     -- Default tagtype and tagname to Full-Spectrum Visions
  2006.     mapId = mapId or gettagid("eqip", "powerups\\full-spectrum vision")
  2007.  
  2008.     -- Store all perimeter objects in a table
  2009.     GEO[self.n].p = GEO[self.n].p or {}
  2010.    
  2011.     -- Store all of the objects we will create
  2012.     local objects = {}
  2013.    
  2014.     if self.t == "sphere" then
  2015.        
  2016.         -- Use concentric rings based on the density given to simulate a sphere
  2017.         local laterals = density
  2018.         local latdist = self.r * 2 / laterals
  2019.         local curz = self.z + self.r
  2020.         local r = 0
  2021.         for i = 1, laterals do
  2022.             if i <= math.floor(laterals / 2) then
  2023.                 local d = i / math.floor(laterals / 2) * density
  2024.                 -- Find the change in angle per point from 0 - 2pi (0° - 360°)
  2025.                 local angle_increment = 2 * math.pi / density
  2026.                 for j = 1, density do
  2027.                     -- Use trigonometry to find the outer edge of the circle (for a sphere, this will be at the z-center -- the widest part of the sphere).
  2028.                     local x = r * math.cos((angle_increment) * j)
  2029.                     local y = r * math.sin((angle_increment) * j)
  2030.                     local z = curz
  2031.                     table.insert(objects, {self.x + x, self.y + y, z})
  2032.                 end
  2033.                
  2034.             elseif i >= math.ceil(laterals / 2) then
  2035.                 local d = math.floor(laterals / 2) / i * density
  2036.                 -- Find the change in angle per point from 0 - 2pi (0° - 360°)
  2037.                 local angle_increment = 2 * math.pi / density      
  2038.                 for j = 1, density do
  2039.                     -- Use trigonometry to find the outer edge of the circle (for a sphere, this will be at the z-center -- the widest part of the sphere).
  2040.                     local x = r * math.cos((angle_increment) * j)
  2041.                     local y = r * math.sin((angle_increment) * j)
  2042.                     local z = curz
  2043.                     table.insert(objects, {self.x + x, self.y + y, z})
  2044.                 end
  2045.             end
  2046.            
  2047.             -- Lower the lateral
  2048.             curz = curz - latdist
  2049.             local diff = curz - self.z
  2050.             local angle = math.acos(math.abs(diff) / self.r)
  2051.            
  2052.             -- Get the new radius at this sphere cap
  2053.             r = math.sin(angle) * self.r
  2054.         end
  2055.        
  2056.     elseif self.t == "cylinder" then
  2057.    
  2058.         -- Use multiple rings based on density given to simulate cylinder
  2059.         local laterals = math.ceil(density / 2)
  2060.         local latdist = self.h / laterals
  2061.         local curh = self[self.o] + (self.h / 2)
  2062.        
  2063.         for i = 0, laterals do
  2064.             if i == 0 or i == laterals then
  2065.                 local rings = density / 2
  2066.                 local inc = self.r / rings
  2067.                 local radius = self.r - inc
  2068.                 for x = 1, rings - 1 do
  2069.                     for j = 1, density do
  2070.                         if self.o == "z" then
  2071.                             local angle_increment = 2 * math.pi / density
  2072.                             local x = radius * math.cos(angle_increment * j)
  2073.                             local y = radius * math.sin(angle_increment * j)
  2074.                             table.insert(objects, {self.x + x, self.y + y, curh})
  2075.                         elseif self.o == "x" then
  2076.                             local y = radius * math.cos(angle_increment * j)
  2077.                             local z = radius * math.sin(angle_increment * j)
  2078.                             table.insert(objects, {curh, self.y + y, self.z + z})
  2079.                         elseif self.o == "y" then
  2080.                             local x = radius * math.cos(angle_increment * j)
  2081.                             local z = radius * math.sin(angle_increment * j)
  2082.                             table.insert(objects, {self.x + x, curh, self.z + z})
  2083.                         end
  2084.                     end
  2085.                    
  2086.                     radius = radius - inc
  2087.                 end
  2088.             end
  2089.            
  2090.             for j = 1, density do
  2091.                 -- Find the change in angle per point from 0 - 2pi (0° - 360°)
  2092.                 local angle_increment = 2 * math.pi / density
  2093.                 -- Use trigonometry to find the outer edge of the circle.
  2094.                 local x, y, z
  2095.                 if self.o == "z" then
  2096.                     x = self.r * math.cos(angle_increment * j)
  2097.                     y = self.r * math.sin(angle_increment * j)
  2098.                     table.insert(objects, {self.x + x, self.y + y, curh})
  2099.                 elseif self.o == "x" then
  2100.                     y = self.r * math.cos(angle_increment * j)
  2101.                     z = self.r * math.sin(angle_increment * j)
  2102.                     table.insert(objects, {curh, self.y + y, self.z + z})
  2103.                 elseif self.o == "y" then
  2104.                     x = self.r * math.cos(angle_increment * j)
  2105.                     z = self.r * math.sin(angle_increment * j)
  2106.                     table.insert(objects, {self.x + x, curh, self.z + z})
  2107.                 end
  2108.             end
  2109.            
  2110.             -- Lower the lateral
  2111.             curh = curh - latdist
  2112.         end
  2113.    
  2114.     elseif self.t == "rectprism" then
  2115.    
  2116.         local d = density / 2
  2117.         local x_inc = self.lx / d
  2118.         local y_inc = self.ly / d
  2119.         local z_inc = self.lz / d
  2120.        
  2121.         for z = 0, d do
  2122.             if z == 0 or z == d then
  2123.                 for y = 0, d do
  2124.                     for x = 0, d do
  2125.                         local x_coord = self.xlow + (x * x_inc)
  2126.                         local y_coord = self.ylow + (y * y_inc)
  2127.                         local z_coord = self.zlow + (z * z_inc)
  2128.                         table.insert(objects, {x_coord, y_coord, z_coord})
  2129.                     end
  2130.                 end
  2131.             else           
  2132.                 for y = 0, d do
  2133.                     if y == 0 or y == d then
  2134.                         for x = 0, d do
  2135.                             local x_coord = self.xlow + (x * x_inc)
  2136.                             local y_coord = self.ylow + (y * y_inc)
  2137.                             local z_coord = self.zlow + (z * z_inc)
  2138.                             table.insert(objects, {x_coord, y_coord, z_coord})
  2139.                         end
  2140.                     else
  2141.                         local y_coord = self.ylow + (y * y_inc)
  2142.                         local z_coord = self.zlow + (z * z_inc)
  2143.                         table.insert(objects, {self.xlow, y_coord, z_coord})
  2144.                         table.insert(objects, {self.xhigh, y_coord, z_coord})
  2145.                     end
  2146.                 end
  2147.             end
  2148.         end
  2149.     end
  2150.    
  2151.     local antiobjs = {}
  2152.     -- Also show the surface or perimeter of AntiGEOs with Overshield powerups
  2153.     for name,bool in pairs(GEO[self.n].anti) do
  2154.         if bool then
  2155.             local geo = GEO.get(name)
  2156.             if geo:type() == "sphere" or geo:type() == "cylinder" or geo:type() == "rectprism" then
  2157.                 local a = geo:surface(density / 2, gettagid("eqip", "powerups\\over shield"))
  2158.                 for k,v in ipairs(a) do
  2159.                     table.insert(antiobjs, v)
  2160.                 end
  2161.             else
  2162.                 geo:perimeter(density / 2, gettagid("eqip", "powerups\\over shield"))
  2163.             end
  2164.         end
  2165.     end
  2166.    
  2167.     local objs = {}
  2168.    
  2169.     for k,v in ipairs(objects) do
  2170.         local objId = createobject(mapId, 0, nil, false, v[1], v[2], v[3])
  2171.         GEO[self.n].p[objId] = {v[1], v[2], v[3]}
  2172.         writebit(getobject(objId) + 0x10, 5, true)  -- Ignore pyhsics
  2173.         table.insert(objs, objId)
  2174.     end
  2175.    
  2176.     return objs, antiobjs
  2177. end
  2178.  
  2179. function GEO:hide()
  2180.  
  2181.     if self.p then
  2182.         for k,v in pairs(GEO[self.n].p) do
  2183.             if getobject(k) then
  2184.                 destroyobject(k)
  2185.                 GEO[self.n].p[k] = nil
  2186.             end
  2187.         end
  2188.     end
  2189. end
  2190.  
  2191. function GEO:contains(objId)
  2192.  
  2193.     for k,v in pairs(GEO[self.n].anti) do
  2194.         if GEO[k]:contains(objId) then
  2195.             return false
  2196.         end
  2197.     end
  2198.  
  2199.     if self.t == "sphere" then
  2200.         return inSphere(objId, self.x, self.y, self.z, self.r)
  2201.     elseif self.t == "rectprism" or self.t == "rectangle" then
  2202.         return inRectPrism(objId, self.xlow, self.xhigh, self.ylow, self.yhigh, self.zlow, self.zhigh)
  2203.     elseif self.t == "circle" then
  2204.         return inCircle(objId, self.x, self.y, self.z, self.r, self.o)
  2205.     elseif self.t == "cylinder" then
  2206.         return inCylinder(objId, self.x, self.y, self.z, self.hlow, self.hhigh, self.r, self.o)
  2207.     end
  2208. end
  2209.  
  2210. function GEO:containspoint(point)
  2211.  
  2212.     local bool
  2213.     if GEO[self.n]:vectorintersect(point, point) then
  2214.         bool = true
  2215.     else
  2216.         bool = false
  2217.     end
  2218.    
  2219.     for k,v in pairs(GEO[self.n].anti) do
  2220.         if GEO[k]:containspoint(point) then
  2221.             bool = false
  2222.         end
  2223.     end
  2224.    
  2225.     return bool
  2226. end
  2227.  
  2228. function GEO:vectorintersect(pointA, pointB)
  2229.  
  2230.     local tempA = {x = pointA[1], y = pointA[2], z = pointA[3]}
  2231.     local tempB = {x = pointB[1], y = pointB[2], z = pointB[3]}
  2232.    
  2233.     pointA, pointB = tempA, tempB
  2234.  
  2235.     local points
  2236.     -- Call the appropriate function depending on the type of GEO.
  2237.     if self.t == "sphere" then
  2238.         points = sphereintersect(self, pointA, pointB)
  2239.     elseif self.t == "cylinder" then
  2240.         points = cylinderintersect(self, pointA, pointB)
  2241.     elseif self.t == "rectprism" then
  2242.         points = rectprismintersect(self, pointA, pointB)
  2243.     elseif self.t == "circle" then
  2244.         points = circleintersect(self, pointA, pointB)
  2245.     elseif self.t == "rect" then
  2246.         points = rectangleintersect(self, pointA, pointB)
  2247.     end
  2248.    
  2249.     -- Return points as 2 variables where point = {x, y, z}.
  2250.     local point1, point2
  2251.     if points[1] then
  2252.         point1 = {points[1].x, points[1].y, points[1].z}
  2253.     end
  2254.    
  2255.     if points[2] then
  2256.         point2 = {points[2].x, points[2].y, points[2].z}
  2257.     end
  2258.    
  2259.     -- Check if the points of intersection are within AntiGEOs.
  2260.     for k,v in pairs(GEO[self.n].anti) do
  2261.         if GEO[k]:containspoint(point1) then
  2262.             point1 = nil
  2263.             point2 = point1
  2264.         end
  2265.        
  2266.         if GEO[k]:containspoint(point2) then
  2267.             point2 = nil
  2268.         end
  2269.     end
  2270.    
  2271.     return point1, point2
  2272. end
  2273.  
  2274. function GEO:randcoord()
  2275.  
  2276.     if self.t == "sphere" then
  2277.         return randomInSphere(self.x, self.y, self.z, self.r)
  2278.     elseif self.t == "circle" then
  2279.         return randomInCircle(self.x, self.y, self.z, self.r, self.o)
  2280.     elseif self.t == "cylinder" then
  2281.         return randomInCylinder(self.x, self.y, self.z, self.hlow, self.hhigh, self.r, self.o)
  2282.     elseif self.t == "rectprism" or self.t == "rect" then
  2283.         return randomInRectPrism(self.xlow, self.xhigh, self.ylow, self.yhigh, self.zlow, self.zhigh)
  2284.     end
  2285. end
  2286.  
  2287. function GEO:follow(objId)
  2288.  
  2289.     -- If the objId exists...
  2290.     if getobject(objId) then
  2291.         GEO[self.n].f = objId  -- Save it (the GEOTimer will access it)
  2292.         -- Check to see if the object is a player
  2293.         if objectidtoplayer(objId) then
  2294.             GEO[self.n].player = true
  2295.         end
  2296.     end
  2297. end
  2298.  
  2299. function GEO:unfollow()
  2300.  
  2301.     -- Nullify the saved objId from GEO:follow()
  2302.     GEO[self.n].f = nil
  2303. end
  2304.  
  2305. function GEO.followedBy(objId)
  2306.  
  2307.     -- Initialize table
  2308.     local geos = {}
  2309.        
  2310.     -- Loop through the GEO table
  2311.     for k,v in pairs(GEO) do
  2312.         if type(v) == "table" and k ~= "__index" then  -- make sure we're actually getting a GEO object
  2313.             if v.f == objId then
  2314.                 -- If this GEO has this objId saved, insert it into the geos table.
  2315.                 table.insert(geos, v)
  2316.             end
  2317.         end
  2318.     end
  2319.        
  2320.     -- Return the GEOs following objId in a table.
  2321.     return geos
  2322. end
  2323.  
  2324. function GEO.cleanup(player)
  2325.  
  2326.     local m_player = getplayer(player)
  2327.     local objId = readdword(m_player, 0x34)
  2328.     local geos = GEO.followedBy(objId)
  2329.     for k,v in ipairs(geos) do
  2330.         v:delete()
  2331.     end
  2332. end
  2333.  
  2334. function GEO:velocity(x, y, z)
  2335.  
  2336.     GEO[self.n].vx = x or GEO[self.n].vx
  2337.     GEO[self.n].vy = y or GEO[self.n].vy
  2338.     GEO[self.n].vz = z or GEO[self.n].vz
  2339. end
  2340.  
  2341. function GEO:killzone(bool, delay, kill_message, func, ...)
  2342.  
  2343.     if bool == true then
  2344.         GEO[self.n].kz = true
  2345.     elseif bool == false then
  2346.         GEO[self.n].kz = false
  2347.     elseif bool == nil then
  2348.         return GEO[self.n].kz or false
  2349.     end
  2350.    
  2351.     GEO[self.n].kzdelay = delay
  2352.     GEO[self.n].kzmessage = kill_message
  2353.     GEO[self.n].kztime = {}
  2354.     GEO[self.n].kzfunc = func
  2355.     GEO[self.n].kzargs = {...}
  2356. end
  2357.  
  2358. function GEO:damagezone(bool, damage, delay, damage_message, func, ...)
  2359.  
  2360.     if bool == true then
  2361.         GEO[self.n].damage = damage or 1  -- Amount of damage applied per second.
  2362.     elseif bool == false then
  2363.         GEO[self.n].damage = nil
  2364.     elseif bool == nil then  -- If nothing is passed, return true if this GEO is a damagezone, false if not.
  2365.         if GEO[self.n].damage then
  2366.             return true
  2367.         else
  2368.             return false
  2369.         end
  2370.     end
  2371.    
  2372.     GEO[self.n].dmgdelay = delay
  2373.     GEO[self.n].dmgmessage = damage_message
  2374.     GEO[self.n].dmgmsgsent = {}
  2375.     GEO[self.n].dmgtime = {}
  2376.     GEO[self.n].dmgfunc = func
  2377.     GEO[self.n].dmgargs = {...}
  2378. end
  2379.  
  2380. function GEO:face(orientation, direction)
  2381.  
  2382.     -- If this is a rectangular prism...
  2383.     if self.t == "rectprism" then
  2384.         orientation = orientation or "z"
  2385.         direction = direction or "+"
  2386.         if orientation == "z" then
  2387.             local width = self.lx
  2388.             local height = self.ly
  2389.             local highlow
  2390.             if direction == "+" then
  2391.                 highlow = self.zhigh
  2392.             else
  2393.                 highlow = self.zlow
  2394.             end
  2395.                
  2396.             -- Create a new rectangle which overlays the specified face and return that rectangle.
  2397.             return GEO.newRect(self.n .. "ZFace" .. os.time(), width, height, self.x, self.y, highlow, orientation)
  2398.                
  2399.         elseif orientation == "x" then
  2400.             local width = self.ly
  2401.             local height = self.lz
  2402.             local highlow
  2403.             if direction == "+" then
  2404.                 highlow = self.xhigh
  2405.             else
  2406.                 highlow = self.xlow
  2407.             end
  2408.                
  2409.             return GEO.newRect(self.n .. "XFace" .. os.time(), width, height, highlow, self.y, self.z, orientation)
  2410.            
  2411.         elseif orientation == "y" then
  2412.             local width = self.lx
  2413.             local height = self.lz
  2414.             local highlow
  2415.             if direction == "+" then
  2416.                 highlow = self.yhigh
  2417.             else
  2418.                 highlow = self.ylow
  2419.             end
  2420.                
  2421.             return GEO.newRect(self.n .. "YFace" .. os.time(), width, height, self.x, highlow, self.z, orientation)
  2422.         end
  2423.        
  2424.     -- If this is a cylinder...
  2425.     elseif self.t == "cylinder" then
  2426.         if orientation == self.o then
  2427.             local highlow
  2428.             if direction == "+" then
  2429.                 highlow = self.hhigh
  2430.             else
  2431.                 highlow = self.hlow
  2432.             end
  2433.                
  2434.             -- Return a new circle which overlays the specified face and return that circle.
  2435.             if orientation == "z" then
  2436.                 return GEO.newCircle(self.n .. "ZFace" .. os.time(), self.r, self.x, self.y, highlow, self.o)
  2437.             elseif orientation == "x" then
  2438.                 return GEO.newCircle(self.n .. "XFace" .. os.time(), self.r, highlow, self.y, self.z, self.o)
  2439.             elseif orientation == "y" then
  2440.                 return GEO.newCircle(self.n .. "YFace" .. os.time(), self.r, self.x, highlow, self.z, self.o)
  2441.             end
  2442.         else
  2443.             hprintf("You may only retrieve the orientation face of a cylinder.")
  2444.         end
  2445.     end
  2446. end
  2447.  
  2448. function GEO:copy(name)
  2449.  
  2450.     name = name or self.n .. "Copy" .. os.time()
  2451.     if not GEO[name] then
  2452.         GEO[name] = self
  2453.         return GEO[name]
  2454.     end
  2455. end
  2456.  
  2457. function GEO:monitor(objId)
  2458.  
  2459.     if getobject(objId) then
  2460.         GEO[self.n].m[objId] = true
  2461.         monitored[objId] = true
  2462.     end
  2463. end
  2464.  
  2465. registertimer(10, "GEOTimer")
  2466.  
  2467. pcoords = pcoords or {}  -- Keeps track of previous coordinates of objects being monitored
  2468. monitored = monitored or {}  -- Keeps track of non-player objects being monitored
  2469.  
  2470. function GEOTimer(id, count)
  2471.        
  2472.     -- Loop through the GEO table
  2473.     for k,v in pairs(GEO) do
  2474.         if type(v) == "table" and k ~= "__index" then
  2475.             -- If this GEO is following an object...
  2476.             if v.f then
  2477.                 -- Get the coordinates of the object
  2478.                 local x, y, z = getobjectcoords(v.f)
  2479.                 if x then  -- If this object exists...
  2480.                     if v.player then
  2481.                         local player = objectidtoplayer(v.f)
  2482.                         if player then
  2483.                             local m_player = getplayer(player)  -- See if the player is still here
  2484.                             if m_player then  -- If they are...
  2485.                                 local time_until_respawn = readdword(m_player, 0x2C)  -- Check to see if they're dead
  2486.                                 if time_until_respawn == 0 then  -- If they're not...
  2487.                                     if v.p then  -- If this GEO has perimeter objects...
  2488.                                         for objId, coords in pairs(v.p) do
  2489.                                             if getobject(objId) then
  2490.                                                 local ox, oy, oz = table.unpack(coords)
  2491.                                                 local x_diff = x - v.x
  2492.                                                 local y_diff = y - v.y
  2493.                                                 local z_diff = z - v.z
  2494.                                                 local m_object = getobject(objId)
  2495.                                                 movobjectcoords(objId, ox + x_diff, oy + y_diff, oz + z_diff)  -- Move them with the GEO.
  2496.                                                 GEO[v.n].p[objId] = {ox + x_diff, oy + y_diff, oz + z_diff}
  2497.                                             end
  2498.                                         end
  2499.                                     end
  2500.                                     v:move(x, y, z)  -- Move the GEO to the player's coordinates
  2501.                                 else  -- Otherwise...
  2502.                                     v:delete()  -- Delete the GEO.
  2503.                                 end
  2504.                             else  -- Otherwise...
  2505.                                 v:delete()  -- Delete the GEO.
  2506.                             end
  2507.                         else  -- Otherwise...
  2508.                             v:delete()  -- Delete the GEO.
  2509.                         end
  2510.                     else  -- Otherwise...
  2511.                         if v.p then  -- If this GEO has perimeter objects...
  2512.                             for objId, coords in pairs(v.p) do
  2513.                                 if getobject(objId) then
  2514.                                     local ox, oy, oz = table.unpack(coords)
  2515.                                     local x_diff = x - v.x
  2516.                                     local y_diff = y - v.y
  2517.                                     local z_diff = z - v.z
  2518.                                     local m_object = getobject(objId)
  2519.                                     movobjectcoords(objId, ox + x_diff, oy + y_diff, oz + z_diff)  -- Move them with the GEO.
  2520.                                     GEO[v.n].p[objId] = {ox + x_diff, oy + y_diff, oz + z_diff}
  2521.                                 end
  2522.                             end
  2523.                         end
  2524.                            
  2525.                         v:move(x, y, z)  -- Don't worry about all of that player nonsense and just move the damn GEO.
  2526.                     end
  2527.                 else  -- Otherwise...
  2528.                     v:delete()  -- Delete the GEO.
  2529.                 end
  2530.             end
  2531.                
  2532.             -- If after all of that following nonsense, we still have a GEO...
  2533.             if v then
  2534.                 -- If this GEO is a killzone...
  2535.                 if v.kz then
  2536.                     -- Check if anyone is inside of it.
  2537.                     for i = 0,15 do
  2538.                         local m_player = getplayer(i)
  2539.                         if m_player then
  2540.                             local objId = readdword(m_player, 0x34)
  2541.                             if getobject(objId) then
  2542.                                 if v:contains(objId) then
  2543.                                     local allow = true
  2544.                                     -- If this killzone has a function...
  2545.                                     if v.kzfunc then
  2546.                                         -- If this function returns false...
  2547.                                         if not v.kzfunc(v, i, table.unpack(v.kzargs)) then
  2548.                                             allow = false
  2549.                                         end
  2550.                                     end
  2551.                                     -- If the function allows this player to be killed by this GEO...
  2552.                                     if allow then
  2553.                                         -- If this killzone kills on a delay...
  2554.                                         if v.kzdelay then
  2555.                                             GEO[k].kztime[i] = (v.kztime[i] or 0) + (os.time() - (lasttime or os.time()))
  2556.                                             if GEO[k].kztime[i] >= GEO[k].kzdelay then
  2557.                                                 kill(i)  -- Time's up.
  2558.                                                 if GEO[k].kzmessage then
  2559.                                                     privatesay(i, v.kzmessage)
  2560.                                                 end
  2561.                                                
  2562.                                                 GEO[k].kztime[i] = 0
  2563.                                             end
  2564.                                         else
  2565.                                             kill(i)  -- Kill that ho.
  2566.                                             GEO[k].kztime[i] = 0
  2567.                                         end
  2568.                                     end
  2569.                                 end
  2570.                             else
  2571.                                 GEO[k].kztime[i] = 0
  2572.                             end
  2573.                         end
  2574.                     end
  2575.                 end
  2576.                
  2577.                 -- If this GEO is a damagezone...
  2578.                 if v.damage then
  2579.                     -- Check if anyone is inside of it.
  2580.                     for i = 0,15 do
  2581.                         local m_player = getplayer(i)
  2582.                         if m_player then
  2583.                             local objId = readdword(m_player, 0x34)
  2584.                             if getobject(objId) then
  2585.                                 if v:contains(objId) then
  2586.                                     local allow = true
  2587.                                     -- If this damagezone has a function...
  2588.                                     if v.dmgfunc then
  2589.                                         -- If this function returns false...
  2590.                                         if not v.dmgfunc(v,  i, table.unpack(v.dmgargs)) then
  2591.                                             allow = false
  2592.                                         end
  2593.                                     end
  2594.                                     -- If the function allows this player to be damaged by this GEO...
  2595.                                     if allow then
  2596.                                         -- If this damagezone is on a delay...
  2597.                                         if v.dmgdelay then                                         
  2598.                                             GEO[k].dmgtime[i] = (v.dmgtime[i] or 0) + (os.time() - (lasttime or os.time()))
  2599.                                             if GEO[k].dmgtime[i] >= GEO[k].dmgdelay then
  2600.                                                 applydmg(objId, v.damage / 100)  -- Apply damage
  2601.                                                 -- Check to see if the player has already received the message
  2602.                                                 if not GEO[k].dmgmsgsent[i] then
  2603.                                                     privatesay(i, v.dmgmessage)
  2604.                                                     GEO[k].dmgmsgsent[i] = true
  2605.                                                 end
  2606.                                             end
  2607.                                         else
  2608.                                             applydmg(objId, v.damage / 100)  -- Apply damage
  2609.                                         end
  2610.                                     end
  2611.                                 else
  2612.                                     GEO[k].dmgtime[i] = 0
  2613.                                     GEO[k].dmgmsgsent[i] = nil
  2614.                                 end
  2615.                             end
  2616.                         end
  2617.                     end
  2618.                 end
  2619.                    
  2620.                 -- If this GEO is monitoring for objects entering and exiting it...
  2621.                 if v.m then
  2622.                     -- Loop through the table of objects this GEO is monitoring for.
  2623.                     for objId,_ in pairs(v.m) do
  2624.                         -- If this object still exists...
  2625.                         if getobject(objId) then
  2626.                             -- If this object is inside of the GEO...
  2627.                             if v:contains(objId) then
  2628.                                 local player = objectidtoplayer(objId)
  2629.                                 -- If the GEO didn't know this object was inside of it 1/100 of a second ago...
  2630.                                 if not v.c[objId] then
  2631.                                     -- Call OnGeoEnter.
  2632.                                     local allow = OnGeoEnter(v, player, objId)
  2633.                                     if allow == 0 or allow == false then
  2634.                                         if pcoords[objId] then
  2635.                                             movobjectcoords(objId, table.unpack(pcoords[objId]))
  2636.                                         end
  2637.                                     else
  2638.                                         GEO[k].c[objId] = true
  2639.                                     end
  2640.                                 end
  2641.                             else  -- Otherwise...
  2642.                                 -- If the GEO thought this object was inside of it 1/100 of a second ago...
  2643.                                 if v.c[objId] then
  2644.                                     local player = objectidtoplayer(objId)
  2645.                                     -- Call OnGeoExit.
  2646.                                     local allow = OnGeoExit(v, player, objId)
  2647.                                     if allow == 0 or allow == false then
  2648.                                         if pcoords[objId] then
  2649.                                             movobjectcoords(objId, table.unpack(pcoords[objId]))
  2650.                                         end
  2651.                                     else
  2652.                                         GEO[k].c[objId] = nil
  2653.                                     end
  2654.                                 end
  2655.                             end
  2656.                         else  -- Otherwise...
  2657.                             GEO[k].m[objId] = nil  -- Stop monitoring for this object.
  2658.                         end
  2659.                     end
  2660.                 end
  2661.                    
  2662.                 -- Automatically monitor for players
  2663.                 -- Loop through every player to check if they are in this GEO.
  2664.                 for i = 0, 15 do
  2665.                     local m_player = getplayer(i)
  2666.                     -- If this player exists...
  2667.                     if m_player then
  2668.                         local objId = readdword(m_player, 0x34)
  2669.                         -- If this player is alive...
  2670.                         if getobject(objId) then
  2671.                             -- If this object is inside of the GEO...
  2672.                             if v:contains(objId) then
  2673.                                 local player = objectidtoplayer(objId)
  2674.                                 -- If the GEO didn't know this object was inside of it 1/100 of a second ago...
  2675.                                 if not v.c[objId] then
  2676.                                     -- Call OnGeoEnter.
  2677.                                     local allow = OnGeoEnter(v, player, objId)
  2678.                                     if allow == 0 or allow == false then
  2679.                                         if pcoords[objId] then
  2680.                                             movobjectcoords(objId, table.unpack(pcoords[objId]))
  2681.                                         end
  2682.                                     else
  2683.                                         GEO[k].c[objId] = true
  2684.                                     end
  2685.                                 end
  2686.                             else  -- Otherwise...
  2687.                                 -- If the GEO thought this object was inside of it 1/100 of a second ago...
  2688.                                 if v.c[objId] then
  2689.                                     -- Call OnGeoExit.
  2690.                                     local allow = OnGeoExit(v, i, objId)
  2691.                                     if allow == 0 or allow == false then
  2692.                                         if pcoords[objId] then
  2693.                                             movobjectcoords(objId, table.unpack(pcoords[objId]))
  2694.                                         end
  2695.                                     else
  2696.                                         GEO[k].c[objId] = nil
  2697.                                     end
  2698.                                 end
  2699.                             end
  2700.                         end
  2701.                     end
  2702.                 end
  2703.                    
  2704.                 -- If this GEO has a velocity...
  2705.                 if v.vx or v.vy or v.vz then
  2706.                     if v.p then  -- If this GEO has perimeter objects...
  2707.                         for objId, coords in pairs(v.p) do
  2708.                             if getobject(objId) then
  2709.                                 local ox, oy, oz = table.unpack(coords)
  2710.                                 local m_object = getobject(objId)
  2711.                                 movobjectcoords(objId, ox + (v.vx or 0), oy + (v.vy or 0), oz + (v.vz or 0))  -- Move them with the GEO.
  2712.                                 GEO[v.n].p[objId] = {ox + (v.vx or 0), oy + (v.vy or 0), oz + (v.vz or 0)}
  2713.                             end
  2714.                         end
  2715.                     end
  2716.                     -- Move that ho.
  2717.                     v:move(v.x + (v.vx or 0) / 100, v.y + (v.vy or 0) / 100, v.z + (v.vz or 0) / 100)
  2718.                 end
  2719.             end
  2720.         end
  2721.     end
  2722.        
  2723.     -- Update coordinates at a slight delay for blocking GEO entry/exit (if there is no delay, players in mid-air could end up dying since they'll just be teleported in mid-air perpetually)
  2724.     if count % 25 == 0 then
  2725.         -- Each player
  2726.         for i = 0,15 do
  2727.             local m_player = getplayer(i)
  2728.             if m_player then
  2729.                 local objId = readdword(m_player, 0x34)
  2730.                 if getobject(objId) then
  2731.                     pcoords[objId] = {getobjectcoords(objId)}
  2732.                 else
  2733.                     pcoords[objId] = nil
  2734.                 end
  2735.             end
  2736.         end
  2737.            
  2738.         -- Each object being monitored
  2739.         for objId,_ in pairs(monitored) do
  2740.             if getobject(objId) then
  2741.                 pcoords[objId] = {getobjectcoords(objId)}
  2742.             else
  2743.                 pcoords[objId] = nil
  2744.                 monitored[objId] = nil
  2745.             end
  2746.         end
  2747.     end
  2748.        
  2749.     lasttime = os.time()  -- Keeps track of the os time the GEOTimer last executed
  2750.        
  2751.     return true
  2752. end
Advertisement
Add Comment
Please, Sign In to add comment