- -- GEO 2.3.2 (Geometric Environment Objects)
- function OnGeoEnter(geo, player, objId)
- return true
- end
- function OnGeoExit(geo, player, objId)
- return true
- end
- --[[
- Documentation:
- This script allows the creation of environmental volumes in the shapes of spheres, rectangular prisms, cylinders, rectangles, or circles.
- 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.
- 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.
- Changes from GEOs for Phasor 058 and GEOs for 2.0:
- -- GEO:damagezone now has the following function header: GEO:damagezone(boolean, damage).
- -- GEO:freeze has been changed to GEO:unfollow.
- Changes in GEO 2.2:
- -- Players are now automatically monitored for every GEO. If you want a GEO to monitor other objects, you still need to use GEO:monitor.
- -- Support for cylinder orientation. You can now orient your cylinder along the x, y, or z axis.
- -- Added GEO.clear() to destroy all GEOs.
- -- 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.
- -- GEO:killzone() improved; now takes three arguments: <boolean> <delay> <kill_message>
- -- GEO:surface() now works; use this to show 3D representations of your GEOs.
- -- GEO:perimeter() and GEO:surface create objects which are unaffected by physics.
- -- Returning false in OnGeoEnter and OnGeoExit now works on non-player objects.
- -- Various bug fixes
- Changes in GEO 2.3.1:
- -- GEO:killzone() further improved; now takes infinite arguments: <boolean> <delay> <kill_message> <function> <args...>
- -- GEO:damagezone() improved; now takes infinite arguments: <boolean> <damage> <delay> <damage_message> <function> <args...>
- -- GEO:surface() fixed for rectangular prisms (performance should be better too), spheres (the rings are now more uniform), and cylinders (the base of the cylinder was sometimes drawn too high)
- -- 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>
- -- Added GEO:containspoint(); returns a boolean (true or false) if the specified GEO contains the specified point. Arguments: <point>
- -- Added GEO:randomcoords(); returns random coordinates within the specified GEO (does not take AntiGEOs into account yet). No arguments.
- -- Fixed a terrible handling of AntiGEOs (now uses GEO:contains() like it should have in the first place)
- GEO Creation Functions:
- GEO.newSphere(name, radius, x_coord, y_coord, z_coord)
- -- name: <string> Name of this new GEO (must be unique).
- -- radius: <float> Radius of this sphere.
- -- x_coord: <float> X-coordinate of the center of this sphere.
- -- y_coord: <float> Y-coordinate of the center of this sphere.
- -- z_coord: <float> Z-coordinate of the center of this sphere.
- Example:
- local sphere = GEO.newSphere("Sphere1", 5, 0, 0, 0)
- Notes:
- 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.
- GEO.newRectPrism(name, x_length, y_length, z_length, x_coord, y_coord, z_coord)
- -- name: <string> Name of this new GEO (must be unique).
- -- x_length: <float> Length of this rectangular prism in the X direction.
- -- y_length: <float> Length of this rectangular prism in the Y direction.
- -- z_length: <float> Length of this rectangular prism in the Z direction.
- -- x_coord: <float> X-coordinate of the center of this rectangular prism.
- -- y_coord: <float> Y-coordinate of the center of this rectangular prism.
- -- z_coord: <float> Z-coordinate of the center of this rectangular prism.
- Example:
- local rectprism = GEO.newRectPrism("RectPrism1", 3, 3, 10, 0, 0, 5)
- Notes:
- 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.
- GEO.newCylinder(name, radius, height, x_coord, y_coord, z_coord, [orientation])
- -- name: <string> Name of this new GEO (must be unique).
- -- radius: <float> Radius of this cylinder.
- -- height: <float> Height of this cylinder.
- -- x_coord: <float> X-coordinate of the center of this cylinder.
- -- y_coord: <float> Y-coordinate of the center of this cylinder.
- -- z_coord: <float> Z-coordinate of the center of this cylinder.
- -- orientation: <string> Axis of orientation ("x", "y", or "z") (default = "z").
- Example:
- local cylinder = GEO.newCylinder("Cylinder1", 3, 10, 0, 0, 0)
- Notes:
- 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.
- GEO.newRect(name, width, height, x_coord, y_coord, z_coord, [orientation])
- -- name: <string> Name of this new GEO (must be unique).
- -- width: <float> Width of this rectangle.
- -- height: <float> Height of this rectangle.
- -- x_coord: <float> X-coordinate of the center of this rectangle.
- -- y_coord: <float> Y-coordinate of the center of this rectangle.
- -- z_coord: <float> Z-coordinate of the center of this rectangle.
- -- orientation: <string> Axis of orientation ("x", "y", or "z") (default = "z").
- Example:
- local rectangle = GEO.newRect("Rect1", 5, 10, 0, 0, 5, "x")
- Notes:
- 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.
- GEO.newCircle(name, radius, x_coord, y_coord, z_coord, [orientation])
- -- name: <string> Name of this new GEO (must be unique).
- -- radius: <float> Radius of this circle.
- -- x_coord: <float> X-coordinate of the center of this circle.
- -- y_coord: <float> Y-coordinate of the center of this circle.
- -- z_coord: <float> Z-coordinate of the center of this circle.
- -- orientation: <string> Axis of orientation ("x", "y", or "z") (default = "z")
- Example:
- local circle = GEO.newCircle("Circle1", 10, 0, 0, 5, "x")
- Notes:
- 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.
- Once you've created your GEO, you want to make it do something. The following are GEO-editing functions.
- 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.
- GEO Editing Functions:
- GEO:delete()
- Example:
- local sphere = GEO.newSphere("Sphere", 5, 0, 0, 0)
- sphere:delete()
- Notes:
- Deletes the specified GEO.
- GEO:move(x_coord, y_coord, z_coord)
- -- x_coord: <float> New x-coordinate of the center of this GEO.
- -- y_coord: <float> New y-coordinate of the center of this GEO.
- -- z_coord: <float> New z-coordinate of the center of this GEO.
- Example:
- local sphere = GEO.newSphere("Sphere", 5, 0, 0, 0)
- sphere:move(0, 0, 10)
- Notes:
- Moves the center of the specified GEO to the specified x, y, and z-coordinates.
- GEO:radius([new_radius])
- -- new_radius: <float> New radius of specified GEO (if no radius is specified, the radius of the GEO is returned).
- Example:
- local cylinder = GEO.newCylinder("Cylinder", 10, 20, 0, 0, 0)
- local cyl_radius = cylinder:radius()
- if cyl_radius < 20 then
- cylinder:radius(20)
- end
- Notes:
- 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.
- GEO:size([x_length], [y_length], [z_length])
- -- x_length: <float> New length of GEO in the x direction.
- -- y_length: <float> New length of GEO in the y direction.
- -- z_length: <float> New length of GEO in the z direction.
- -- Read the notes for descriptions about how the optional parameters work in this function.
- Example:
- -- Create three GEOs
- local rectprism = GEO.newRectPrism("RectPrism", 5, 5, 10, 0, 0, 5)
- local rectangle = GEO.newRect("Rect", 5, 10, 0, 0, 5, "y")
- local cylinder = GEO.newCylinder("Cylinder", 5, 20, 0, 0, 10)
- -- Get sizes of all three shapes
- local rpx, rpy, rpz = rectprism:size()
- local rw, rh = rectangle:size()
- local cyl_height = cylinder:size()
- -- Double their sizes
- rectprism:size(rpx * 2, rpy * 2, rpz * 2)
- rectangle:size(rw * 2, 0, rh * 2)
- cylinder:size(cyl_height * 2)
- Notes:
- 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).
- GEO:contains(objId)
- objId: <object id> Object being tested to see if it is inside of the specfied GEO.
- Example:
- local sphere = GEO.newSphere("Sphere", 5, 0, 0, 0)
- function OnPlayerSpawn(player)
- local m_player = getplayer(player)
- local objId = readdword(m_player, 0x34)
- if sphere:contains(objId) then
- say(getname(player) .. " has spawned inside of the sphere")
- end
- end
- Notes:
- Checks if the specified objId is within the specified GEO. Returns true or false.
- GEO:containspoint(point)
- point: <table> Coordinates being checked.
- Example:
- local sphere = GEO.newSphere("Sphere", 5, 0, 0, 0)
- local point = {10, 10, 0}
- local insphere = GEO:containspoint(point)
- if insphere(point) then
- say("(10, 10, 0) is in the sphere!")
- end
- Notes:
- Make sure the table passed is in {x, y, z} format.
- GEO:vectorintersect(pointA, pointB)
- pointA: <table> First point in vector.
- pointB: <table> Second point in vector.
- Example:
- -- This is a poor example that shows simply functionality
- sphere = GEO.newSphere("sphere", 5, 0, 0, 0)
- function OnPlayerSpawn(player)
- local m_player = getplayer(player)
- local objId = readdword(m_player, 0x34)
- local x, y, z = getobjectcoords(objId)
- 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.
- if point1 then
- say("x: " .. point1[1] .. " y: " .. point1[2] .. " z: " .. point1[3])
- end
- if point2 then
- say("x: " .. point2[1] .. " y: " .. point2[2] .. " z: " .. point2[3])
- end
- end
- Notes:
- 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.
- GEO:follow(objId)
- objId: <object id> Object this GEO should follow.
- Example:
- function OnPlayerSpawn(player)
- local m_player = getplayer(player)
- local objId = readdword(m_player, 0x34)
- -- Create a sphere with the objId in the name so you know it is unique
- local sphere = GEO.newSphere("Sphere" .. objId, 5, 0, 0, 0)
- sphere:follow(objId)
- end
- Notes:
- Sets a GEO to follow the specified objId. The GEO will be automatically deleted if the object it is following has also been deleted.
- GEO:unfollow()
- Example:
- function OnPlayerSpawn(player)
- local m_player = getplayer(player)
- local objId = readdword(m_player, 0x34)
- -- Create a sphere with the objId in the name so you know it is unique
- local sphere = GEO.newSphere("Sphere" .. objId, 5, 0, 0, 0)
- sphere:follow(objId)
- sphere:unfollow()
- end
- Notes:
- Tells the specified GEO to stop following the objId it is currently following.
- GEO:velocity(x_velocity, y_velocity, z_velocity)
- x_velocity: <float> GEO's velocity in the x direction.
- y_velocity: <float> GEO's velocity in the y direction.
- z_velocity: <float> GEO's velocity in the z direction.
- Example:
- local rectangle = GEO.newRectPrism("RectPrism", math.inf, math.inf, 100, 0, 0, -50)
- rectangle:velocity(0, 0, 0.1)
- Notes:
- Sets the velocity of the specified GEO in the x, y, and z directions.
- GEO:killzone([boolean], [delay], [kill_message], [func], [args...])
- boolean: <boolean> The ability of this GEO to be a killzone (if no boolean is specified, the current killzone boolean is returned).
- delay: <int> The time (in seconds) the GEO should wait to kill a player after they enter.
- kill_message: <string> The message a player will receive when they are killed by this GEO.
- func: <function> A special function that MUST return true or false and MUST pass the GEO, then player as its first two arguments.
- args...: <data> Additional arguments to be passed into the function.
- Example:
- -- Kill a player immediately for entering a sphere
- local killsphere = GEO.newSphere("Sphere", 10, 0, 0, 0)
- killsphere:killzone(true)
- function isredwednesday(geo, player, day)
- if getteam(player) == 0 then
- if day == "Wednesday" then
- return true
- end
- end
- return false
- end
- -- Kill a player after being in a cube for 5 seconds if they're on the Red Team and today is Wednesday.
- local killcube = GEO.newRectPrism("Cube", 5, 5, 5, 10, -10, 5)
- 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"))
- Notes:
- 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.
- GEO:damagezone([boolean], [damage], [delay], [damage_message], [func], [args...])
- boolean: <boolean> Indicates whether or not this GEO is a damagezone (if no boolean is specified, the current damagezone boolean is returned).
- damage: <float> Amount of damage per second this GEO does to a player within it.
- delay: <int> The time (in seconds) the GEO should wait to damage a player after they enter.
- damage_message: <string> The message a player will receive when they are initially damaged by this GEO.
- func: <function> A special function that MUST return true or false and MUST pass the GEO, then player as its first two arguments.
- args...: <data> Additional arguments to be passed into the function.
- Example:
- -- Damage a player as soon as they enter this sphere.
- local damagesphere = GEO.newSphere("Sphere", 10, 0, 0, 0)
- damagesphere:damagezone(true, 10)
- -- Damage a player after 10 seconds if they are named Nuggets and there is a GEO called "Sphere".
- function isnuggetssphere(geo, player, sphere_exists)
- if getname(player) == "Nuggets" then
- if sphere_exists then
- return true
- end
- end
- return false
- end
- local damagecylinder = GEO.newCylinder("Cylinder", 5, 10, 0, 0, 5)
- damagecylinder:damagezone(true, 50, 10, "Your name is Nuggets and there is a sphere somewhere around here.", isnuggetssphere, GEO.get("Sphere"))
- Notes:
- 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.
- GEO:face(orientation, direction)
- orientation: <string> The axis on which the face will be created ("x", "y", or "z").
- direction: <string> The specification of the "top" or "bottom" face in the specified orientation ("+" or "-").
- Example:
- local cylinder = GEO.newCylinder("Cylinder", 5, 10, 0, 0, 5)
- local topcircle = cylinder:face("z", "+")
- local bottomcircle = cylinder:face("z", "-")
- Notes:
- 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.
- GEO:extend(orientation, direction, amount)
- orientation: <string> The axis on which the face will be extended ("x", "y", or "z").
- direction: <string> The specification of the "top" or "bottom" face in the specified orientation ("+" or "-").
- amount: <float> Distance the specified face will be extended.
- Example:
- local cylinder = GEO.newCylinder("Cylinder", 5, 10, 0, 0, 5)
- cylinder:extend("z", "+", 10)
- Notes:
- 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.
- GEO:antigeo([boolean, geos...])
- boolean: <boolean> Toggles whether or not this is an AntiGEO.
- geos...: <data> Defines for which GEOs this is an AntiGEO (infinite number of argumnets).
- Example:
- -- Battle Creek Blue Base Roof
- blue_base_roof = GEO.newRectPrism("blue_roof", 9.5, 7.8, 1.3, 1.95, 13.9, 1.85)
- blue_roof_anti = GEO.newRectPrism("blue_roof_anti", 10, 10, 2, 28.75, 14, 1.5)
- -- Cut out the section of the blue_base_roof GEO that is inside of the room where the CTF flag is
- blue_roof_anti:antigeo(true, blue_base_roof)
- Notes:
- 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.
- GEO:copy([name])
- name: <string> Name given to the copy of the specified GEO (if no name is specified, a default name is given).
- Example:
- local sphere = GEO.newSphere("Sphere", 5, 0, 0, 0)
- local sphere2 = sphere:copy()
- Notes:
- Returns a copy of the specified GEO with the specified name. If no name is specified, a default name will be given.
- GEO:monitor(objId)
- objId: <object id> The object this GEO should monitor for entering/exiting.
- Example:
- local rectprism = GEO.newRectPrism("RectPrism", math.inf, math.inf, 100, 0, 0, -50)
- function OnObjectCreation(objId)
- local m_object = getobject(objId)
- local mapId = readdword(m_object)
- local _, tagtype = gettaginfo(mapId)
- if tagtype == "vehi" then
- rectprism:monitor() -- Monitor all vehicles
- end
- end
- Notes:
- 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.
- GEO:coords()
- Example:
- local sphere = GEO.newSphere("Sphere", 10, 0, 0, 0)
- local x, y, z = sphere:coords()
- Notes:
- Returns the x, y, and z coordinates of the center of the specified GEO.
- GEO:high(orientation)
- orientation: <string> Axis of which you're attempting to find the highest point ("x", "y", or "z").
- Example:
- local sphere = GEO.newSphere("Sphere", 10, 0, 0, 0)
- local zhigh = sphere:high("z")
- Notes:
- 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).
- GEO:low(orientation)
- orientation: <string> Axis of which you're attempting to find the lowest point ("x", "y", or "z")
- Example:
- local sphere = GEO.newSphere("Sphere", 10, 0, 0, 0)
- local zlow = sphere:low("z")
- Notes:
- 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).
- GEO:randomcoords()
- Example:
- local cylinder = GEO.newCylinder("Cylinder", 5, 10, 0, 0, 0)
- local x, y, z = cylinder:randomcoords()
- Notes:
- Returns random x, y, and z coordinates within the specified GEO.
- GEO:type()
- Example:
- local cylinder = GEO.newCylinder("Cylinder", 5, 10, 0, 0, 0)
- local type = cylinder:type()
- Notes:
- Returns the type of the specified GEO as a string.
- GEO:orientation()
- Example:
- local cylinder = GEO.newCylinder("Cylinder", 5, 10, 0, 0, 0)
- local orientation = cylinder:orientation()
- Notes:
- Returns the orientation of a 2-dimensional GEO or Cylinder. Note that this cannot be used on Spheres or RectPrisms.
- GEO:name()
- Example:
- local sphere = GEO.newSphere("Sphere", 5, 0, 0, 0)
- local name = sphere:name()
- Notes:
- Returns the name of the specified GEO.
- GEO:perimeter([density], [mapId])
- density: <int> Amount of objects that should spawn around the perimeter of the GEO.
- mapId: <tag id> The tag of the objects that should spawn around the GEO's perimeter (default = Full-Spectrum Vision)
- Example:
- local sphere = GEO.newSphere("sphere", 5, 0, 0, 0.5)
- sphere:perimeter(20)
- local rectprism = GEO.newRectPrism("rectprism", 3, 4, 5, 0, 0, 0.5)
- rectprism:perimeter(7, gettagid("weap", "weapons\\flag\\flag"))
- Notes:
- 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.
- GEO:surface([density], [mapId])
- density: <int> Amount of objects that should spawn to create a 3D representation of the GEO.
- mapId: <tag id> The tag of the objects that should spawn to create the 3D representation of the GEO (default = Full-Spectrum Vision)
- Example:
- local sphere = GEO.newSphere("sphere", 5, 0, 0, 1)
- sphere:surface()
- local cylinder = GEO.newCylinder("cylinder", 4, 7, 0, 0, 1, "x")
- cylinder:surface(20, gettagid("weap", "weapons\\plasma grenade\\plasma grenade"))
- Notes:
- 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.
- OnGeoEnter and OnGeoExit Event Functions:
- OnGeoEnter(geo, player, objId)
- geo: <GEO object> The GEO the player or object is entering.
- player: <player memory id> The player entering the GEO.
- objId: <object id> The objId entering the GEO.
- Example:
- sphere = GEO.newSphere("sphere", 5, 0, 0, 0)
- function OnGeoEnter(geo, player, objId)
- if geo == sphere then
- privatesay(player, "You may not enter this GEO.")
- return false
- end
- return true
- end
- Notes:
- 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.
- OnGeoExit(geo, player, objId)
- geo: <GEO object> The GEO the player or object is exiting.
- player: <player memory id> The player exiting the GEO.
- objId: <object id> The objId exiting the GEO.
- Example:
- sphere = GEO.newSphere("sphere", 5, 0, 0, 0)
- function OnGeoExit(geo, player, objId)
- if geo == sphere then
- privatesay(player, "You may not exit this GEO.")
- return false
- end
- return true
- end
- Notes:
- 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.
- Miscellaneous GEO functions:
- GEO.get(name)
- name: <string> Name of the GEO you're trying to access.
- Example:
- function OnPlayerSpawn(player)
- local m_player = getplayer(player)
- local objId = readdword(m_player, 0x34)
- local sphere = GEO.newSphere("Sphere" .. objId, 5, 0, 0, 0)
- sphere:follow(objId)
- end
- function OnPlayerKill(killer, victim, mode)
- local m_victim = getplayer(victim)
- local v_objectId = readdword(m_victim, 0x34)
- local victim_sphere = GEO.get("Sphere" .. v_objectId)
- victim_sphere:unfollow()
- end
- Notes:
- Returns a GEO given the specified name.
- GEO.followedBy(objId)
- objId: <object id> Object being followed by GEOs.
- Example:
- function OnPlayerKill(killer, victim, mode)
- local geos = GEO.followedBy(victim)
- for k,v in ipairs(geos) do
- v:unfollow()
- end
- end
- Notes:
- Returns a list of GEOs following the specified objId.
- GEO.cleanup(player)
- player: <player memory id> Player whose following of GEOs you want to delete.
- Example:
- -- Note that this is here only for the sake of example; this is not necessary, as the GEOTimer takes care of this automatically.
- function OnPlayerKill(killer, victim, mode)
- GEO.cleanup(victim)
- end
- Notes:
- Deletes all of the GEOs currently following the specified player.
- GEO.clear()
- Example:
- function OnNewGame(map)
- -- Global variable for map name
- cur_map = map
- end
- function OnServerChat(player, message, type)
- if message == "reset" then
- svcmd("sv_map_reset")
- GEO.clear()
- OnNewGame(cur_map)
- end
- end
- Notes:
- Deletes all GEOs currently active on the map.
- If you have any questions about how GEOs work, PM me (Nuggets) at phasor.proboards.com.
- --]]
- -- GEO --
- -- Create Metatable
- GEO = {}
- GEO.__index = GEO
- -- Set random seed and define infinity
- math.randomseed(os.time())
- math.inf = 1 / 0
- function inSphere(objId, x, y, z, r)
- local ox, oy, oz = getobjectcoords(objId)
- if ox then
- -- Pythagorean
- local dist = math.sqrt((x - ox) ^ 2 + (y - oy) ^ 2 + (z - oz) ^ 2)
- if dist <= r then
- return true
- end
- end
- return false
- end
- function inCircle(objId, x, y, z, r, orientation)
- local ox, oy, oz = getobjectcoords(objId)
- if ox then
- -- Default orientation to "z"
- orientation = orientation or "z"
- -- Pythagorean based on circle's orientation
- if orientation == "z" then
- local dist = math.sqrt((x - ox) ^ 2 + (y - oy) ^ 2)
- if dist <= r and oz == z then
- return true
- end
- elseif orientation == "y" then
- local dist = math.sqrt((x - ox) ^ 2 + (z - oz) ^ 2)
- if dist <= r and oy == y then
- return true
- end
- elseif orientation == "x" then
- local dist = math.sqrt((y - oy) ^ 2 + (z - oz) ^ 2)
- if dist <= r and ox == x then
- return true
- end
- end
- end
- return false
- end
- function inCylinder(objId, x, y, z, hlow, hhigh, r, orientation)
- local ox, oy, oz = getobjectcoords(objId)
- if ox then
- if orientation == "z" then
- -- Pythagorean to see if object is within radius of circle
- local dist = math.sqrt((x - ox) ^ 2 + (y - oy) ^ 2)
- -- Make sure the object is also within the height of the cylinder
- if dist <= r and oz >= hlow and oz <= hhigh then
- return true
- end
- elseif orientation == "x" then
- local dist = math.sqrt((y - oy) ^ 2 + (z - oz) ^ 2)
- if dist <= r and ox >= hlow and ox <= hhigh then
- return true
- end
- elseif orientation == "y" then
- local dist = math.sqrt((z - oz) ^ 2 + (x - ox) ^ 2)
- if dist <= r and oy >= hlow and oy <= hhigh then
- return true
- end
- end
- end
- return false
- end
- function inRectangle(objId, xlow, xhigh, ylow, yhigh, zlow, zhigh)
- -- These functions are essentially the same
- return inRectPrism(objId, xlow, xhigh, ylow, yhigh, zlow, zhigh)
- end
- function inRectPrism(objId, xlow, xhigh, ylow, yhigh, zlow, zhigh)
- local x, y, z = getobjectcoords(objId)
- if x then
- -- Make sure the coordinates are inside of each extreme of the rectangular prism
- if x <= xhigh and x >= xlow and y <= yhigh and y >= ylow and z <= zhigh and z >= zlow then
- return true
- end
- end
- return false
- end
- function randomInSphere(x, y, z, r)
- -- Increase precision
- x = math.floor(x * 100)
- y = math.floor(y * 100)
- z = math.floor(z * 100)
- r = math.floor(r * 100)
- -- Find random values inside of the sphere.
- 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
- end
- function randomInCircle(x, y, z, r, orientation)
- -- Increase precision
- r = math.floor(r * 100)
- -- Possible values depend on circle's orientation.
- if orientation == "z" then
- x = math.floor(x * 100)
- y = math.floor(y * 100)
- return math.random(x - r, x + r + 1) / 100, math.random(y - r, y + r + 1) / 100, z
- elseif orientation == "x" then
- y = math.floor(y * 100)
- z = math.floor(z * 100)
- return x, math.random(y - r, y + r + 1) / 100, math.random(z - r, z + r + 1) / 100
- elseif orientation == "y" then
- x = math.floor(x * 100)
- z = math.floor(z * 100)
- return math.random(x - r, x + r + 1) / 100, y, math.random(z - r, z + r + 1) / 100
- end
- end
- function randomInCylinder(x, y, z, hlow, hhigh, r, orientation)
- -- Increase precision
- x = math.floor(x * 100)
- y = math.floor(y * 100)
- z = math.floor(z * 100)
- hlow = math.floor(hlow * 100)
- hhigh = math.floor(hhigh * 100)
- r = math.floor(r * 100)
- -- Find random values inside of the cylinder depending on its orientation.
- if orientation == "z" then
- return math.random(x - r, x + r + 1) / 100, math.random(y - r, y + r + 1) / 100, math.random(hlow, hhigh + 1) / 100
- elseif orientation == "y" then
- return math.random(x - r, x + r + 1) / 100, math.random(hlow, hhigh + 1) / 100, math.random(z - r, z + r + 1) / 100
- elseif orientation == "x" then
- return math.random(hlow, hhigh + 1) / 100, math.random(y - r, r + r + 1) / 100, math.random(z - r, z + r + 1) / 100
- end
- end
- function randomInRectPrism(xlow, xhigh, ylow, yhigh, zlow, zhigh)
- -- Increase precision
- xlow = math.floor(xlow * 100)
- xhigh = math.floor(xhigh * 100)
- ylow = math.floor(ylow * 100)
- yhigh = math.floor(yhigh * 100)
- zlow = math.floor(zlow * 100)
- zhigh = math.floor(zhigh * 100)
- -- Find random values inside of the rectangular prism.
- return math.random(xlow, xhigh + 1) / 100, math.random(ylow, yhigh + 1) / 100, math.random(zlow, zhigh)
- end
- function sphereintersect(sphere, pointA, pointB)
- local points = {}
- local a = (pointA.x - pointB.x) ^ 2 + (pointA.y - pointB.y) ^ 2 + (pointA.z - pointB.z) ^ 2
- local c = (pointA.x - sphere.x) ^ 2 + (pointA.y - sphere.y) ^ 2 + (pointA.z - sphere.z) ^ 2 - sphere.r ^ 2
- local b = (pointB.x - sphere.x) ^ 2 + (pointB.y - sphere.y) ^ 2 + (pointB.z - sphere.z) ^ 2 - c - a - sphere.r ^ 2
- -- Make sure square root is not imaginary.
- local q = b ^ 2 - 4 * a * c
- if q > 0 then
- -- Quadratic Formula:
- local t1 = (-b + math.sqrt(q)) / (2 * a)
- local t2 = (-b - math.sqrt(q)) / (2 * a)
- local xt1 = pointA.x * (1 - t1) + t1 * pointB.x
- local yt1 = pointA.y * (1 - t1) + t1 * pointB.y
- local zt1 = pointA.z * (1 - t1) + t1 * pointB.z
- local xt2 = pointA.x * (1 - t2) + t2 * pointB.x
- local yt2 = pointA.y * (1 - t2) + t2 * pointB.y
- local zt2 = pointA.z * (1 - t2) + t2 * pointB.z
- -- Make sure the intersection is on this line.
- local ipoint1, ipoint2
- if t1 >= 0 and t1 <= 1 then
- table.insert(points, {x = xt1, y = yt1, z = zt1})
- end
- -- Make sure the intersection is on this line.
- if t2 >= 0 and t2 <= 1 then
- table.insert(points, {x = xt2, y = yt2, z = zt2})
- end
- end
- return points
- end
- function cylinderintersect(cylinder, pointA, pointB)
- -- Change x, y and z depending on orientation.
- local x, y, z
- if cylinder.o == "z" then
- x, y, z = "x", "y", "z"
- elseif cylinder.o == "x" then
- x, y, z = "z", "y", "x"
- elseif cylinder.o == "y" then
- x, y, z = "x", "z", "y"
- end
- local points = {}
- -- Determine if line intersects cylinder's edges.
- local a = (pointA[x] - pointB[x]) ^ 2 + (pointA[y] - pointB[y]) ^ 2
- local c = (pointA[x] - cylinder[x]) ^ 2 + (pointA[y] - cylinder[y]) ^ 2 - cylinder.r ^ 2
- local b = (pointB[x] - cylinder[x]) ^ 2 + (pointB[y] - cylinder[y]) ^ 2 - a - c - cylinder.r ^ 2
- local q = b ^ 2 - 4 * a * c
- if q > 0 then
- local t1 = (-b + math.sqrt(q)) / (2 * a)
- local t2 = (-b - math.sqrt(q)) / (2 * a)
- local xt1 = pointA[x] * (1 - t1) + t1 * pointB[x]
- local yt1 = pointA[y] * (1 - t1) + t1 * pointB[y]
- local zt1 = pointA[z] * (1 - t1) + t1 * pointB[z]
- local xt2 = pointA[x] * (1 - t2) + t2 * pointB[x]
- local yt2 = pointA[y] * (1 - t2) + t2 * pointB[y]
- local zt2 = pointA[z] * (1 - t2) + t2 * pointB[z]
- -- Make sure the intersection is on this line and within the cylinder's height.
- if t1 >= 0 and t1 <= 1 and zt1 >= cylinder.hlow and zt1 <= cylinder.hhigh then
- table.insert(points, {[x] = xt1, [y] = yt1, [z] = zt1})
- end
- -- Make sure the intersection is on this line and within the cylinder's height.
- if t2 >= 0 and t2 <= 1 and zt2 >= cylinder.hlow and zt2 <= cylinder.hhigh then
- table.insert(points, {[x] = xt2, [y] = yt2, [z] = zt2})
- end
- -- Check end caps
- -- If either point on the line is lower than or equal to the lowest point on the cylinder...
- if pointA[z] <= cylinder.hlow or pointB[z] <= cylinder.hlow then
- local tzlow = (cylinder.hlow - pointA[z]) / (pointA[z] + pointB[z])
- local xtlow = pointA[x] * (1 - tzlow) + tzlow * pointB[x]
- local ytlow = pointA[y] * (1 - tzlow) + tzlow * pointB[y]
- local dist = math.sqrt((cylinder[x] - xtlow) ^ 2 + (cylinder[y] - ytlow) ^ 2)
- if dist < cylinder.r then
- table.insert(points, {[x] = xtlow, [y] = ytlow, [z] = cylinder.hlow})
- end
- end
- -- If either point on the line is higher than or equal to the lowest point on the cylinder...
- if pointA[z] >= cylinder.hhigh or pointB[z] >= cylinder.hhigh then
- local tzhigh = (cylinder.hhigh - pointA[z]) / (pointA[z] + pointB[z])
- local xthigh = pointA[x] * (1 - tzhigh) + tzhigh * pointB[x]
- local ythigh = pointA[y] * (1 - tzhigh) + tzhigh * pointB[y]
- local dist = math.sqrt((cylinder[x] - xthigh) ^ 2 + (cylinder[y] - ythigh) ^ 2)
- if dist < cylinder.r then
- table.insert(points, {[x] = xthigh, [y] = ythigh, [z] = cylinder.hhigh})
- end
- end
- end
- return points
- end
- function rectprismintersect(rectprism, pointA, pointB)
- local points = {}
- -- Get the time "t" on the line where intersection with each coordinate's extreme occurs.
- local txlow = (rectprism.xlow - pointA.x) / (pointA.x + pointB.x)
- local txhigh = (rectprism.xhigh - pointA.x) / (pointA.x + pointB.x)
- local tylow = (rectprism.ylow - pointA.y) / (pointA.y + pointB.y)
- local tyhigh = (rectprism.yhigh - pointA.y) / (pointA.y + pointB.y)
- local tzlow = (rectprism.zlow - pointA.z) / (pointA.z + pointB.z)
- local tzhigh = (rectprism.zhigh - pointA.z) / (pointA.z + pointB.z)
- -- Find other coordinates given t.
- local xlowy = pointA.y * (1 - txlow) + txlow * pointB.y
- local xlowz = pointA.z * (1 - txlow) + txlow * pointB.z
- -- Ensure that this intersection is within the bounds of the rectangular prism.
- if xlowy <= rectprism.yhigh and xlowy >= rectprism.ylow and xlowz <= rectprism.zhigh and xlowz >= rectprism.zlow then
- table.insert(points, {x = rectprism.xlow, y = xlowy, z = xlowz})
- end
- -- Find other coordinates given t.
- local xhighy = pointA.y * (1 - txhigh) + txhigh * pointB.y
- local xhighz = pointA.z * (1 - txhigh) + txhigh * pointB.z
- -- Ensure that this intersection is within the bounds of the rectangular prism.
- if xhighy <= rectprism.yhigh and xhighy >= rectprism.ylow and xhighz <= rectprism.zhigh and xhighz >= rectprism.zlow then
- table.insert(points, {x = rectprism.xhigh, y = xhighy, z = xhighz})
- end
- -- Find other coordinates given t.
- local ylowx = pointA.x * (1 - tylow) + tylow * pointB.x
- local ylowz = pointA.z * (1 - tylow) + tylow * pointB.z
- -- Ensure that this intersection is within the bounds of the rectangular prism.
- if ylowx <= rectprism.xhigh and ylowx >= rectprism.xlow and ylowz <= rectprism.zhigh and ylowz >= rectprism.zlow then
- table.insert(points, {x = ylowx, y = rectprism.ylow, z = ylowz})
- end
- -- Find other coordinates given t.
- local yhighx = pointA.x * (1 - tyhigh) + tyhigh * pointB.x
- local yhighz = pointA.z * (1 - tyhigh) + tyhigh * pointB.z
- -- Ensure that this intersection is within the bounds of the rectangular prism.
- if yhighx <= rectprism.xhigh and yhighx >= rectprism.xlow and yhighz <= rectprism.zhigh and yhighz >= rectprism.zlow then
- table.insert(points, {x = yhighx, y = rectprism.yhigh, z = yhighz})
- end
- -- Find other coordinates given t.
- local zlowx = pointA.x * (1 - tzlow) + tzlow * pointB.x
- local zlowy = pointA.y * (1 - tzlow) + tzlow * pointB.y
- -- Ensure that this intersection is within the bounds of the rectangular prism.
- if zlowx <= rectprism.xhigh and zlowx >= rectprism.xlow and zlowy <= rectprism.yhigh and zlowy >= rectprism.ylow then
- table.insert(points, {x = zlowx, y = zlowy, z = rectprism.zlow})
- end
- -- Find other coordinates given t.
- local zhighx = pointA.x * (1 - tzhigh) + tzhigh * pointB.x
- local zhighy = pointA.y * (1 - tzhigh) + tzhigh * pointB.y
- -- Ensure that this intersection is within the bounds of the rectangular prism.
- if zhighx <= rectprism.xhigh and zhighx >= rectprism.xlow and zhighy <= rectprism.yhigh and zhighy >= rectprism.ylow then
- table.insert(points, {x = zhighx, y = zhighy, z = rectprism.zhigh})
- end
- -- Corner and edge check
- if #points > 2 then
- -- Remove entries from the table while we loop through it.
- local i = 0
- while i <= #points do
- -- There can be a total of three copies of the same coordinates (if the intersection happens at a corner).
- local rem1, rem2
- for k,v in ipairs(points) do
- if points[i].x == v.x and points[i].y == v.y and points[i].z == v.z then
- if rem1 then
- 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.
- else
- rem1 = i
- end
- end
- end
- -- Remove the two keys.
- if rem1 or rem2 then
- if rem1 then
- table.remove(points, rem1)
- elseif rem2 then
- table.remove(points, rem2)
- end
- else
- i = i + 1 -- Increment i only if an entry has not been deleted.
- end
- end
- end
- return points
- end
- function circleintersect(circle, pointA, pointB)
- -- Change x, y and z depending on orientation.
- local x, y, z
- if circle.o == "z" then
- x, y, z = "x", "y", "z"
- elseif circle.o == "x" then
- x, y, z = "z", "y", "x"
- elseif circle.o == "y" then
- x, y, z = "x", "z", "y"
- end
- local points = {}
- -- Find the time "t" when the line intersects the plane at which the circle is oriented.
- local tz = (circle[z] - pointA[z]) / (pointA[z] + pointB[z])
- local xtz = pointA[x] * (1 - tz) + tz * pointB[x]
- local ytz = pointA[y] * (1 - tz) + tz * pointB[y]
- -- Find the distance from this intersection point to the center of the circle and compare it against the radius.
- local dist = math.sqrt((xtz - circle[x]) ^ 2 + (ytz - circle[y]) ^ 2)
- if dist <= circle.r then
- table.insert(points, {[x] = xtz, [y] = ytz, [z] = circle[z]})
- end
- return points
- end
- function rectangleintersect(rectangle, pointA, pointB)
- -- Change x, y and z depending on orientation.
- local x, y, z
- if rectangle.o == "z" then
- x, y, z = "x", "y", "z"
- elseif rectangle.o == "x" then
- x, y, z = "z", "y", "x"
- elseif rectangle.o == "y" then
- x, y, z = "x", "z", "y"
- end
- local points = {}
- -- Find the time "t" when the line intersects the plane at which the rectangle is oriented.
- local tz = (rectangle[z] - pointA[z]) / (pointA[z] + pointB[z])
- local xtz = pointA[x] * (1 - tz) + tz * pointB[x]
- local ytz = pointA[y] * (1 - tz) + tz * pointB[y]
- -- Find if the intersection point is within the bounds of the rectangle.
- if xtz >= rectangle[x .. "low"] and xtz <= rectangle[x .. "high"] and ytz >= rectangle[y .. "low"] and ytz <= rectangle[y .. "high"] then
- table.insert(points, {[x] = xtz, [y] = ytz, [z] = rectangle[z]})
- end
- return points
- end
- -- Object Creation Comments (refer to this function for questions on how the other ones work)
- function GEO.newSphere(name, r, x, y, z)
- -- Check to see if there is already a GEO with this name.
- if not GEO[name] then
- -- Create new table
- GEO[name] = {}
- GEO[name].t = "sphere" -- type
- GEO[name].n = name -- name
- GEO[name].r = r or 1 -- radius (default value of 1)
- GEO[name].x = x or 0 -- x coordinate of center (default value of 0)
- GEO[name].y = y or 0 -- y coordinate of center (default value of 0)
- GEO[name].z = z or 0 -- z coordinate of center (default value of 0)
- -- Initialize monitor and contain tables
- GEO[name].m = {}
- GEO[name].c = {}
- -- Initialize list of antigeos
- GEO[name].anti = {}
- -- Notify the console that a sphere has been created.
- hprintf("Sphere \"" .. name .. "\" created.")
- setmetatable(GEO[name], GEO) -- Add this object to the GEO metatable to allow GEO-editing functions to work on it.
- return GEO[name] -- Return the object
- end
- -- If no object was returned, the name was invalid; notify the console.
- hprintf("Invalid name: \"" .. name .. "\"")
- end
- function GEO.newCircle(name, r, x, y, z, orientation)
- if not GEO[name] then
- GEO[name] = {}
- GEO[name].t = "circle"
- GEO[name].n = name
- GEO[name].o = orientation or "z" -- orientation
- GEO[name].r = r or 0
- GEO[name].x = x or 0
- GEO[name].y = y or 0
- GEO[name].z = z or 0
- -- Initialize monitor and contain tables
- GEO[name].m = {}
- GEO[name].c = {}
- -- Initialize list of antigeos
- GEO[name].anti = {}
- hprintf("Circle \"" .. name .. "\" created.")
- setmetatable(GEO[name], GEO)
- return GEO[name]
- end
- hprintf("Invalid name: \"" .. name .. "\"")
- end
- function GEO.newCylinder(name, r, h, x, y, z, orientation)
- if not GEO[name] then
- GEO[name] = {}
- GEO[name].t = "cylinder"
- GEO[name].n = name
- x = x or 0
- y = y or 0
- z = z or 0
- r = r or 1
- h = h or 1
- orientation = orientation or "z"
- GEO[name].x = x
- GEO[name].y = y
- GEO[name].z = z
- GEO[name].r = r
- GEO[name].h = h -- height
- GEO[name].o = orientation -- orientation "x", "y", or "z"
- GEO[name].hlow = GEO[name][orientation] - h / 2 -- lowest orientation coordinate still within the cylinder
- GEO[name].hhigh = GEO[name][orientation] + h / 2 -- highest orientation coordinate still within the cylinder
- -- Initialize monitor and contain tables
- GEO[name].m = {}
- GEO[name].c = {}
- -- Initialize list of antigeos
- GEO[name].anti = {}
- hprintf("Cylinder \"" .. name .. "\" created.")
- setmetatable(GEO[name], GEO)
- return GEO[name]
- end
- end
- function GEO.newRectPrism(name, lx, ly, lz, x, y, z)
- if not GEO[name] then
- GEO[name] = {}
- GEO[name].t = "rectprism"
- GEO[name].n = name
- lx = lx or 1
- ly = ly or 1
- lz = lz or 1
- x = x or 0
- y = y or 0
- z = z or 0
- GEO[name].lx = lx -- x length
- GEO[name].ly = ly -- y length
- GEO[name].lz = lz -- z length
- GEO[name].x = x
- GEO[name].y = y
- GEO[name].z = z
- GEO[name].xlow = x - lx / 2 -- lowest x-coordinate still within the rectangular prism
- GEO[name].xhigh = lx / 2 + x -- highest x-coordinate still within in the rectangular prism
- GEO[name].ylow = y - ly / 2 -- lowest y-coordinate still within the rectangular prism
- GEO[name].yhigh = ly / 2 + y -- highest y-coordinate still within the rectangular prism
- GEO[name].zlow = z - lz / 2 -- lowest z-coordinate still within the rectangular prism
- GEO[name].zhigh = lz / 2 + z -- highest z-coordinate still within the rectangular prism
- -- Initialize monitor and contain tables
- GEO[name].m = {}
- GEO[name].c = {}
- -- Initialize list of antigeos
- GEO[name].anti = {}
- hprintf("Rectangular Prism \"" .. name .. "\" created.")
- setmetatable(GEO[name], GEO)
- return GEO[name]
- end
- hprintf("Invalid name: \"" .. name .. "\"")
- end
- function GEO.newRect(name, width, height, x, y, z, orientation)
- if not GEO[name] then
- GEO[name] = {}
- GEO[name].t = "rectangle"
- GEO[name].n = name
- width = width or 1
- height = height or 1
- x = x or 0
- y = y or 0
- z = z or 0
- orientation = orientation or "z"
- GEO[name].width = width
- GEO[name].height = height
- GEO[name].x = x
- GEO[name].y = y
- GEO[name].z = z
- GEO[name].o = orientation
- -- Coordinates' highs and lows depend on orientation
- if orientation == "z" then
- GEO[name].xlow = x - width / 2
- GEO[name].xhigh = x + width / 2
- GEO[name].ylow = y - height / 2
- GEO[name].yhigh = y + height / 2
- GEO[name].zlow = z
- GEO[name].zhigh = z
- elseif orientation == "x" then
- GEO[name].xlow = x
- GEO[name].xhigh = x
- GEO[name].ylow = y - width / 2
- GEO[name].yhigh = y + width / 2
- GEO[name].zlow = z - height / 2
- GEO[name].zhigh = z + height / 2
- elseif orientation == "y" then
- GEO[name].xlow = x - width / 2
- GEO[name].xhigh = x + width / 2
- GEO[name].ylow = y
- GEO[name].yhigh = y
- GEO[name].zlow = z - height / 2
- GEO[name].zhigh = z + height / 2
- end
- -- Initialize monitor and contain tables
- GEO[name].m = {}
- GEO[name].c = {}
- -- Initialize list of antigeos
- GEO[name].anti = {}
- hprintf("Rectangle \"" .. name .. "\" created.")
- setmetatable(GEO[name], GEO)
- return GEO[name]
- end
- hprintf("Invalid name: \"" .. name .. "\"")
- end
- function GEO.get(name)
- return GEO[name]
- end
- function GEO.clear()
- for k,v in pairs(GEO) do
- if type(v) == "table" and k ~= "__index" then -- make sure we're actually getting a GEO object
- v:delete() -- Delete every GEO
- end
- end
- end
- function GEO:delete()
- -- Get rid of perimeter and surface objects
- self:hide()
- -- Make sure any GEO that this is an AntiGEO of is aware that this GEO no longer exists
- for k,v in pairs(GEO) do
- if type(v) == "table" and k ~= "__index" then
- for name,bool in pairs(v.anti) do
- if name == self.n then
- GEO[v.n].anti[name] = nil
- break
- end
- end
- end
- end
- -- Nullify GEO
- GEO[self.n] = nil
- hprintf("Geo \"" .. self.n .. "\" deleted.")
- end
- function GEO:move(x, y, z)
- -- Move the center of the object
- -- Default to GEO's current coordinates
- GEO[self.n].x = x or GEO[self.n].x
- GEO[self.n].y = y or GEO[self.n].y
- GEO[self.n].z = z or GEO[self.n].z
- -- If this is a rectangular prism...
- if self.t == "rectprism" then
- -- Change the x, y, and z lows and highs accordingly to adjust to the new center
- GEO[self.n].xlow = x - GEO[self.n].lx / 2
- GEO[self.n].xhigh = GEO[self.n].lx / 2 + x
- GEO[self.n].ylow = y - GEO[self.n].ly / 2
- GEO[self.n].yhigh = GEO[self.n].ly / 2 + y
- GEO[self.n].zlow = z - GEO[self.n].lz / 2
- GEO[self.n].zhigh = GEO[self.n].lz / 2 + z
- -- If this is a rectangle...
- elseif self.t == "rectangle" then
- -- Change the x, y, and z lows and highs accordingly to adjust to the new center (depends on orientation)
- if self.o == "z" then
- GEO[self.n].xlow = self.x - self.width / 2
- GEO[self.n].xhigh = self.x + self.width / 2
- GEO[self.n].ylow = self.y - self.height / 2
- GEO[self.n].yhigh = self.y + self.height / 2
- GEO[self.n].zlow = self.z
- GEO[self.n].zhigh = self.z
- elseif self.o == "x" then
- GEO[self.n].xlow = self.x
- GEO[self.n].xhigh = self.x
- GEO[self.n].ylow = self.y - self.width / 2
- GEO[self.n].yhigh = self.y + self.width / 2
- GEO[self.n].zlow = self.z - self.height / 2
- GEO[self.n].zhigh = self.z + self.height / 2
- elseif self.o == "y" then
- GEO[self.n].xlow = self.x - self.width / 2
- GEO[self.n].xhigh = self.x + self.width / 2
- GEO[self.n].ylow = self.y
- GEO[self.n].yhigh = self.y
- GEO[self.n].zlow = self.z - self.height / 2
- GEO[self.n].zhigh = self.z + self.height / 2
- end
- -- If this is a cylinder...
- elseif self.t == "cylinder" then
- GEO[self.n].hlow = self[self.o] - self.h / 2
- GEO[self.n].hhigh = self[self.o] + self.h / 2
- end
- end
- function GEO:radius(new)
- if self.t == "sphere" or self.t == "circle" or self.t == "cylinder" then
- if new then -- If "new" is defined...
- GEO[self.n].r = new -- Change the radius to its value.
- else -- If not...
- return GEO[self.n].r -- Return its current radius.
- end
- end
- end
- function GEO:size(x, y, z)
- -- If this is a rectangular prism...
- if self.t == "rectprism" then
- if x or y or z then -- If any of these variables have been defined...
- -- Adjust lengths and x, y, and z highs and lows accordingly.
- GEO[self.n].lx = x or GEO[self.n].lx
- GEO[self.n].ly = y or GEO[self.n].ly
- GEO[self.n].lz = z or GEO[self.n].lz
- GEO[self.n].xlow = GEO[self.n].x - x / 2
- GEO[self.n].xhigh = x / 2 + GEO[self.n].x
- GEO[self.n].ylow = GEO[self.n].y - y / 2
- GEO[self.n].yhigh = y / 2 + GEO[self.n].y
- GEO[self.n].zlow = GEO[self.n].z - z / 2
- GEO[self.n].zhigh = z / 2 + GEO[self.n].z
- else -- Otherwise...
- return GEO[self.n].lx, GEO[self.n].ly, GEO[self.n].lz -- Return the x, y, and z lengths.
- end
- -- If this is a rectangle...
- elseif self.t == "rectangle" then
- if x or y or z then -- If any of these variables are defined...
- -- Adjust width, height, and x, y, and z highs and lows accordingly (depends on orientation).
- if self.o == "z" then
- GEO[self.n].width = x
- GEO[self.n].height = y
- GEO[self.n].xlow = self.x - self.width / 2
- GEO[self.n].xhigh = self.x + self.width / 2
- GEO[self.n].ylow = self.y - self.height / 2
- GEO[self.n].yhigh = self.y + self.height / 2
- GEO[self.n].zlow = self.z
- GEO[self.n].zhigh = self.z
- elseif self.o == "x" then
- GEO[self.n].width = y
- GEO[self.n].height = z
- GEO[self.n].xlow = self.x
- GEO[self.n].xhigh = self.x
- GEO[self.n].ylow = self.y - self.width / 2
- GEO[self.n].yhigh = self.y + self.width / 2
- GEO[self.n].zlow = self.z - self.height / 2
- GEO[self.n].zhigh = self.z + self.height / 2
- elseif self.o == "y" then
- GEO[self.n].width = x
- GEO[self.n].height = z
- GEO[self.n].xlow = self.x - self.width / 2
- GEO[self.n].xhigh = self.x + self.width / 2
- GEO[self.n].ylow = self.y
- GEO[self.n].yhigh = self.y
- GEO[self.n].zlow = self.z - self.height / 2
- GEO[self.n].zhigh = self.z + self.height / 2
- end
- else -- Otherwise...
- return GEO[self.n].width, GEO[self.n].height -- Return the width and height of the rectangle.
- end
- -- If this is a cylinder...
- elseif self.t == "cylinder" then
- local h = x or y or z -- Whichever variable is defined, it is taken as the height.
- if h then -- If a height is specified...
- -- Adjust height and z high and low accordingly.
- GEO[self.n].h = h
- GEO[self.n].hlow = self[self.o] - h / 2
- GEO[self.n].hhigh = self[self.o] + h / 2
- else -- Otherwise...
- return GEO[self.n].h -- Return the height.
- end
- end
- end
- function GEO:extend(orientation, direction, amount)
- -- Change the direction from "+" or "-" to "high" or "low".
- local dir
- dir = string.gsub(direction, "-", "low")
- dir = string.gsub(direction, "+", "high")
- -- Get the face we're trying to extend (i.e. "xhigh")
- local face = string.lower(orientation) .. direction
- -- If this is a rectangular prism or a rectangle...
- if self.t == "rectprism" or self.t == "rectangle" then
- -- Make sure "face" is actually a valid face (and not something like "cheesederp")
- if self[face] then
- -- 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.
- -- Change the length of the GEO in the orientation specified.
- GEO[self.n]["l" .. string.lower(orientation)] = self["l" .. string.lower(orientation)] + amount
- -- Figure out if the positive or negative face is being extended.
- if direction == "+" then
- GEO[self.n][face] = self[face] + amount
- GEO[self.n][string.lower(orientation)] = self[string.lower(orientation)] + amount / 2
- else
- GEO[self.n][face] = self[face] - amount
- GEO[self.n][string.lower(orientation)] = self[string.lower(orientation)] - amount / 2
- end
- end
- -- If this is a cylinder...
- elseif self.t == "cylinder" then
- -- The orientation must be the orientation of the cylinder
- if orientation == self.o then
- if self[face] then
- GEO[self.n].h = self.h + amount
- -- Figure out if the top or bottom face is being extended.
- if direction == "+" then
- GEO[self.n].hhigh = self.hhigh + amount
- GEO[self.n][self.o] = self[self.o] + amount / 2
- else
- GEO[self.n].hhigh = self.hhigh - amount
- GEO[self.n][self.o] = self[self.o] - amount / 2
- end
- end
- end
- end
- end
- function GEO:antigeo(boolean, ...)
- GEO[self.n].geolist = GEO[self.n].geolist or {}
- -- Get all GEOs from args.
- local geos = {...}
- if boolean == true then
- for _,geo in ipairs(geos) do
- local name = geo:name()
- -- Insert AntiGEO into the specified GEOs' AntiGEO table.
- GEO[name].anti[self.n] = true
- -- Insert GEO into list of AntiGEO's GEOs.
- GEO[self.n].geolist[name] = true
- end
- elseif boolean == false then
- for _,geo in ipairs(geos) do
- local name = geo:name()
- -- Remove AntiGEO from table.
- GEO[name].anti[self.n] = false
- -- Remove GEO from AntiGEO table.
- GEO[self.n].geolist[name] = false
- end
- elseif boolean == nil then
- if GEO[self.n].a == true then
- return true
- else
- return false
- end
- end
- local count = 0
- for k,v in pairs(GEO[self.n].geolist) do
- count = count + 1
- end
- if count > 0 then
- GEO[self.n].a = true
- else
- GEO[self.n].a = false
- end
- end
- function GEO:coords()
- return self.x, self.y, self.z
- end
- function GEO:high(orientation)
- if self.t == "sphere" or self.t == "circle" then
- return self[orientation] + self.r
- elseif self.t == "rectprism" or self.t == "rectangle" then
- return self[orientation .. "high"]
- elseif self.t == "cylinder" then
- if orientation == self.o then
- return self.hhigh
- else
- return self[orientation] + self.r
- end
- end
- end
- function GEO:low(orientation)
- if self.t == "sphere" or self.t == "circle" then
- return self[orientation] - self.r
- elseif self.t == "rectprism" or self.t == "rectangle" then
- return self[orientation .. "low"]
- elseif self.t == "cylinder" then
- if orientation == self.o then
- return self.hlow
- else
- return self[orientation] - self.r
- end
- end
- end
- function GEO:randomcoords()
- if self.t == "sphere" then
- return randomInSphere(self.x, self.y, self.z, self.r)
- elseif self.t == "circle" then
- return randomInCircle(self.x, self.y, self.z, self.r, self.o)
- elseif self.t == "cylinder" then
- return randomInCylinder(self.x, self.y, self.z, self.hlow, self.hhigh, self.r, self.o)
- elseif self.t == "rectprism" or self.t == "rectangle" then
- return randomInRectPrism(self.xlow, self.xhigh, self.ylow, self.yhigh, self.zlow, self.zhigh)
- end
- end
- function GEO:type()
- return self.t
- end
- function GEO:orientation()
- return self.o
- end
- function GEO:name()
- return self.n
- end
- function GEO:perimeter(density, mapId)
- -- Default density to 10
- density = density or 10
- -- Default tagtype and tagname to Full-Spectrum Visions
- mapId = mapId or gettagid("eqip", "powerups\\full-spectrum vision")
- tagname, tagtype = gettaginfo(mapId)
- -- Store all of the perimeter objects in a table
- GEO[self.n].p = GEO[self.n].p or {}
- -- Find the change in angle per point from 0 - 2pi (0° - 360°)
- local angle_increment = 2 * math.pi / density
- if self.t == "sphere" then
- for i = 1,density do
- -- 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).
- local x = self.r * math.cos(angle_increment * i)
- local y = self.r * math.sin(angle_increment * i)
- local z = self.z
- local objId = createobject(mapId, 0, nil, false, self.x + x, self.y + y, self.z)
- GEO[self.n].p[objId] = {self.x + x, self.y + y, self.z}
- end
- elseif self.t == "circle" or self.t == "cylinder" then
- if self.o == "z" then
- for i = 1,density do
- -- Use trigonometry to find the outer edge of the circle.
- local x = self.r * math.cos(angle_increment * i)
- local y = self.r * math.sin(angle_increment * i)
- local z = self.z
- local objId = createobject(mapId, 0, nil, false, self.x + x, self.y + y, self.z)
- GEO[self.n].p[objId] = {self.x + x, self.y + y, self.z}
- end
- elseif self.o == "x" then
- for i = 1,density do
- -- Use trigonometry to find the outer edge of the circle.
- local x = self.x
- local y = self.r * math.cos(angle_increment * i)
- local z = self.r * math.sin(angle_increment * i)
- local objId = createobject(mapId, 0, nil, false, self.x, self.y + y, self.z + z)
- GEO[self.n].p[objId] = {self.x, self.y + y, self.z}
- end
- elseif self.o == "y" then
- for i = 1,density do
- -- Use trigonometry to find the outer edge of the circle.
- local x = self.r * math.cos(angle_increment * i)
- local y = self.y
- local z = self.r * math.sin(angle_increment * i)
- hprintf("x: " .. (self.x + x) .. " y: " .. (self.y) .. " z: " .. (self.z + z))
- local objId = createobject(mapId, 0, nil, false, self.x + x, self.y, self.z + z)
- GEO[self.n].p[objId] = {self.x + x, self.y, self.z + z}
- end
- end
- elseif self.t == "rectprism" or self.t == "rectangle" then
- if self.t == "rectangle" then
- if self.o ~= "z" then return end
- end
- -- Create points at four corners of the rectangle
- local o1 = createobject(mapId, 0, nil, false, self.xhigh, self.yhigh, self.z)
- local o2 = createobject(mapId, 0, nil, false, self.xhigh, self.ylow, self.z)
- local o3 = createobject(mapId, 0, nil, false, self.xlow, self.yhigh, self.z)
- local o4 = createobject(mapId, 0, nil, false, self.xlow, self.ylow, self.z)
- GEO[self.n].p[o1] = {self.xhigh, self.yhigh, self.z}
- GEO[self.n].p[o2] = {self.xhigh, self.yhigh, self.z}
- GEO[self.n].p[o3] = {self.xhigh, self.yhigh, self.z}
- GEO[self.n].p[o4] = {self.xhigh, self.yhigh, self.z}
- for i = 1,density do
- local herp = createobject(mapId, 0, nil, false, self.xhigh - (i * self.lx / density), self.yhigh, self.z)
- local derp = createobject(mapId, 0, nil, false, self.xhigh, self.yhigh - (i * self.ly / density), self.z)
- local weee = createobject(mapId, 0, nil, false, self.xhigh - (i * self.lx / density), self.ylow, self.z)
- local cheese = createobject(mapId, 0, nil, false, self.xlow, self.ylow + (i * self.ly / density), self.z)
- GEO[self.n].p[herp] = {self.xhigh - (i * self.lx / density), self.yhigh, self.z}
- GEO[self.n].p[derp] = {self.xhigh, self.yhigh - (i * self.ly / density), self.z}
- GEO[self.n].p[weee] = {self.xhigh - (i * self.lx / density), self.ylow, self.z}
- GEO[self.n].p[cheese] = {self.xlow, self.ylow + (i * self.ly / density), self.z}
- end
- end
- for objId,_ in pairs(GEO[self.n].p) do
- writebit(getobject(objId) + 0x10, 5, true) -- Ignore pyhsics
- end
- -- Also show the surface or perimeter of AntiGEOs with Overshield powerups
- for name,bool in pairs(GEO[self.n].anti) do
- if bool then
- local geo = GEO.get(name)
- if geo:type() == "sphere" or geo:type() == "cylinder" or geo:type() == "rectprism" then
- geo:surface(density, gettagid("eqip", "powerups\\over shield"))
- else
- geo:perimeter(density, gettagid("eqip", "powerups\\over shield"))
- end
- end
- end
- end
- function GEO:surface(density, mapId)
- -- Default density to 12
- density = density or 12
- -- Default tagtype and tagname to Full-Spectrum Visions
- mapId = mapId or gettagid("eqip", "powerups\\full-spectrum vision")
- -- Store all perimeter objects in a table
- GEO[self.n].p = GEO[self.n].p or {}
- -- Store all of the objects we will create
- local objects = {}
- if self.t == "sphere" then
- -- Use concentric rings based on the density given to simulate a sphere
- local laterals = density
- local latdist = self.r * 2 / laterals
- local curz = self.z + self.r
- local r = 0
- for i = 1, laterals do
- if i <= math.floor(laterals / 2) then
- local d = i / math.floor(laterals / 2) * density
- -- Find the change in angle per point from 0 - 2pi (0° - 360°)
- local angle_increment = 2 * math.pi / density
- for j = 1, density do
- -- 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).
- local x = r * math.cos((angle_increment) * j)
- local y = r * math.sin((angle_increment) * j)
- local z = curz
- table.insert(objects, {self.x + x, self.y + y, z})
- end
- elseif i >= math.ceil(laterals / 2) then
- local d = math.floor(laterals / 2) / i * density
- -- Find the change in angle per point from 0 - 2pi (0° - 360°)
- local angle_increment = 2 * math.pi / density
- for j = 1, density do
- -- 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).
- local x = r * math.cos((angle_increment) * j)
- local y = r * math.sin((angle_increment) * j)
- local z = curz
- table.insert(objects, {self.x + x, self.y + y, z})
- end
- end
- -- Lower the lateral
- curz = curz - latdist
- local diff = curz - self.z
- local angle = math.acos(math.abs(diff) / self.r)
- -- Get the new radius at this sphere cap
- r = math.sin(angle) * self.r
- end
- elseif self.t == "cylinder" then
- -- Use multiple rings based on density given to simulate cylinder
- local laterals = math.ceil(density / 2)
- local latdist = self.h / laterals
- local curh = self[self.o] + (self.h / 2)
- for i = 0, laterals do
- if i == 0 or i == laterals then
- local rings = density / 2
- local inc = self.r / rings
- local radius = self.r - inc
- for x = 1, rings - 1 do
- for j = 1, density do
- if self.o == "z" then
- local angle_increment = 2 * math.pi / density
- local x = radius * math.cos(angle_increment * j)
- local y = radius * math.sin(angle_increment * j)
- table.insert(objects, {self.x + x, self.y + y, curh})
- elseif self.o == "x" then
- local y = radius * math.cos(angle_increment * j)
- local z = radius * math.sin(angle_increment * j)
- table.insert(objects, {curh, self.y + y, self.z + z})
- elseif self.o == "y" then
- local x = radius * math.cos(angle_increment * j)
- local z = radius * math.sin(angle_increment * j)
- table.insert(objects, {self.x + x, curh, self.z + z})
- end
- end
- radius = radius - inc
- end
- end
- for j = 1, density do
- -- Find the change in angle per point from 0 - 2pi (0° - 360°)
- local angle_increment = 2 * math.pi / density
- -- Use trigonometry to find the outer edge of the circle.
- local x, y, z
- if self.o == "z" then
- x = self.r * math.cos(angle_increment * j)
- y = self.r * math.sin(angle_increment * j)
- table.insert(objects, {self.x + x, self.y + y, curh})
- elseif self.o == "x" then
- y = self.r * math.cos(angle_increment * j)
- z = self.r * math.sin(angle_increment * j)
- table.insert(objects, {curh, self.y + y, self.z + z})
- elseif self.o == "y" then
- x = self.r * math.cos(angle_increment * j)
- z = self.r * math.sin(angle_increment * j)
- table.insert(objects, {self.x + x, curh, self.z + z})
- end
- end
- -- Lower the lateral
- curh = curh - latdist
- end
- elseif self.t == "rectprism" then
- local d = density / 2
- local x_inc = self.lx / d
- local y_inc = self.ly / d
- local z_inc = self.lz / d
- for z = 0, d do
- if z == 0 or z == d then
- for y = 0, d do
- for x = 0, d do
- local x_coord = self.xlow + (x * x_inc)
- local y_coord = self.ylow + (y * y_inc)
- local z_coord = self.zlow + (z * z_inc)
- table.insert(objects, {x_coord, y_coord, z_coord})
- end
- end
- else
- for y = 0, d do
- if y == 0 or y == d then
- for x = 0, d do
- local x_coord = self.xlow + (x * x_inc)
- local y_coord = self.ylow + (y * y_inc)
- local z_coord = self.zlow + (z * z_inc)
- table.insert(objects, {x_coord, y_coord, z_coord})
- end
- else
- local y_coord = self.ylow + (y * y_inc)
- local z_coord = self.zlow + (z * z_inc)
- table.insert(objects, {self.xlow, y_coord, z_coord})
- table.insert(objects, {self.xhigh, y_coord, z_coord})
- end
- end
- end
- end
- end
- local antiobjs = {}
- -- Also show the surface or perimeter of AntiGEOs with Overshield powerups
- for name,bool in pairs(GEO[self.n].anti) do
- if bool then
- local geo = GEO.get(name)
- if geo:type() == "sphere" or geo:type() == "cylinder" or geo:type() == "rectprism" then
- local a = geo:surface(density / 2, gettagid("eqip", "powerups\\over shield"))
- for k,v in ipairs(a) do
- table.insert(antiobjs, v)
- end
- else
- geo:perimeter(density / 2, gettagid("eqip", "powerups\\over shield"))
- end
- end
- end
- local objs = {}
- for k,v in ipairs(objects) do
- local objId = createobject(mapId, 0, nil, false, v[1], v[2], v[3])
- GEO[self.n].p[objId] = {v[1], v[2], v[3]}
- writebit(getobject(objId) + 0x10, 5, true) -- Ignore pyhsics
- table.insert(objs, objId)
- end
- return objs, antiobjs
- end
- function GEO:hide()
- if self.p then
- for k,v in pairs(GEO[self.n].p) do
- if getobject(k) then
- destroyobject(k)
- GEO[self.n].p[k] = nil
- end
- end
- end
- end
- function GEO:contains(objId)
- for k,v in pairs(GEO[self.n].anti) do
- if GEO[k]:contains(objId) then
- return false
- end
- end
- if self.t == "sphere" then
- return inSphere(objId, self.x, self.y, self.z, self.r)
- elseif self.t == "rectprism" or self.t == "rectangle" then
- return inRectPrism(objId, self.xlow, self.xhigh, self.ylow, self.yhigh, self.zlow, self.zhigh)
- elseif self.t == "circle" then
- return inCircle(objId, self.x, self.y, self.z, self.r, self.o)
- elseif self.t == "cylinder" then
- return inCylinder(objId, self.x, self.y, self.z, self.hlow, self.hhigh, self.r, self.o)
- end
- end
- function GEO:containspoint(point)
- local bool
- if GEO[self.n]:vectorintersect(point, point) then
- bool = true
- else
- bool = false
- end
- for k,v in pairs(GEO[self.n].anti) do
- if GEO[k]:containspoint(point) then
- bool = false
- end
- end
- return bool
- end
- function GEO:vectorintersect(pointA, pointB)
- local tempA = {x = pointA[1], y = pointA[2], z = pointA[3]}
- local tempB = {x = pointB[1], y = pointB[2], z = pointB[3]}
- pointA, pointB = tempA, tempB
- local points
- -- Call the appropriate function depending on the type of GEO.
- if self.t == "sphere" then
- points = sphereintersect(self, pointA, pointB)
- elseif self.t == "cylinder" then
- points = cylinderintersect(self, pointA, pointB)
- elseif self.t == "rectprism" then
- points = rectprismintersect(self, pointA, pointB)
- elseif self.t == "circle" then
- points = circleintersect(self, pointA, pointB)
- elseif self.t == "rect" then
- points = rectangleintersect(self, pointA, pointB)
- end
- -- Return points as 2 variables where point = {x, y, z}.
- local point1, point2
- if points[1] then
- point1 = {points[1].x, points[1].y, points[1].z}
- end
- if points[2] then
- point2 = {points[2].x, points[2].y, points[2].z}
- end
- -- Check if the points of intersection are within AntiGEOs.
- for k,v in pairs(GEO[self.n].anti) do
- if GEO[k]:containspoint(point1) then
- point1 = nil
- point2 = point1
- end
- if GEO[k]:containspoint(point2) then
- point2 = nil
- end
- end
- return point1, point2
- end
- function GEO:randcoord()
- if self.t == "sphere" then
- return randomInSphere(self.x, self.y, self.z, self.r)
- elseif self.t == "circle" then
- return randomInCircle(self.x, self.y, self.z, self.r, self.o)
- elseif self.t == "cylinder" then
- return randomInCylinder(self.x, self.y, self.z, self.hlow, self.hhigh, self.r, self.o)
- elseif self.t == "rectprism" or self.t == "rect" then
- return randomInRectPrism(self.xlow, self.xhigh, self.ylow, self.yhigh, self.zlow, self.zhigh)
- end
- end
- function GEO:follow(objId)
- -- If the objId exists...
- if getobject(objId) then
- GEO[self.n].f = objId -- Save it (the GEOTimer will access it)
- -- Check to see if the object is a player
- if objectidtoplayer(objId) then
- GEO[self.n].player = true
- end
- end
- end
- function GEO:unfollow()
- -- Nullify the saved objId from GEO:follow()
- GEO[self.n].f = nil
- end
- function GEO.followedBy(objId)
- -- Initialize table
- local geos = {}
- -- Loop through the GEO table
- for k,v in pairs(GEO) do
- if type(v) == "table" and k ~= "__index" then -- make sure we're actually getting a GEO object
- if v.f == objId then
- -- If this GEO has this objId saved, insert it into the geos table.
- table.insert(geos, v)
- end
- end
- end
- -- Return the GEOs following objId in a table.
- return geos
- end
- function GEO.cleanup(player)
- local m_player = getplayer(player)
- local objId = readdword(m_player, 0x34)
- local geos = GEO.followedBy(objId)
- for k,v in ipairs(geos) do
- v:delete()
- end
- end
- function GEO:velocity(x, y, z)
- GEO[self.n].vx = x or GEO[self.n].vx
- GEO[self.n].vy = y or GEO[self.n].vy
- GEO[self.n].vz = z or GEO[self.n].vz
- end
- function GEO:killzone(bool, delay, kill_message, func, ...)
- if bool == true then
- GEO[self.n].kz = true
- elseif bool == false then
- GEO[self.n].kz = false
- elseif bool == nil then
- return GEO[self.n].kz or false
- end
- GEO[self.n].kzdelay = delay
- GEO[self.n].kzmessage = kill_message
- GEO[self.n].kztime = {}
- GEO[self.n].kzfunc = func
- GEO[self.n].kzargs = {...}
- end
- function GEO:damagezone(bool, damage, delay, damage_message, func, ...)
- if bool == true then
- GEO[self.n].damage = damage or 1 -- Amount of damage applied per second.
- elseif bool == false then
- GEO[self.n].damage = nil
- elseif bool == nil then -- If nothing is passed, return true if this GEO is a damagezone, false if not.
- if GEO[self.n].damage then
- return true
- else
- return false
- end
- end
- GEO[self.n].dmgdelay = delay
- GEO[self.n].dmgmessage = damage_message
- GEO[self.n].dmgmsgsent = {}
- GEO[self.n].dmgtime = {}
- GEO[self.n].dmgfunc = func
- GEO[self.n].dmgargs = {...}
- end
- function GEO:face(orientation, direction)
- -- If this is a rectangular prism...
- if self.t == "rectprism" then
- orientation = orientation or "z"
- direction = direction or "+"
- if orientation == "z" then
- local width = self.lx
- local height = self.ly
- local highlow
- if direction == "+" then
- highlow = self.zhigh
- else
- highlow = self.zlow
- end
- -- Create a new rectangle which overlays the specified face and return that rectangle.
- return GEO.newRect(self.n .. "ZFace" .. os.time(), width, height, self.x, self.y, highlow, orientation)
- elseif orientation == "x" then
- local width = self.ly
- local height = self.lz
- local highlow
- if direction == "+" then
- highlow = self.xhigh
- else
- highlow = self.xlow
- end
- return GEO.newRect(self.n .. "XFace" .. os.time(), width, height, highlow, self.y, self.z, orientation)
- elseif orientation == "y" then
- local width = self.lx
- local height = self.lz
- local highlow
- if direction == "+" then
- highlow = self.yhigh
- else
- highlow = self.ylow
- end
- return GEO.newRect(self.n .. "YFace" .. os.time(), width, height, self.x, highlow, self.z, orientation)
- end
- -- If this is a cylinder...
- elseif self.t == "cylinder" then
- if orientation == self.o then
- local highlow
- if direction == "+" then
- highlow = self.hhigh
- else
- highlow = self.hlow
- end
- -- Return a new circle which overlays the specified face and return that circle.
- if orientation == "z" then
- return GEO.newCircle(self.n .. "ZFace" .. os.time(), self.r, self.x, self.y, highlow, self.o)
- elseif orientation == "x" then
- return GEO.newCircle(self.n .. "XFace" .. os.time(), self.r, highlow, self.y, self.z, self.o)
- elseif orientation == "y" then
- return GEO.newCircle(self.n .. "YFace" .. os.time(), self.r, self.x, highlow, self.z, self.o)
- end
- else
- hprintf("You may only retrieve the orientation face of a cylinder.")
- end
- end
- end
- function GEO:copy(name)
- name = name or self.n .. "Copy" .. os.time()
- if not GEO[name] then
- GEO[name] = self
- return GEO[name]
- end
- end
- function GEO:monitor(objId)
- if getobject(objId) then
- GEO[self.n].m[objId] = true
- monitored[objId] = true
- end
- end
- registertimer(10, "GEOTimer")
- pcoords = pcoords or {} -- Keeps track of previous coordinates of objects being monitored
- monitored = monitored or {} -- Keeps track of non-player objects being monitored
- function GEOTimer(id, count)
- -- Loop through the GEO table
- for k,v in pairs(GEO) do
- if type(v) == "table" and k ~= "__index" then
- -- If this GEO is following an object...
- if v.f then
- -- Get the coordinates of the object
- local x, y, z = getobjectcoords(v.f)
- if x then -- If this object exists...
- if v.player then
- local player = objectidtoplayer(v.f)
- if player then
- local m_player = getplayer(player) -- See if the player is still here
- if m_player then -- If they are...
- local time_until_respawn = readdword(m_player, 0x2C) -- Check to see if they're dead
- if time_until_respawn == 0 then -- If they're not...
- if v.p then -- If this GEO has perimeter objects...
- for objId, coords in pairs(v.p) do
- if getobject(objId) then
- local ox, oy, oz = table.unpack(coords)
- local x_diff = x - v.x
- local y_diff = y - v.y
- local z_diff = z - v.z
- local m_object = getobject(objId)
- movobjectcoords(objId, ox + x_diff, oy + y_diff, oz + z_diff) -- Move them with the GEO.
- GEO[v.n].p[objId] = {ox + x_diff, oy + y_diff, oz + z_diff}
- end
- end
- end
- v:move(x, y, z) -- Move the GEO to the player's coordinates
- else -- Otherwise...
- v:delete() -- Delete the GEO.
- end
- else -- Otherwise...
- v:delete() -- Delete the GEO.
- end
- else -- Otherwise...
- v:delete() -- Delete the GEO.
- end
- else -- Otherwise...
- if v.p then -- If this GEO has perimeter objects...
- for objId, coords in pairs(v.p) do
- if getobject(objId) then
- local ox, oy, oz = table.unpack(coords)
- local x_diff = x - v.x
- local y_diff = y - v.y
- local z_diff = z - v.z
- local m_object = getobject(objId)
- movobjectcoords(objId, ox + x_diff, oy + y_diff, oz + z_diff) -- Move them with the GEO.
- GEO[v.n].p[objId] = {ox + x_diff, oy + y_diff, oz + z_diff}
- end
- end
- end
- v:move(x, y, z) -- Don't worry about all of that player nonsense and just move the damn GEO.
- end
- else -- Otherwise...
- v:delete() -- Delete the GEO.
- end
- end
- -- If after all of that following nonsense, we still have a GEO...
- if v then
- -- If this GEO is a killzone...
- if v.kz then
- -- Check if anyone is inside of it.
- for i = 0,15 do
- local m_player = getplayer(i)
- if m_player then
- local objId = readdword(m_player, 0x34)
- if getobject(objId) then
- if v:contains(objId) then
- local allow = true
- -- If this killzone has a function...
- if v.kzfunc then
- -- If this function returns false...
- if not v.kzfunc(v, i, table.unpack(v.kzargs)) then
- allow = false
- end
- end
- -- If the function allows this player to be killed by this GEO...
- if allow then
- -- If this killzone kills on a delay...
- if v.kzdelay then
- GEO[k].kztime[i] = (v.kztime[i] or 0) + (os.time() - (lasttime or os.time()))
- if GEO[k].kztime[i] >= GEO[k].kzdelay then
- kill(i) -- Time's up.
- if GEO[k].kzmessage then
- privatesay(i, v.kzmessage)
- end
- GEO[k].kztime[i] = 0
- end
- else
- kill(i) -- Kill that ho.
- GEO[k].kztime[i] = 0
- end
- end
- end
- else
- GEO[k].kztime[i] = 0
- end
- end
- end
- end
- -- If this GEO is a damagezone...
- if v.damage then
- -- Check if anyone is inside of it.
- for i = 0,15 do
- local m_player = getplayer(i)
- if m_player then
- local objId = readdword(m_player, 0x34)
- if getobject(objId) then
- if v:contains(objId) then
- local allow = true
- -- If this damagezone has a function...
- if v.dmgfunc then
- -- If this function returns false...
- if not v.dmgfunc(v, i, table.unpack(v.dmgargs)) then
- allow = false
- end
- end
- -- If the function allows this player to be damaged by this GEO...
- if allow then
- -- If this damagezone is on a delay...
- if v.dmgdelay then
- GEO[k].dmgtime[i] = (v.dmgtime[i] or 0) + (os.time() - (lasttime or os.time()))
- if GEO[k].dmgtime[i] >= GEO[k].dmgdelay then
- applydmg(objId, v.damage / 100) -- Apply damage
- -- Check to see if the player has already received the message
- if not GEO[k].dmgmsgsent[i] then
- privatesay(i, v.dmgmessage)
- GEO[k].dmgmsgsent[i] = true
- end
- end
- else
- applydmg(objId, v.damage / 100) -- Apply damage
- end
- end
- else
- GEO[k].dmgtime[i] = 0
- GEO[k].dmgmsgsent[i] = nil
- end
- end
- end
- end
- end
- -- If this GEO is monitoring for objects entering and exiting it...
- if v.m then
- -- Loop through the table of objects this GEO is monitoring for.
- for objId,_ in pairs(v.m) do
- -- If this object still exists...
- if getobject(objId) then
- -- If this object is inside of the GEO...
- if v:contains(objId) then
- local player = objectidtoplayer(objId)
- -- If the GEO didn't know this object was inside of it 1/100 of a second ago...
- if not v.c[objId] then
- -- Call OnGeoEnter.
- local allow = OnGeoEnter(v, player, objId)
- if allow == 0 or allow == false then
- if pcoords[objId] then
- movobjectcoords(objId, table.unpack(pcoords[objId]))
- end
- else
- GEO[k].c[objId] = true
- end
- end
- else -- Otherwise...
- -- If the GEO thought this object was inside of it 1/100 of a second ago...
- if v.c[objId] then
- local player = objectidtoplayer(objId)
- -- Call OnGeoExit.
- local allow = OnGeoExit(v, player, objId)
- if allow == 0 or allow == false then
- if pcoords[objId] then
- movobjectcoords(objId, table.unpack(pcoords[objId]))
- end
- else
- GEO[k].c[objId] = nil
- end
- end
- end
- else -- Otherwise...
- GEO[k].m[objId] = nil -- Stop monitoring for this object.
- end
- end
- end
- -- Automatically monitor for players
- -- Loop through every player to check if they are in this GEO.
- for i = 0, 15 do
- local m_player = getplayer(i)
- -- If this player exists...
- if m_player then
- local objId = readdword(m_player, 0x34)
- -- If this player is alive...
- if getobject(objId) then
- -- If this object is inside of the GEO...
- if v:contains(objId) then
- local player = objectidtoplayer(objId)
- -- If the GEO didn't know this object was inside of it 1/100 of a second ago...
- if not v.c[objId] then
- -- Call OnGeoEnter.
- local allow = OnGeoEnter(v, player, objId)
- if allow == 0 or allow == false then
- if pcoords[objId] then
- movobjectcoords(objId, table.unpack(pcoords[objId]))
- end
- else
- GEO[k].c[objId] = true
- end
- end
- else -- Otherwise...
- -- If the GEO thought this object was inside of it 1/100 of a second ago...
- if v.c[objId] then
- -- Call OnGeoExit.
- local allow = OnGeoExit(v, i, objId)
- if allow == 0 or allow == false then
- if pcoords[objId] then
- movobjectcoords(objId, table.unpack(pcoords[objId]))
- end
- else
- GEO[k].c[objId] = nil
- end
- end
- end
- end
- end
- end
- -- If this GEO has a velocity...
- if v.vx or v.vy or v.vz then
- if v.p then -- If this GEO has perimeter objects...
- for objId, coords in pairs(v.p) do
- if getobject(objId) then
- local ox, oy, oz = table.unpack(coords)
- local m_object = getobject(objId)
- movobjectcoords(objId, ox + (v.vx or 0), oy + (v.vy or 0), oz + (v.vz or 0)) -- Move them with the GEO.
- GEO[v.n].p[objId] = {ox + (v.vx or 0), oy + (v.vy or 0), oz + (v.vz or 0)}
- end
- end
- end
- -- Move that ho.
- v:move(v.x + (v.vx or 0) / 100, v.y + (v.vy or 0) / 100, v.z + (v.vz or 0) / 100)
- end
- end
- end
- end
- -- 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)
- if count % 25 == 0 then
- -- Each player
- for i = 0,15 do
- local m_player = getplayer(i)
- if m_player then
- local objId = readdword(m_player, 0x34)
- if getobject(objId) then
- pcoords[objId] = {getobjectcoords(objId)}
- else
- pcoords[objId] = nil
- end
- end
- end
- -- Each object being monitored
- for objId,_ in pairs(monitored) do
- if getobject(objId) then
- pcoords[objId] = {getobjectcoords(objId)}
- else
- pcoords[objId] = nil
- monitored[objId] = nil
- end
- end
- end
- lasttime = os.time() -- Keeps track of the os time the GEOTimer last executed
- return true
- end
Recent Pastes
Raw Paste