Author Topic: Creating Arena Hazards  (Read 4554 times)

Offline Trovaner

  • *
  • Posts: 1222
  • Rep: 32
    • View Profile
    • Awards
Creating Arena Hazards
« on: June 14, 2015, 08:07:06 PM »
Since the subject of arena hazards is pretty vast, I'll try to break this up into two categories: effects and objects. However, we'll also need to go over a few of the basics before we jump into those.

Table of Contents

Basics
In the Arena.py file, the lines starting with "def" are called methods and the methods that you will be concerning yourself with are __init__, Activate, HazardsOn, Tick, and ZoneEvent.
  • The __init__ method is called before anything else in the class. This is called before the arena has been created from the GMF or any bots have been added so it is not to be used to check on either of them.
  • The Activate method is called after the initial countdown with the "on" variable set to True and after the battle ends with the "on" variable set to False.
  • The HazardsOn method is called after the Arena.gmf has been fully loaded (IIRC) and has an "on" variable that says if hazards have been enabled. This method is often used for setting up things. For example, if you want bots to drive around hazards, you'll want to make calls to self.AddCollisionLine and self.AddPOV (which I have not done in any of my examples just because I wanted my examples to be brief and easy to read). If you want bots to be aware of hazards in certain areas, you'll want to make calls to AddHazard (which, as far as I can remember, adds the hazard to a list that is just used by the self.GetNearestHazard method in Arena/__init__.py's SuperArena class).
  • The Tick method is called approximately 4 times per second (unless you change it to something else). This method is commonly used for checking for state changes (like bot position) and responding accordingly.
  • The ZoneEvent method is called when a bot is inside of a zone object that has been registered. Similar to the smartzones that we often use for AI bots, zones are objects that we use for triggering things. This method is often used for hammers, flippers, hellraizers, flame jets, etc. In order for this method to be called, you will need to register objects that bots can pass-through as being zones (by calling the RegisterZone method).
If you do not have one of these and decide that you need to use it, here are all five of them in their most basic forms for easy copy/pasting.
Code: [Select]
    def __init__(self):
        Arenas.SuperArena.__init__(self, "path/to/arena.gmf")
        # Don't forget to replace the above Arena.gmf path
        # Your early setup code goes here

    def Activate(self, on):
        # Your bot-related setup/shutdown code goes here
        return Arenas.SuperArena.Activate(self, on)
   
    def HazardsOn(self, on):
        # Your hazard setup code goes here
        return Arenas.SuperArena.HazardsOn(self, on)

    def Tick(self):
        # Your repeating code goes here
        return Arenas.SuperArena.Tick(self)

    def ZoneEvent(self, direction, id, robot, chassis):
        # Your event code goes here
        return True
In Python, everything after the number sign ("#") is a comment unless it is wrapped in quotes so those lines can be completely replaced by your code. The first line under __init__ is basically telling RA2 to call the parent's __init__ method with teh given Arena.gmf. Similarly, the "return" statements found at the bottom of the next three methods are telling RA2 to run the parent's similarly named method (just Google "python inheritance" if you would like to learn more). The last method is sending "True" back to the sender to indicate that the zone event has been handled (you could potentially do "return False" but, for all intents and purposes, we won't). If you don't have one of these defined in your Arena.py, it will just call the parent's equivalent so it would be like having the above code without adding any of your own.

For the sake of simplicity, all of my examples will assume that "import plus" and "import Arenas" are in the top section of the Arena.py next to the others. Any other imports will be noted below the code.

Offline Trovaner

  • *
  • Posts: 1222
  • Rep: 32
    • View Profile
    • Awards
Re: Creating Arena Hazards
« Reply #1 on: June 14, 2015, 08:07:25 PM »
Effects
In this category, we'll go over the common, hazard-related things that don't require GMF objects to function (unless we choose to have a zone for triggering them).

OOTAs
I'll list this for completeness and so that you can compare and contrast it with other hazards.
Code: (OOTA Example) [Select]
    def __init__(self):
        Arenas.SuperArena.__init__(self, "path/to/arena.gmf")
        # Don't forget to replace the above Arena.gmf path
        self.players = ()

    def Activate(self, on):
        if on:
            self.players = plus.getPlayers()
       
        Arenas.SuperArena.Activate(self, on)

    def Tick(self):
        for player in self.players:
            if plus.getLocation(player)[1] < -5:
                plus.eliminatePlayer(player)
        return Arenas.SuperArena.Tick(self)
In the above code, we set self.players equal to an empty array (you can think of an array as a list that can't be modified) so that it doesn't crash when RA2 calls the Tick method gets called before the Activate method. In the Activate method, we assign a new array of players to our self.players variable. In the Tick method, we loop through each of the players, check to see if they are located below -5, and eliminate them if they are.

If you would like to see a couple examples, flextop.py and G_antweight.py both use something like this.

Pits
Similar to OOTAs, we can eliminate or deliver damage to bots that fall below some height but let's try a different implementation.
Code: (Pit Example) [Select]
    def HazardsOn(self, on):
        self.RegisterZone("pit_zone", 1)
        return Arenas.SuperArena.HazardsOn(self, on)

    def ZoneEvent(self, direction, id, robot, chassis):
        if id == 1 and chassis:
            plus.elimatePlayer(robot - 1)
        return True
This time around, we are assigning the "pit_zone" object found in the Arena.gmf to be a zone and eliminating any bot that has a chassis that is touching it.

Usually, we do the same thing as we do for OOTA for pits but I was able to remember one example of an arena that did it similar to this. In DSLCaveArena.py, damage is delivered to any bot that falls into the pit but, since ZoneEvent doesn't get called unless there is a change, you only get damaged once (I would consider this broken). If we wanted to fix it, we would have to have something in the Tick method or use a command that only has to be called once.

Fire
The fire particle effect can be accomplished in one line of code but, in order to have the sound effect and damage, you are going to need a lot more. That said, both Infogrames and the DSL team have already programmed a decent flamethrower inside of Hazards.py and DSLHaz.py that we can use. When I did a quick scan of the differences between the two, I found that DSL is using a slightly quieter sound effect with slightly more damage per tick. For my example, I'll use the one found in stock RA2.
Code: (Fire Example) [Select]
    def HazardsOn(self, on):
        if on:
            self.flame1 = Hazards.Flame((0, 0, 0), (0, 6, 0), (.2, .4, .2), .25)
            self.AddHazard(self.flame1)
        return Arenas.SuperArena.HazardsOn(self, on)

    def Tick(self):
        if self.bHazardsOn:
            self.flame1.Tick()
        return Arenas.SuperArena.Tick(self)
If you were to stick this in an arena, you would see a flamethrower shooting up from the middle of the arena and driving into it delivers damage. If you wanted to use the DSL version, you can just replace "Hazards.Flame" with "DSLHaz.Flame" and add "import DSLHaz" to the top section.

You will also need "import Hazards" at the top of the file by all the other imports or it will crash.

Just for reference, here are the commands specific to flames.
Code: (Fire Commands) [Select]
#plus.AddParticleEmitter(XYZ_StartingLocation, XYZ_EndingLocation, XYZ_Variance) - Creates a ParticleEmitter object that looks like fire. While emitting, the fire moves in the designated direction with some leeway if variance isn't set to (0,0,0) (each value in variance represents the amount of leeway the smoke will have in the X, Y, and Z directions)
# .SetEmittting(Enabled) - Turns a ParticleEmitter object on/off (True/False). By default, the fire will not be emitting particles.

#import Hazards
#Hazards.Flame(XYZ_Location, XYZ_Velocity, XYZ_Variance, yOffset) - Create a Flame object that extends from Hazards.Hazard.
# .Tick() - This should be called once per tick.
# .ZoneEvent(Direction) - This should be called every time a bot enters a fire zone.
# .FlameOff() - Turns off the Flame object's fire, damage, and sound effect.
# .FlameOn() - Turns on the Flame object's fire, damage, and sound effect.

A good example of this can be found in Bridge.py. If you are just looking for something cosmetic, the Epic Showdown Arena (bbhaz.py) has some really neat torches.

Electricity
Similar to fire, electricity doesn't actually have any sound effect or damage so we would need to include that in our implementation. Unlike fire, the implementation in Hazards does not include the lightning itself (just the zap effect that you see when you drive over a grate in the Electric Arena). For this reason, I'll start by showing you how to use the electric grates and then we'll go over how to use lightning.

Code: (Electric Grates Example) [Select]
    def HazardsOn(self, on):
        if on:
            self.RegisterZone("elec_zone", 1)
            self.electricity = Hazards.Electricity()
            self.AddHazard(self.electricity)
        return Arenas.SuperArena.HazardsOn(self, on)

    def Tick(self):
        if self.bHazardsOn:
            self.electricity.Tick()
        return Arenas.SuperArena.Tick(self)

    def ZoneEvent(self, direction, id, robot, chassis):
        if id == 1:
            self.electricity.ZoneEvent(direction, robot, chassis)
        return True
This will shock any bots that enter into a zone when it isn't recharging.

Once again, don't forget to add "import Hazards" to the top section of the Arena.py if it isn't already there.

Code: (Lightning Example) [Select]
    def HazardsOn(self, on):
        self.CreateLightning(0, (0, 40, 0), (0, 0, 0))
        self.SetLightningVisible(0)
        return Arenas.SuperArena.HazardsOn(self, on)

Just for reference, here are the commands specific to electricity.
Code: (Electricity Commands) [Select]
#self.CreateLightning(LightningID, XYZ_StartingLocation, XYZ_EndingLocation) - Create lightning with the given ID and set the position of both ends.
#self.SetLightningStartEnd(LightningID, XYZ_StartingLocation, XYZ_EndingLocation) - Change the position of both ends of the specified lightning id.
#self.SetLightningVisible(LightningID, Showing) - Set the given lightning ID's visibility.
#plus.zap(BotID, AmountOfSparks, Duration) - Surround the given bot's chassis with harmless sparks

#import Hazards
#Hazards.Electricity(XYZ_Location) - Creates an Electricity object that extends from Hazards.Hazard.
# .Tick() - This should be called once per tick.
# .Zap() - Call this if you want to zap any bot that is currently in a zone.
# .Button() - Call this if you want to zap any bot that is currently in a zone.
# .NumBotsInRange() - Returns the number of bots that would get shocked if you were to call the Zap method now.
# .ZoneEvent(Direction, Robot, Chassis) - This should be called every time a bot enters a zone that should zap them.

See electric.py for a simple example of using both lightning and electric grates.

Miscellaneous
Here are a few other commands that you may find useful when creating effect hazards.
Code: (Misc Commands) [Select]
#plus.emitSmoke(Intensity, XYZ_StartingLocation, XYZ_Direction, XYZ_Variance) - Creates smoke that moves in the designated direction with some leeway if variance isn't set to (0,0,0) (each value in variance represents the amount of leeway the smoke will have in the X, Y, and Z directions).

#plus.force(BotID, X_Direction, Y_Direction, Z_Direction) - Applies a linear force onto the designated bot in the designated direction every time it is called.
#plus.gravity(X_Direction, Y_Direction, Z_Direction) - Applies a linear force on all bots and objects in the arena in the designated direction every time the physics update.
#plus.damage(BotID, ComponentID, Damage, XYZ_Coordinates) - Inflicts the specified amount of damage to the specified component on the given bot. The given coordinates are used for positioning the damage delivered.
#plus.disable(BotID,True/False) - Turns the controls on or off for the designated bot (1 is off, 0 is on) (the designated bot may not be killed unless they are enabled first).
#plus.addPoints(BotID, Points) - Increase the score of a selected bot by a selected number of points.
#plus.eliminatePlayer(BotID) - Eliminates the designated bot ID.

#plus.areHazardsOn() - Returns 1 if hazards are enabled (1 is the same as True and 0 is the same as False).
#plus.isDefeated(BotID) - Returns 1 if the bot has been defeated (1 is the same as True and 0 is the same as False).
#plus.isEliminated(BotID) - Returns 1 if the bot has been eliminated (1 is the same as True and 0 is the same as False).
#plus.isMatchOver() - Returns 1 if match is over (1 is the same as True and 0 is the same as False).
#plus.isMatchPaused() - Returns 1 if the Pause/Break button is activated (1 is the same as True and 0 is the same as False).
#plus.rayTest(XYZ_StartingLocation, XYZ_EndingLocation) - If something is between the two points it will return the BotID (-1 if it is not a bot), the component ID (0 if chassis), the X_Coordinate of intersection, the Y_Coordinate of intersection, and the Z_Coordinate of intersection; otherwise it will return -1 for all five values if it doesn't hit anything. If it hits something that isn't a bot, the first two values are -1 while the rest are the XYZ of the point of intersection.

Offline Trovaner

  • *
  • Posts: 1222
  • Rep: 32
    • View Profile
    • Awards
Re: Creating Arena Hazards
« Reply #2 on: June 14, 2015, 08:07:36 PM »
Objects
Basically, this category is comprised of things like flippers, loose objects, compressors, etc. For the sake of simplicity, we are going to break this up into four groups based on how they move: static, loose, linear, and angular. We'll also only be looking at the *GMID_HAVOK_RBCOLLECTION and *GMID_HAVOK_CONSTRAINTSOLVER sections of the GMF since the rest doesn't really matter as long as you have the objects named the same as me.

Static
Just in case it wasn't already obvious, this group is made up of things that don't move. Things like spikes, walls, bouncy floors, inverted control floors, etc. fall into this group.

Here is a simple example of a non-moving spike.
Code: (Static Spike GMF Example) [Select]
*GMID_HAVOK_RBCOLLECTION
{
*NODE_NAME RBCollection01
*NUM_DISABLED_PAIRS 0
*SOLVER_TYPE 0
*COUNT 1
*GMID_HAVOK_RIGIDBODY_LIST
{
*COUNT 1
*GMID_HAVOK_RIGIDBODY
{
*NODE_NAME spike
*MASS 0
*ELASTICITY 0.300000
*FRICTION 0.500000
*OPTIMIZATION_LEVEL 0.500000
*UNYIELDING 0
*SIMULATION_GEOMETRY 3
*GEOMETRY_PROXY_NAME null
*USE_DISPLAY_PROXY 0
*DISABLE_COLLISIONS 0
*INACTIVE 0
*DISPLAY_PROXY_NAME (null)
*NODE_TM
{
*NODE_NAME spike
*TM_ROW0 1.000000 0.000000 0.000000
*TM_ROW1 0.000000 1.000000 0.000000
*TM_ROW2 0.000000 0.000000 1.000000
*TM_ROW3 0.000000 0.000000 0.000000
}
*HAVOK_GEO_TYPE Standard
*NUMBER_OF_CHILDREN 0
}
}
}
This can be part of the Arena.gmf or be in its own file (like the ramps that get loaded into the Practice Arena). The reason why it doesn't move has to do with the fact that it weighs 0kg. Alternatively, you can assign a weight to it and set it *UNYIELDING equal to 1 (which means true).

Alright, so what would you do if you wanted it to play a sound and deliver more damage?
Code: (Static Spike PY Example) [Select]
    def HazardsOn(self, on):
        if on:
            self.SetSubMaterialSound("spike", "metal", .8, "Sounds\\hzd_spike_hit.wav")
        return Arenas.SuperArena.HazardsOn(self, on)
The key piece here is that we are telling the game that the spike object should be treated like metal and 0.8 damage. The sound effect is played whenever the spike hits something. If we set the material to rubber, there wouldn't be any damage delivered but we would still be able to play the sound effect. I should also note that the below commands are used by all the different object groups but they are especially important for static hazards since they have no movement of their own.

Just for reference, here are the commands specific to object hazards that you can use for more than just the static objects.
Code: (Object Commands) [Select]
#self.AddXtra(ObjectName, GMF, Alloy)
#self.AddXtraSound(ObjectName, GMF, Alloy, SoundFile)
#self.SetSubMaterial(ObjectName, Alloy, DamageValue)
#self.SetSubMaterialSound(ObjectName, Alloy, DamageValue, SoundFile)

Loose
Things like cones, barrels, and cinderblocks fall into this group. The only real difference between a static object and a loose object is that the loose object has mass and unyielding set to false.
Code: (Loose Crate GMF Example) [Select]
*GMID_HAVOK_RBCOLLECTION
{
*NODE_NAME RBCollection01
*NUM_DISABLED_PAIRS 0
*SOLVER_TYPE 0
*COUNT 1
*GMID_HAVOK_RIGIDBODY_LIST
{
*COUNT 1
*GMID_HAVOK_RIGIDBODY
{
*NODE_NAME crate
*MASS 25
*ELASTICITY 0.300000
*FRICTION 0.500000
*OPTIMIZATION_LEVEL 0.500000
*UNYIELDING 0
*SIMULATION_GEOMETRY 3
*GEOMETRY_PROXY_NAME null
*USE_DISPLAY_PROXY 0
*DISABLE_COLLISIONS 0
*INACTIVE 0
*DISPLAY_PROXY_NAME (null)
*NODE_TM
{
*NODE_NAME crate
*TM_ROW0 1.000000 0.000000 0.000000
*TM_ROW1 0.000000 1.000000 0.000000
*TM_ROW2 0.000000 0.000000 1.000000
*TM_ROW3 0.000000 0.000000 0.000000
}
*HAVOK_GEO_TYPE Standard
*NUMBER_OF_CHILDREN 0
}
}
}

There are two gotchas that you should be aware of:
  • Objects added to the Arena.gmf have to be enabled before physics start applying to them. You'll need something like this in your Arena.py:
Code: (Loose Crate PY Example) [Select]
    def HazardsOn(self, on):
        if on:
            self.SetActive("crate", 1)
        return Arenas.SuperArena.HazardsOn(self, on)
  • Objects added using one of the AddXtra commands can not be activated until a movable object bumps into them. This is why the cinderblocks don't fall over until you bump into them with your bot in the Practice Arena.

Linear
This group is made up of things like compactors, pit covers, lifts, rivers, etc. Many of these types of things have been coded inside of Hazards.py. You just have to make sure that the moving part is what we defined as loose in the previous group.

Code: (Prismatic Spike PY Example) [Select]
    def HazardsOn(self, on):
        if on:
            prism = self.AddPrismatic("base", "spike", -0.432429,0.791208,0.432429, 0, 1.1, 0)
            self.spike = Hazards.Spikes(prism, 40000, (5.85, 0, -5.87)
            self.AddHazard(spike)
        return Arenas.SuperArena.HazardsOn(self, on)

    def Tick(self):
        if self.bHazardsOn:
            self.spike.Tick()
        return Arenas.SuperArena.Tick(self)

    def ZoneEvent(self, direction, id, robot, chassis):
        if id == 0:
            self.spike.ZoneEvent(direction)
        return True
The "base" (parent object) is the part that "spike" (child object) is moving relative to. Usually we use an object that is static for the parent and something that it loose for the child but they could both be loose if you wanted it to move around (like if it was on a GMF housebot).

A good example of the above can be found in king.py.

I should probably also point out that RA2 is using the word "prismatic" as if it is synonymous with "actuator" (perhaps they were thinking of "pneumatic"...). Correct me if I'm wrong but I don't think that this is accurate terminology.

Here are all of the commands specific to prismatics.
Code: (Prismatic Commands) [Select]
#self.AddPrismatic(ParentObjectName, ChildObjectName, X_Direction, Y_Direction, Z_Direction, MovementLimit1, MovementLimit2, 0) - Creates a PrismaticController object.
# .ApplyForce(Amount) - Applies the given amount of linear force.
# .Lock(Enabled) - Determines whether the actuating part is movable.
# .SetAutoLock(Enabled) - Determines whether the prismatic automatically locks when it is at its longest and shortest length.
# .SetDirection(Amount) - Sets the direction of movement and the amount of strength.
# .SetPowerSettings(Amount1, Amount2) - Sets the amount of force and strength of the PrismaticController object.

#import Hazards
#Hazards.Spikes(Prismatic, Power, XYZ_Location) - Creates a Spikes object that extends from Hazards.Hazard.
# .Tick() - This should be called once per tick.
# .ZoneEvent(Direction) - This should be called every time a bot enters a zone that should zap them.
# .FireTeeth() - You can call this if you just want to fire the actuator.
#Hazards.Saws(Prismatic, XYZ_Location) - Creates a Saws object that extends from Hazards.Hazard.
# .ZoneEvent(Direction) - This should be called every time a bot enters a zone that should zap them.
# .MoveSaw(Amount) - Sets the direction of movement for the PrismaticController object and the amount of strength.
#Hazards.PitSlider(Prismatic, Delay, XYZ_Location) - Creates a PitSlider object that extends from Hazards.Hazard.
# .Tick() - This should be called once per tick.
# .setactive(Enabled) - Starts/Stops the timer for moving the slider.

Angular
This group is made up of everything from hammers to spinning blades.

Code: (Spinning Saw GMF Example) [Select]
*GMID_HAVOK_CONSTRAINTSOLVER
{
*NODE_NAME CSolver01
*THRESHOLD 10
*RB_COLLECTION_NAME RBCollection01
*GMID_HAVOK_CONSTRAINT_LIST
{
*COUNT 1
*GMID_HAVOK_HINGE_CONSTRAINT
{
*NODE_NAME Hinge01
*NODE_TM
{
*NODE_NAME Hinge01
*TM_ROW0 1 0 0
*TM_ROW1 0 1 0
*TM_ROW2 0 0 1
*TM_ROW3 0 0 0
}
*BODY1 base
*BODY2 saw
*POINT 0 0 0
*SPIN_AXIS 0 1 0
*IS_LIMITED 0
*FRICTION 0
*ANGLE_LIMITS -0.8 0
}
}
}
Basically, the above code is saying that our child object ("saw") is going to be moving around the Y axis of the parent object ("base"). We also set *IS_LIMITED to false so it can move 360 degrees around the spin axis. Once again, the parent is usually set to a static object and the child is always set to something loose but you could have the parent be loose as well. Another thing worth noting is that we called our hinge constraint "Hinge01" so we'll be using that when we finally apply some torque.

With just the above and no python code, you have the equivalent to an axle mount component. If we need it to spin on its own, you'll need to have something like the following in your Arena.py. Since Hazards.py doesn't include a hazard for continuously spinning an object, I'll code one of those.
Code: (Spinning Saw PY Example) [Select]
    def HazardsOn(self, on):
        if on:
            self.hinge = self.GetHinge("Hinge01")
            self.hinge.SetAutoLocks(False, False)
            self.hinge.Lock(False)
        return Arenas.SuperArena.HazardsOn(self, on)

    def Tick(self):
        if self.bHazardsOn:
            self.hinge.SetPowerSettings(12, 150000)
            self.hinge.SetDirection(100)
            self.hinge.ApplyTorque(10)
        return Arenas.SuperArena.Tick(self)
In the above, we're basically telling RA2 that our hinge should rotate and not be locked at any point in time.

If you wanted to create a something with a limited swing, you would just set *IS_LIMITED to true (1) and adjust the *ANGLE_LIMITS accordingly. You would also need to add code for going back and forth instead of in one direction unless you take advantage of gravity or use one of the many pre-coded options found in Hazards.py.

Last, but not least, here are all of the hinge commands that I could dig up.
Code: (Hinge Commands) [Select]
#self.GetHinge(HingeName) - Creates a HingeController object.
# .Lock(Enable) - Determines whether the hinge is locked or not.
# .SetAutoLocks(Enable1, Enable2) - Determines whether the hinge locks when it reaches its limits.
# .ApplyTorque(Amount) - Applies the given amount of rotational force.
# .SetDirection(Amount) - Sets the direction of movement and the amount of strength.
# .SetPowerSettings(Amount1, Amount2) - Sets the amount of force and strength of the PrismaticController object.
# .SetLimits(Amount1, Amount2) - Programmatic alternative to setting the rotational limits inside of the GMF.

#import Hazards
#Hazards.HellRaiser(Hinge, XYZ_Location) - Creates a HellRaiser object that extends from Hazards.Hazard.
# .Tick() - This should be called once per tick.
# .RaiseHell() - Rotates the hinge in the positive direction with a sound effect and a high amount of force.
# .LowerHell() - Rotates the hinge in the negative direction with a low amount of force.
#Hazards.Hammer(Hinge, XYZ_Location) - Creates a Hammer object that extends from Hazards.Hazard.
# .Tick() - This should be called once per tick.
# .ZoneEvent(Direction) - This should be called every time a bot enters a hammer zone.
# .Smash() - Rotates the hinge in the positive direction with a sound effect and a high amount of force.
# .Retract() - Rotates the hinge in the negative direction with a low amount of force.
# .Shutdown() - Sets all hinge values to zero.
#Hazards.Smasher(Hinge1, Hinge2, XYZ_Location) - Creates a Smasher object that extends from Hazards.Hazard.
# .Tick() - This should be called once per tick.
# .ZoneEvent(Direction, Robot) - This should be called every time a bot enters a compactor zone.
# .Smash() - Rotates both hinges in the positive direction with a high level of force.
# .Retract() - Rotates both hings in the negative direction with a low level of force.
# .Shutdown() - Sets all hinge values to zero.
#Hazards.TrapDoor(Hinge, XYZ_Location) - Creates a TrapDoor object that extends from Hazards.Hazard.
# .Trigger() - Activates the trap door.

Offline Trovaner

  • *
  • Posts: 1222
  • Rep: 32
    • View Profile
    • Awards
Re: Creating Arena Hazards
« Reply #3 on: June 14, 2015, 08:10:47 PM »
This is still pretty rough around the edges but I'll try to improve on it over time...

Feel free to leave your comments/questions here.

Offline Jaydee99

  • Waltuhweight
  • *
  • Posts: 1938
  • Rep: -23
  • :/
    • View Profile
    • Awards
  • See profile for gamer tags: Yes
Re: Creating Arena Hazards
« Reply #4 on: June 17, 2015, 10:05:04 AM »
Sticky this please? And thanks for the guide.

Offline cephalopod

Re: Creating Arena Hazards
« Reply #5 on: June 17, 2015, 10:11:11 AM »
The guide has been added to the Tutorial Index, no need to sticky unless we stickied every other tutorial too :P
Very handy though, thanks Trov - looking forward to helping script some stuff with this.
bristol bot builders / two headed death flamingo / snappy robots
//
kindest and friendliest '13, '15, '16, '17 / favourite staff member '14, '15

Offline Badnik96

  • tired of your shit
  • *
  • Posts: 17537
  • Rep: 3
    • Badnik96GTM
  • Awards BOTM Winner
    • View Profile
    • BattleBots Wiki
    • Awards
  • See profile for gamer tags: Yes
  • Skype: Badnik96
Re: Creating Arena Hazards
« Reply #6 on: June 19, 2015, 06:22:40 AM »
Dude this is awesome. Thanks for doing this.

Offline HereticBlue

  • *
  • Posts: 447
  • Rep: 22
  • Team Immersion Robotics
    • View Profile
    • Team Immersion
    • Awards
Re: Creating Arena Hazards
« Reply #7 on: June 25, 2015, 07:46:26 AM »
Well 3 of us working on the mod have tried adding the flames, electricity, working flippers and loose objects and it hasn't worked at all.

Don't know where to go from here. It feels as though you've left some information unexplained.
Team Immersion - Vulture, Amnesia, Amnesia 2, Halcyon, Hysteria and many commissions!

https://m.facebook.com/teamimmersionrobotics/

Offline HereticBlue

  • *
  • Posts: 447
  • Rep: 22
  • Team Immersion Robotics
    • View Profile
    • Team Immersion
    • Awards
Re: Creating Arena Hazards
« Reply #8 on: October 08, 2015, 06:55:45 AM »
Fire
The fire particle effect can be accomplished in one line of code but, in order to have the sound effect and damage, you are going to need a lot more. That said, both Infogrames and the DSL team have already programmed a decent flamethrower inside of Hazards.py and DSLHaz.py that we can use. When I did a quick scan of the differences between the two, I found that DSL is using a slightly quieter sound effect with slightly more damage per tick. For my example, I'll use the one found in stock RA2.
Code: (Fire Example) [Select]
    def HazardsOn(self, on):
        if on:
            self.flame1 = Hazards.Flame((0, 0, 0), (0, 6, 0), (.2, .4, .2), .25)
            self.AddHazard(self.flame1)
        return Arenas.SuperArena.HazardsOn(self, on)

    def Tick(self):
        if self.bHazardsOn:
            self.flame1.Tick()
        return Arenas.SuperArena.Tick(self)
If you were to stick this in an arena, you would see a flamethrower shooting up from the middle of the arena and driving into it delivers damage. If you wanted to use the DSL version, you can just replace "Hazards.Flame" with "DSLHaz.Flame" and add "import DSLHaz" to the top section.

You will also need "import Hazards" at the top of the file by all the other imports or it will crash.

Just for reference, here are the commands specific to flames.
Code: (Fire Commands) [Select]
#plus.AddParticleEmitter(XYZ_StartingLocation, XYZ_EndingLocation, XYZ_Variance) - Creates a ParticleEmitter object that looks like fire. While emitting, the fire moves in the designated direction with some leeway if variance isn't set to (0,0,0) (each value in variance represents the amount of leeway the smoke will have in the X, Y, and Z directions)
# .SetEmittting(Enabled) - Turns a ParticleEmitter object on/off (True/False). By default, the fire will not be emitting particles.

#import Hazards
#Hazards.Flame(XYZ_Location, XYZ_Velocity, XYZ_Variance, yOffset) - Create a Flame object that extends from Hazards.Hazard.
# .Tick() - This should be called once per tick.
# .ZoneEvent(Direction) - This should be called every time a bot enters a fire zone.
# .FlameOff() - Turns off the Flame object's fire, damage, and sound effect.
# .FlameOn() - Turns on the Flame object's fire, damage, and sound effect.

A good example of this can be found in Bridge.py. If you are just looking for something cosmetic, the Epic Showdown Arena (bbhaz.py) has some really neat torches.

When we do this, the arena just doesn't appear in the game anymore or the flames are nonexistant. Been struggling for months now.
Team Immersion - Vulture, Amnesia, Amnesia 2, Halcyon, Hysteria and many commissions!

https://m.facebook.com/teamimmersionrobotics/