• Welcome to Smashboards, the world's largest Super Smash Brothers community! Over 250,000 Smash Bros. fans from around the world have come to discuss these great games in over 19 million posts!

    You are currently viewing our boards as a visitor. Click here to sign up right now and start on your path in the Smash community!

Project: TUSSLE - An infinite Smash Bros with any characters you can imagine!

digiholic

Smash Ace
Joined
Nov 13, 2009
Messages
678
Location
Albuquerque, New Mexico
NNID
digiholic
What is Project TUSSLE?

Project TUSSLE is an in-beta open-source fighting engine meant to emulate platform fighting games, such as Super Smash Bros. The game itself is a series of frameworks and editors to allow users to create their own stages, fighters, and modes. TUSSLE is meant to be accessible to non-programmers to quickly make their favorite characters, while still allowing experienced creators to make characters that push the engine to its absolute limit.

As the game goes further in development, and the player base grows, TUSSLE will become more than just an engine, it will serve as a community hub of creative works, to build a game where anything can happen. Future endeavors include an online module repository, a community-driven character rating and balancing system, and tournaments.

Features
  • Simple tools for building any character you can imagine! Take sprites from video game classics or draw up your own, then get to creating!

  • Customize your game however you see fit, the entire physics engine is at your control! Enable wavedashing, change shield stun, tweak gravity, enable tripping WHY?!?,then save and share your presets with others!

  • Save and watch replays to step up your game, show off a slick KO, or heavily modify your characters and watch them break in hilarious ways!

  • Use your own collection of music in-game, with options to adjust song frequency, and even set up playlists instead of loops for stages or game sessions!

  • FULLY COMPATIBLE WITH GAMECUBE CONTROLLERS! (Assuming you have a PC-compatible Gamecube controller adapter)

  • Fully open source, and freely modifiable! Take the engine and modify it into a classical beat-em-up, create a more traditional fighting game experience, or create sprawling single-player adventure stages!

  • And to top it all off, TUSSLE is, and will always be, FREE!
Technical Details

TUSSLE is made from the ground-up in Python. It's cross-platform, and characters created in Windows will work on Mac or Linux, and vice versa. Right now, this means that TUSSLE is limited to 2D sprites and environments, although, once completed, I do intend to create a TUSSLE 2 of sorts in Unity that would allow for 3D fighters (but that's a long ways off)

The characters work as, essentially, a large finite state machine. Each action that can be taken, such as walking, attacking, or rolling, is a list of instructions to be played out on specific frames of the action or when a trigger is reached. This allows for a very versatile character that can have as many complex animations and states as you want them to. If you want a character to do something abnormal, you can create your own version of a basic action for the character, without having to modify the physics engine and potentially breaking other characters. If you don't need anything different? Just put some animations on top of the base actions and call it a day. Everything is implemented for you, but fully modifiable if it does not suit your needs.

The code is open source and freely available on GitHub. If you're an experienced programmer and want to make a change, feel free to fork your own distribution or submit a pull request. TUSSLE is a game for everyone, by everyone.

Download the game here!
 
Last edited:

Eugene Wang

Smash Apprentice
Joined
Jan 11, 2016
Messages
148
Ooh, nice. I was thinking of doing something similar from the ground up, but you did it first, so I'll join you.

Now I'll get to work with a bunch of open-source mascots. Starting with Tux...
 

schmooblidon

Smash Journeyman
Joined
Feb 18, 2014
Messages
496
This is a really awesome project! I'd really like to join the team, I'd say probably content development. I have a very strong understanding of the physics and mechanics of smash. I'm not the best coder right now, I have a good understanding of oop, but still learning all the tools and how they work. I mostly program in javascript and do web stuff on ikneedata but I also have a little experience in c#, where I briefly started a smash clone with unity, and a little of python where I made some melee physics calculations for a thread about movement options.

I would definitely say that melee mechanics understanding is my strongest area, so at the very least I can explain the ideas of how things work and relate to code, but I can also write some stuff (perhaps not the cleanest and efficient as a professional) or learn how to.
 

digiholic

Smash Ace
Joined
Nov 13, 2009
Messages
678
Location
Albuquerque, New Mexico
NNID
digiholic
Website is up! Follow the dev blog at http://projecttussle.com or keep up to date with code updates as they happen by following @TUSSLEdev on Twitter!

Directory of Blog Posts:

8/26/16 - Event-uality <-New!
8/19/16 - Control Freak
8/12/16 - Articulate!
8/05/16 - Minor Text Fixes
7/29/16 - Project TUSSLE - More Stable Than Ever!
7/15/16 - Building a Better Builder
7/08/16 - Fighter Modification is up and Running!
7/01/16 - Getting There
6/17/16 - Visual Builder Status: Existent
6/10/16 - Converting Characters
6/03/16 - Actions, Subactions, and You
5/27/16 - A Crash Course in Coursing Crashes
5/13/16 - Actionable Updates
5/06/16 - Easier Characters
4/29/16 - The Future of Project TUSSLE
4/22/16 - Project Tussle enters Open Beta!
4/15/16 - Say Hello to Hitboxie('s Completed Moveset)!
4/08/16 - A Sense of Control
4/01/16 - WARNING! Challenger Approaching!
3/25/16 - The Duckling
3/18/16 - An Old Friend Returns!
3/11/16 - Smashing Stages!
3/03/16 - Lights, Camera, ACTIONS!
2/26/16 - A Primer on Characters
2/19/16 - Say Hello to Hitboxie!
2/19/16 - What is Project: TUSSLE?
 
Last edited:
Joined
Oct 12, 2015
Messages
514
Location
New Jersey
This sounds really cool. You need a starting stage theme or a default character moveset/voicework, I can provide it.
(Hitboxie is fine.)
 
Last edited:

digiholic

Smash Ace
Joined
Nov 13, 2009
Messages
678
Location
Albuquerque, New Mexico
NNID
digiholic
This sounds really cool. You need a starting stage theme or a default character moveset/voicework, I can provide it.
(Hitboxie is fine.)
Actually, if you can do music and sounds, we're pretty much missing just that to have a full team. Right now we're using some royalty free techno, but it'd be nice to have some original work. Shoot me a PM if you're interested.
 
Last edited:

GothicSlenderman

Smash Journeyman
Joined
Dec 31, 2012
Messages
291
I really like this idea, will it be simple to use? Also, let's say if someone wanted to make an official game with Tussle, would they be able to make their game and post it on something like Steam? or even consoles?
 

digiholic

Smash Ace
Joined
Nov 13, 2009
Messages
678
Location
Albuquerque, New Mexico
NNID
digiholic
Feature Friday - A primer on characters



Welcome, Tusslers, to another Feature Friday! Today, we’re going to look at a character, and what it takes to build them. First thing’s first, let’s take a quick look at the anatomy of a character.





First thing’s first, your characters are going to be folders inside of the fighters directory in the game (that fancy icon just means “a folder with an __init__.py file in it”). Let’s go down the list. The sprites folder is where you should put all of your character sprites, which we’ll go into more detail on in a bit. Below that is a file called __init__.py. This file is needed for the game to recognize your fighter as a Python object, but it’s actually completely empty! All you need is a file called __init__.py with nothing in it and the game takes care of the rest!

Below that is fighter.py. Unlike the rest of this list, fighter.py has to be named fighter.py. It’s the code that the program looks for to actually load your character, and it contains all of the relevant metagame information, such as their name, color palettes, CSS icons, etc.

The franchise icon is simply an image that represents the franchise the character is from. In this case, Hitboxie’s is a bit of our logo that represents the Legacy Editor, the (in progress!) character builder Hitboxie will be representing.

Let’s skip down to the end and take a look at hitboxie.py. This file is the character. This will be what is loaded in a game, and it will manage the character. Let’s take a look at the most important part, the initialization code.



The first line tells us that Hitboxie inherits from abstractFighter, a truly mammoth and horrifying bundle of Spaghetti code that, thankfully, you’ll probably never need to look at.

The initialization code is going to give you your playerNumber (player 1, player 2, etc) and the sprites you’re going to be using (in case we switched costumes in fighter.py, that’s why we’re not just loading them here). That var dictionary there is your stats. Hitboxie is meant to be a fairly standard character, if a bit on the floaty side of things. Here’s a breakdown of what the stats mean:

  • Weight - How resistant your character is to knockback. A character like Kirby would have a low weight, while someone like Bowser would have a high weight.
  • Gravity affects how fast your character falls, the higher the faster. Fox would have a high gravity, and Jigglypuff would have a low gravity.
  • Max Fall Speed is excactly what it says on the tin. At this speed, your character will stop accelerating and fall at this speed until they hit something. Or die. Or both. This is also the character’s fast fall speed.
  • MaxGroundSpeed is how fast your character can run (not dash). Captain Falcon’s maxGroundSpeed is pretty high, while Ganondorf’s would be low.
  • MaxAirSpeed is how fast you can move to the side in the air. Most characters are not as nimble in the air as they are on the ground, but some, like Jigglypuff, are much faster when they’re not tied to this earthen prison.
  • Friction is the rate at which a character comes to a stop when they are no longer moving. Lower friction means your character will slide more, like Luigi, while characters with high friction can stop on a dime.
  • Air Control is how much of an effect holding directional buttons will have on your horizontal speed in the air. Wario is the king of airControl, and would have a very high number here.
  • Jumps is, quite simply, how many air jumps you get. All characters have a ground jump, and most characters have 1 extra midair jump. If you wanted to make a floaty character like Kirby, or a flying character like Charizard, you could increase this number.
  • Jump Height is how high your grounded jump will take you. This affects your full hop height, and by default, short hops cover half this (although you can change that in the action if you’d like, but more on that another day)
  • Air Jump Height is how high your jumps take you in the air. Most characters will have an air jump comparable to their ground jump height, but some (like Falco) have one much higher than that, while others (Meta-Knight) have shorter air jumps to offset the fact that they have so many of them.
Next, we have the code that actually creates the abstractFighter object. You’ll pass along the playerNum and Sprites that you have been given already, followed by your characters name (that will show up in the results screen) and the vars you set before.

The very last line is just a fancy way of loading up the last file we haven’t talked about, hitboxie_actions.py. This file is the delicious medium-rare burger patty in our character sandwich. It’s right at the center of everything. It contains every move your character can take, from smashing to grabbing to walking to even just standing there menacingly. It’s so important, that it’s gonna get its own blog post! Next week! After that, you’ll be a certified TUSSLE character-understanderer. Also next time, we’ll cover Sprites, and how to make them juuuuust right so that the game will be able to load them quickly and accurately.

Until that time, keep fighting your way!

-digi
 

digiholic

Smash Ace
Joined
Nov 13, 2009
Messages
678
Location
Albuquerque, New Mexico
NNID
digiholic
Feature Friday - Lights, Camera, ACTIONS!



You’ve been waiting for it, and now I’m gonna go into detail about actions! Actions are the very core of a fighter in TUSSLE! Each tiny move your character can make is controlled by an action. Actions, in turn, control which actions are loaded and how to change when keys are pressed. The fighter object we discussed last week always has an action, even if that action is just kinda standing there. First off, we need to take a brief detour to explain what a state machine is, and how it applies to fighters.


The fighter always has an action. By default, the character is in the Neutral State. In this state, any input will produce a result. By pressing a movement key, you will then enter the Movement State, and will stay in this until you let go of the direction, or transition into Jump State or Attack State. (Or pivot, or shield, or damage, or grabbed, etc. but we’re keeping this simple for now). The important takeaway of this relation is that each state knows how to get to the other ones next to it, but don’t care about stuff that’s far away. The Movement State knows how to transition into Jump State, but doesn’t even have any idea what an Air Dodge even is.

Let’s delve into the anatomy of an action. As our example, let’s look at Hitboxie’s forward attack:



Starting at the top, we see that ForwardAttack inherits from the Action class (located in engine.action.Action). This defines the methods that this action then goes on to define. You’ll see some actions that instead inherit from baseActions.<something>, these are actions that basically work the same for everyone, such as walking, grabbing a ledge, or getting hit. Most of the functionality of these is in a library called baseActions in the engine folder. The only thing these actions tend to do is change sprites and hurtboxes accordingly and let the baseAction handle the moves.

The init function, like for every Python object, is called when the object is first created. This is where the action class sets its variables, like current frame, and length, which is given to it in the next line (this attack is 24 frames long.) SetUp is different from init in that it’s not called until this action is ready to be executed. The game might decide to pre-load an action before it’s actually ready, but these are things we don’t want to do until the action is really right at the gates. For the most part, you’ll be defining hitboxes and articles in this function. Hitboxes will be explained in more detail below.

The next (absolutely massive!) function is the update method. Update is the cornerstone of the entirety of TUSSLE! Each frame, every object is updated. The object knows what it’s doing last frame, and what new information has come in this frame, so it changes itself to what it should be doing this frame. Simple as that. On the very first frame (called frame 0 because programming), we stop our horizontal movement, and our horizontal acceleration, and change our sprite to the “fsmash” sprite (right now Hitboxie’s forward smash and forward tilt use the same sprite out of laziness, don’t tell anyone, okay?) For reference, here’s the sprite in question, annotated with frame numbers:



When it first changes, it stays on subimage zero (although, if you gave it a number after the sprite name, you could start at a different frame). All the way up until frame 14, all that changes is the subimage, every other frame (So Hitboxie renders at 30FPS, the rest of the game is 60 I swear!). On Frame 14, we ass our hitbox to our owner’s (actor’s) list of active hitboxes. This means the hitbox is out into the fight and ready to hurt some people. On frame 16, we kill off the hitbox, removing it from the list of active hitboxes, and letting it eventually get deleted. It also goes up one more subimage. After that, it starts cycling subimages every other frame, until it’s last frame, at which time, it notifies its owner to politely switch back to neutral. At the end of the if statement, don’t forget to increase your frame count! (Aside: some actions actually occasionally subtract frames from this count. For example, if you want your jab to repeat while you’re holding the attack button, you could subtract enough frames to do the attack over again.)

The last method here is the stateTransitions function. It is called every frame, right before the update function. This function is where we check for buttons and change actions if necessary. For convenience, there’s tons of stateTransitions defined in baseActions. In this case, we’re loading up the Neutral State (the same one that’s called when you’re doing nothing) after frame 20. This means that, although your move has four more frames before it ends, you can do anything you could do at that time a little early to interrupt the animation. These are called IASA (Interruptable as soon as) Frames. You don’t have to have an entire state transition here, so if you were making a move that can chain into another move, you could check for the button for that in this frame, and change into it if you want.

Now that we’ve covered how actions transition you from state to state, how they change sprite animations, and how they activate their hitboxes, let’s look at what makes a hitbox actually work.



First off, DamageHitbox inherits from Hitbox. This really doesn’t add much functionality, but it allows other kinds of Hitboxes (such as windboxes, grab boxes, etc) to be able to collide with hurtboxes. The init function takes a whole buttload of data, but once that’s done, so is your hitbox! It’s ready to load up and hit people. Let’s break down the init data:

  • Center - the spot where the center of the hitbox is located. This is relative to the center of the owner. So, giving a center value of [20,0] (like the forward tilt above) would mean that the center of the hitbox is 20 pixels in front of Hitboxie, and 0 pixels up or down. This is in the form of [x,y], with positive x being forward, negative being backward, positive y being downward, and negative y being upward
  • Size - This is the size of the hitbox. It is in the form of [width,height].
  • Owner - the fighter that controls the hitbox.
  • Damage - How much percent this hitbox will do
  • BaseKnockback - This is the flat amount of knockback the hitbox will deal if the opponent is at zero percent.
  • KnockbackGrowth - This is the amount that the knockback grows by depending on how high the opponent’s percentage is
  • Trajectory - the direction it flings the opponent. This is always relative to the direction the owner is facing, with zero being straight forward, 90 being straight up, and so on.
  • Hitstun - This is a multiplier applied to the hitstun of the move (which is normally calculated by the knockback), so some moves can cause more hitstun or less.
  • Hitbox_id - This is a way to “group” several hitboxes together so that they can’t all hit the opponent at once (like if you want a sweet spot and a sourspot, you wouldn’t want both to hit, would you?) If two hitboxes are given the same ID, they won’t stack, and the earlier one will take precedence.
  • Weight Influence - A ratio of how much the opponent’s weight factors into knockback. Some moves don’t care how fat you are, they move you just the same, and others really struggle to send heavyweight flying. This number is simply multiplied with the opponent’s weight before calculating knockback.
  • Shield Multiplier - A ratio of how much damage this attack does to shields. Normally, this is based on percentage dealt, but if you want a move to be effective at killing shields (Like a Marth Shield Breaker), you can increase this multiplier
The rest of the hitbox is all bookkeeping and a bit too involved to deal with in this already huge Feature Friday update, so we’ll skip it for now. Basically, it tells the fighter who got hit to kindly go fling himself off in a direction.

By the way, here’s the calculation for the knockback:

p = percentage currently on the fighter
d = the damage the attack does
w = the weight of the fighter, multiplied by the weight influence
s = the knockback growth
b = base knockback

totalKB = (((((p/10) + (p*d)/20) * (200/(w+100))*1.4) + 5) * s) + b


Well, that about wraps it up for this week. It’s been a huge week in development for TUSSLE, We’re rewriting all of the horrible, awful Spaghetti Code I started with into something a little bit more robust. If you’ve been following along with the updates on our Twitter Feed of live updates to the game, you’ll see just how many new things we’ve got this week! Projectiles, a shield overhaul, a hitbox overhaul, collision detection improvements, and a working launcher! This means that you don’t need to have Python to run the game any more! That’s right, Project TUSSLE is now fully playable without needing to be a nerdy computer programmer like we are! Simply launch main.exe and the game will load up whatever you have on disk! No need to wait for us to build a new exe file, if you download any of our updates from the GitHub, you’ll be ready to play the new changes by firing up ol’ main.exe

If you have trouble running the game, please don’t hesitate to send us a bug report on GitHub, or on our subreddit. We’d love to hear your feedback, and want to address any concerns as soon as possible.

Tune in next week for more exciting episodes of Dragonball Z Project TUSSLE development! Until then, keep fighting your way!

-digi
 

TheAmazingMemer

Smash Apprentice
Joined
Apr 18, 2015
Messages
75
Location
Who knows
NNID
Faves3000
3DS FC
4940-5681-4585
I keep getting this error every time I try and start a battle. I select someone on the character select screen, it says I got an error, and it shuts down, and I honestly don't know how to fix it.

Traceback (most recent call last):
File "main.py", line 36, in <module>
if __name__ == '__main__': main(True)
File "main.py", line 19, in main
menu.Menu()
File "menu\menu.py", line 39, in __init__
self.currentScreen.executeMenu(screen)
File "menu\menu.py", line 89, in executeMenu
retValue = menu.executeMenu(screen)
File "menu\menu.py", line 146, in executeMenu
status = RulesMenu(self.parent).executeMenu(screen)
File "menu\menu.py", line 328, in executeMenu
status = css.CSSScreen(gameRules)
File "menu\css.pyo", line 55, in __init__
File "menu\css.pyo", line 90, in getFightersFromPanels
File "C:\Documents and Settings\Memer\My Documents\Stuff\Games\TUSSLE\fighters\hitboxie\fighter.py", line 75, in getFighter
fighter = fight.Hitboxie(playerNum,sprites)
File "C:\Documents and Settings\Memer\My Documents\Stuff\Games\TUSSLE\fighters\hitboxie\hitboxie.py", line 32, in __init__
raise ValueError(os.path.normpath(os.path.join(os.path.dirname(__file__).replace('main.exe',''), 'hitboxie_actions.py')))
ValueError: C:\Documents and Settings\Memer\My Documents\Stuff\Games\TUSSLE\fighters\hitboxie\hitboxie_actions.py
 

digiholic

Smash Ace
Joined
Nov 13, 2009
Messages
678
Location
Albuquerque, New Mexico
NNID
digiholic
I keep getting this error every time I try and start a battle. I select someone on the character select screen, it says I got an error, and it shuts down, and I honestly don't know how to fix it.

Traceback (most recent call last):
File "main.py", line 36, in <module>
if __name__ == '__main__': main(True)
File "main.py", line 19, in main
menu.Menu()
File "menu\menu.py", line 39, in __init__
self.currentScreen.executeMenu(screen)
File "menu\menu.py", line 89, in executeMenu
retValue = menu.executeMenu(screen)
File "menu\menu.py", line 146, in executeMenu
status = RulesMenu(self.parent).executeMenu(screen)
File "menu\menu.py", line 328, in executeMenu
status = css.CSSScreen(gameRules)
File "menu\css.pyo", line 55, in __init__
File "menu\css.pyo", line 90, in getFightersFromPanels
File "C:\Documents and Settings\Memer\My Documents\Stuff\Games\TUSSLE\fighters\hitboxie\fighter.py", line 75, in getFighter
fighter = fight.Hitboxie(playerNum,sprites)
File "C:\Documents and Settings\Memer\My Documents\Stuff\Games\TUSSLE\fighters\hitboxie\hitboxie.py", line 32, in __init__
raise ValueError(os.path.normpath(os.path.join(os.path.dirname(__file__).replace('main.exe',''), 'hitboxie_actions.py')))
ValueError: C:\Documents and Settings\Memer\My Documents\Stuff\Games\TUSSLE\fighters\hitboxie\hitboxie_actions.py
It looks like it's having trouble finding the hitboxie_actions.py file. Is that file located in the same directory as hitboxie.py? If it is, could you open it up in Notepad and let me see it?
 

TheAmazingMemer

Smash Apprentice
Joined
Apr 18, 2015
Messages
75
Location
Who knows
NNID
Faves3000
3DS FC
4940-5681-4585
It looks like it's having trouble finding the hitboxie_actions.py file. Is that file located in the same directory as hitboxie.py? If it is, could you open it up in Notepad and let me see it?
I've checked, yes they're in the same directory. Here's what I got from opening it up:

Code:
import engine.action as action
import engine.baseActions as baseActions
import engine.hitbox as hitbox
import engine.article as article
import engine.abstractFighter as abstractFighter
import math

import settingsManager #TEMPORARY until I figure out article sprites

class SplatArticle(article.AnimatedArticle):
    def __init__(self, owner, origin, direction):
        article.AnimatedArticle.__init__(self, settingsManager.createPath('sprites/hitboxie_projectile.png'), owner, origin, imageWidth=16,length=120)
        self.direction = direction
        self.change_y = 0
        self.hitbox = hitbox.DamageHitbox(self.rect.center, [12,12], self.owner, 6, 2, 0, 270, 1, hitbox.HitboxLock())
        self.hitbox.article = self
          
    # Override the onCollision of the hitbox
    def onCollision(self, other):
        othersClasses = map(lambda(x):x.__name__,other.__class__.__bases__) + [other.__class__.__name__]
        print othersClasses
        if 'AbstractFighter' in othersClasses or 'Platform' in othersClasses:
            self.hitbox.kill()
            self.kill()
        #TODO check for verticality of platform landing
          
          
    def update(self):
        self.rect.x += 24 * self.direction
        self.rect.y += self.change_y
        self.change_y += 0.5
        self.hitbox.rect.center = self.rect.center
        self.frame += 1
          
        if self.frame > 120:
            self.kill()
            self.hitbox.kill()

class NeutralGroundSpecial(action.Action):
    def __init__(self):
        action.Action.__init__(self,40)
              
    def setUp(self, actor):
        self.projectile = SplatArticle(actor,(actor.sprite.boundingRect.centerx + (32 * actor.facing), actor.sprite.boundingRect.centery), actor.facing)
        actor.change_x = 0
        actor.preferred_xspeed = 0
        actor.changeSprite("nspecial",0)
      
    def tearDown(self, actor, new):
        pass
  
    def stateTransitions(self, actor):
        pass
             
    def update(self, actor):
        actor.changeSpriteImage(math.floor(self.frame/4))
        if self.frame == 24:
            self.projectile.rect.center = (actor.sprite.boundingRect.centerx + (24 * actor.facing),actor.sprite.boundingRect.centery-8)
            actor.articles.add(self.projectile)
            actor.active_hitboxes.add(self.projectile.hitbox)
            print actor.active_hitboxes
          
        if self.frame == self.lastFrame:
            actor.doIdle()
        self.frame += 1

class NeutralAirSpecial(action.Action):
    def __init__(self):
        action.Action.__init__(self,40)
              
    def setUp(self, actor):
        self.projectile = SplatArticle(actor,(actor.sprite.boundingRect.centerx + (32 * actor.facing), actor.sprite.boundingRect.centery), actor.facing)
        actor.changeSprite("nspecial",0)


    def stateTransitions(self, actor):
        if actor.bufferContains('down'):
            if actor.change_y >= 0:
                actor.change_y = actor.var['maxFallSpeed']
        baseActions.airControl(actor)
      
    def tearDown(self, actor, new):
        pass
             
    def update(self, actor):
        actor.landingLag = 25
        actor.changeSpriteImage(math.floor(self.frame/4))
        if self.frame == 24:
            self.projectile.rect.center = (actor.sprite.boundingRect.centerx + (24 * actor.facing),actor.sprite.boundingRect.centery-8)
            actor.articles.add(self.projectile)
            actor.active_hitboxes.add(self.projectile.hitbox)
            print actor.active_hitboxes
            if actor.inputBuffer.contains('special', 10):
                actor.changeAction(NeutralAirSpecial())
          
        if self.frame == self.lastFrame:
            actor.landingLag = 12
            actor.changeAction(Fall())
        self.frame += 1

class ForwardSpecial(action.Action):
    def __init__(self):
        action.Action.__init__(self,44)

    def setUp(self, actor):
        actor.change_x = 0
        actor.preferred_xspeed = 0
        actor.changeSprite("nair",0)
      
         
class NeutralAttack(action.Action):
    def __init__(self):
        action.Action.__init__(self,22)
  
    def setUp(self, actor):
        actor.change_x = 0
        actor.preferred_xspeed = 0
        actor.changeSprite("neutral",0)
        self.jabHitbox = self.outwardHitbox(actor)
      
    def tearDown(self, actor, new):
        self.jabHitbox.kill()
  
    def stateTransitions(self, actor):
        if self.frame == self.lastFrame:
            if actor.keysContain('attack'):
                self.jabHitbox.hitbox_lock = hitbox.HitboxLock()
                self.frame = 0
              
    # Here's an example of creating an anonymous hitbox class.
    # This one calculates its trajectory based off of the angle between the two fighters.
    # Since this hitbox if specifically for this attack, we can hard code in the values.
    class outwardHitbox(hitbox.DamageHitbox):
        def __init__(self,actor):
            hitbox.DamageHitbox.__init__(self, [0,0], [80,80], actor, 2, 8, 0.02, 0, 1, hitbox.HitboxLock())
          
        def onCollision(self,other):
            self.trajectory = abstractFighter.getDirectionBetweenPoints(self.owner.rect.midbottom, other.rect.center)
            hitbox.DamageHitbox.onCollision(self, other)
          
    def update(self, actor):
        if self.frame < 9:
            actor.changeSpriteImage(self.frame)
        elif self.frame == 9:
            actor.active_hitboxes.add(self.jabHitbox)
        elif self.frame >= 10 and self.frame <= 13:
            actor.changeSpriteImage(9)
        elif self.frame > 13:
            self.jabHitbox.kill()
            if not (self.frame) > 18:
                actor.changeSpriteImage(self.frame - 4)
        if self.frame == self.lastFrame:
            actor.doIdle()
        self.frame += 1

class DashAttack(action.Action):
    def __init__(self):
        action.Action.__init__(self,32)

    def setUp(self, actor):
        actor.setSpeed(actor.change_x, actor.facing)
        actor.changeSprite("nair")

        self.dashHitbox = hitbox.DamageHitbox([0,0],[70,70],actor,2,10,0.07,20,1,hitbox.HitboxLock())
        self.chainHitbox = hitbox.AutolinkHitbox([0,0],[70,70],actor,2,1,hitbox.HitboxLock(),1,1.5)

    def tearDown(self,actor,other):
        self.dashHitbox.kill()
        self.chainHitbox.kill()
        actor.setSpeed(0, actor.facing)

    def update(self,actor):
        if self.frame%2 == 0 and self.frame <= 8:
            actor.changeSpriteImage(self.frame/2)
        elif self.frame <= 24:
            actor.changeSpriteImage((self.frame-4)%16)
        elif self.frame%2 == 0:
            actor.changeSpriteImage((self.frame/2-8)%16)

        self.dashHitbox.rect.center = [actor.rect.center[0], actor.rect.center[1]]
        self.chainHitbox.rect.center = [actor.rect.center[0], actor.rect.center[1]]

        if self.frame == 8:
            actor.active_hitboxes.add(self.chainHitbox)
        if self.frame == 12:
            self.chainHitbox.hitbox_lock = hitbox.HitboxLock()
        if self.frame == 16:
            self.chainHitbox.hitbox_lock = hitbox.HitboxLock()
        if self.frame == 20:
            self.chainHitbox.kill()
            actor.active_hitboxes.add(self.dashHitbox)
        if self.frame == 24:
            self.dashHitbox.kill()
            actor.setSpeed(0, actor.facing)

        if self.frame == self.lastFrame:
            actor.doIdle()
        self.frame += 1
      
class DownAttack(action.Action):
    def __init__(self):
        action.Action.__init__(self, 34)
  
    def setUp(self, actor):
        actor.change_x = 0
        actor.preferred_xspeed = 0
        actor.changeSprite("dsmash",0)
        hitbox_lock = hitbox.HitboxLock()
        self.dsmashHitbox1 = hitbox.SakuraiAngleHitbox([34,26],[24,52],actor,12,8,0.075,40,1,hitbox_lock)
        self.dsmashHitbox2 = hitbox.SakuraiAngleHitbox([-34,26],[24,52],actor,12,8,0.075,140,1,hitbox_lock)
  
    def tearDown(self,actor,other):
        self.dsmashHitbox1.kill()
        self.dsmashHitbox2.kill()
      
    def update(self,actor):
        if self.frame == 0:
            actor.changeSpriteImage(0)
        elif self.frame == 2:
            actor.changeSpriteImage(1)
        elif self.frame == 4:
            actor.changeSpriteImage(2)
        elif self.frame == 6:
            actor.changeSpriteImage(3)
        elif self.frame == 8:
            actor.changeSpriteImage(4)
        elif self.frame == 12:
            actor.changeSpriteImage(5)
        elif self.frame == 14:
            actor.changeSpriteImage(6)
        elif self.frame == 15:
            actor.changeSpriteImage(7)
            actor.active_hitboxes.add(self.dsmashHitbox1)
            actor.active_hitboxes.add(self.dsmashHitbox2)
            #create hitbox
        elif self.frame == 17:
            actor.changeSpriteImage(8)
        elif self.frame == 19:
            actor.changeSpriteImage(9)
            #create sweetspot hitbox
        elif self.frame == 23:
            actor.changeSpriteImage(10)
        elif self.frame == 25:
            actor.changeSpriteImage(11)
        elif self.frame == 27:
            actor.changeSpriteImage(12)
        elif self.frame == 29:
            actor.changeSpriteImage(13)
        elif self.frame == 31:
            actor.changeSpriteImage(14)
        if self.frame == self.lastFrame:
            actor.doIdle()
        self.frame += 1
                     
class ForwardAttack(action.Action):
    def __init__(self):
        action.Action.__init__(self, 24)
  
    def setUp(self,actor):
        self.fSmashHitbox = hitbox.DamageHitbox([20,0],[120,40],actor,8,2.0,0.3,40,1,hitbox.HitboxLock())
          
    def update(self,actor):
        if self.frame == 0:
            actor.change_x = 0
            actor.preferred_xspeed = 0
            actor.changeSprite("fsmash",0)
        elif self.frame == 2:
            actor.changeSpriteImage(1)
        elif self.frame == 4:
            actor.changeSpriteImage(2)
        elif self.frame == 6:
            actor.changeSpriteImage(3)
        elif self.frame == 8:
            actor.changeSpriteImage(4)
        elif self.frame == 10:
            actor.changeSpriteImage(5)
        elif self.frame == 12:
            actor.changeSpriteImage(6)
        elif self.frame == 14:
            actor.changeSpriteImage(7)
            actor.active_hitboxes.add(self.fSmashHitbox)
        elif self.frame == 16:
            actor.changeSpriteImage(8)
            self.fSmashHitbox.kill()
        elif self.frame == 18:
            actor.changeSpriteImage(9)
        elif self.frame == self.lastFrame:
            actor.doIdle()
      
        self.frame += 1
      
        def stateTransitions(self,actor):
            if self.frame > 20:
                baseActions.neutralState(actor)  

class ForwardSmash(action.Action):
    def __init__(self):
        action.Action.__init__(self, 42)
        self.chargeLevel = 0
      
    def setUp(self,actor):
        self.fSmashHitbox = hitbox.DamageHitbox([20,0],[120,40],actor,12,0.8,.35,40,1,hitbox.HitboxLock())
          
    def update(self,actor):
      
        if self.frame >= 3 and self.frame <= 8 and not actor.keysContain('attack') and self.chargeLevel > 0:
            self.frame = 9
            actor.mask = None
          
        if self.frame == 0:
            actor.change_x = 0
            actor.preferred_xspeed = 0
            actor.changeSprite("fsmash",0)
        elif self.frame == 3:
            if actor.keysContain('attack') and self.chargeLevel == 0:
                actor.createMask([255,255,0],72,True,32)
            actor.changeSpriteImage(1)
        elif self.frame == 6:
            actor.changeSpriteImage(2)
        elif self.frame == 8:
            if actor.keysContain('attack') and self.chargeLevel <= 5:
                print("charging...")
                self.chargeLevel += 1
                self.fSmashHitbox.damage += 1
                self.fSmashHitbox.baseKnockback += 0.05
                self.frame = 3
        elif self.frame == 9:
            actor.changeSpriteImage(3)
        elif self.frame == 12:
            actor.changeSpriteImage(4)
        elif self.frame == 15:
            actor.mask = None
            actor.changeSpriteImage(5)
        elif self.frame == 18:
            actor.changeSpriteImage(6)
        elif self.frame == 21:
            actor.changeSpriteImage(7)
            actor.active_hitboxes.add(self.fSmashHitbox)
        elif self.frame == 36:
            actor.changeSpriteImage(8)
            self.fSmashHitbox.kill()
        elif self.frame == 39:
            actor.changeSpriteImage(9)
        elif self.frame == self.lastFrame:
            actor.doIdle()
      
        self.frame += 1
      
        def stateTransitions(self,actor):
            if self.frame > 36:
                baseActions.neutralState(actor)  

class NeutralAir(action.Action):
    def __init__(self):
        action.Action.__init__(self, 34)
  
    def setUp(self, actor):
        actor.change_x = 0
        actor.preferred_xsped = 0
        actor.changeSprite("nair",0)
        self.subImage = 0
        self.nairHitbox = hitbox.DamageHitbox([0,0],[72,72],actor,10,4,0.06,361,1,hitbox.HitboxLock())
  
    def stateTransitions(self, actor):
        if actor.bufferContains('down'):
            if actor.change_y >= 0:
                actor.change_y = actor.var['maxFallSpeed']
        baseActions.airControl(actor)
  
    def tearDown(self,actor,other):
        self.nairHitbox.kill()
  
    def update(self,actor):
        actor.landingLag = 28
        actor.changeSpriteImage(self.subImage % 16)
        self.subImage += 1
        if self.frame == 2:
            actor.active_hitboxes.add(self.nairHitbox)
        if self.frame > 2:
            self.nairHitbox.rect.center = actor.rect.center
        if self.frame == 6:
            self.nairHitbox.damage = 8
            self.nairHitbox.baseKnockback = 5
        elif self.frame == 18:
            self.nairHitbox.damage = 4
            self.nairHitbox.baseKnockback = 2
        if self.frame == self.lastFrame:
            self.nairHitbox.kill()
            actor.landingLag = 14
            actor.changeAction(Fall())
        self.frame += 1

class GroundGrab(action.Action):
    def __init__(self):
        action.Action.__init__(self, 35)

    def setUp(self, actor):
        self.grabHitbox = hitbox.GrabHitbox([40,0], [40,40], actor, hitbox.HitboxLock(), 30)

    def tearDown(self, actor, other):
        self.grabHitbox.kill()

    def update(self,actor):
        if self.frame == 0:
            actor.changeSprite("pivot", 0)
        elif self.frame == 4:
            actor.changeSpriteImage(1)
        elif self.frame == 8:
            actor.changeSpriteImage(2)
        elif self.frame == 12:
            actor.changeSpriteImage(3)
            actor.active_hitboxes.add(self.grabHitbox)
        elif self.frame == 16:
            actor.changeSpriteImage(4)
        elif self.frame == 20:
            actor.changeSpriteImage(3)
            self.grabHitbox.kill()
        elif self.frame == 24:
            actor.changeSpriteImage(2)
        elif self.frame == 28:
            actor.changeSpriteImage(1)
        elif self.frame == 32:
            actor.changeSpriteImage(0)
        if self.frame == self.lastFrame:
            actor.doIdle()
        self.frame += 1

class Pummel(baseActions.BaseGrabbing):
    def __init__(self):
        baseActions.BaseGrabbing.__init__(self,22)

    def update(self, actor):
        if self.frame == 0:
            actor.changeSprite("neutral", self.frame)
        elif self.frame < 9:
            actor.changeSpriteImage(self.frame)
        elif isinstance(actor.grabbing.current_action, baseActions.Grabbed) and self.frame == 9:
            actor.grabbing.dealDamage(2)
        elif self.frame >= 10 and self.frame <= 13:
            actor.changeSpriteImage(9)
        elif self.frame > 13:
            if not (self.frame) > 18:
                actor.changeSpriteImage(self.frame - 4)
        if self.frame == self.lastFrame:
            actor.doGrabbing()
        self.frame += 1
      
class Throw(baseActions.BaseGrabbing):
    def __init__(self):
        baseActions.BaseGrabbing.__init__(self,20)

    def setUp(self,actor):
        self.fSmashHitbox = hitbox.DamageHitbox([20,0],[120,40],actor,10,20.0,0.20,40,1,hitbox.HitboxLock())

    def tearDown(self, actor, other):
        self.fSmashHitbox.kill()
        baseActions.BaseGrabbing.tearDown(self, actor, other)

    def update(self, actor):
        actor.change_y = 0
        if self.frame == 0:
            actor.change_x = 0
            actor.preferred_xspeed = 0
            actor.changeSprite("fsmash",0)
        elif self.frame == 2:
            actor.changeSpriteImage(1)
        elif self.frame == 4:
            actor.changeSpriteImage(2)
        elif self.frame == 6:
            actor.changeSpriteImage(3)
        elif self.frame == 8:
            actor.changeSpriteImage(4)
        elif self.frame == 10:
            actor.changeSpriteImage(5)
        elif self.frame == 12:
            actor.changeSpriteImage(6)
        elif self.frame == 14:
            actor.changeSpriteImage(7)
            actor.active_hitboxes.add(self.fSmashHitbox)
        elif self.frame == 16:
            actor.changeSpriteImage(8)
            self.fSmashHitbox.kill()
        elif self.frame == 18:
            actor.changeSpriteImage(9)
        elif self.frame == self.lastFrame:
            actor.doIdle()
      
        self.frame += 1
      
        def stateTransitions(self,actor):
            if self.frame > 20:
                baseActions.neutralState(actor) 

########################################################
#            BEGIN OVERRIDE CLASSES                    #
########################################################

class Move(baseActions.Move):
    def __init__(self,accel = True):
        baseActions.Move.__init__(self,15)
        self.accel = accel
      
    def update(self, actor):
        if self.accel:
            if (self.frame == 0):
                actor.changeSprite("run",0)
            elif (self.frame == 3):
                actor.changeSpriteImage(1)
            elif (self.frame == 6):
                actor.changeSpriteImage(2)
            elif (self.frame == 9):
                actor.changeSpriteImage(3)
            elif (self.frame == 12):
                actor.changeSpriteImage(4)
        else:
            if (self.frame == 0):
                actor.changeSprite("run",4)
              
        if actor.grounded == False:
            actor.current_action = Fall()
        baseActions.Move.update(self, actor)
        if (self.frame == self.lastFrame):
            self.frame = 12

class Dash(baseActions.Dash):
    def __init__(self,accel = True):
        baseActions.Dash.__init__(self,15)
        self.accel = accel
      
    def update(self, actor):
        if self.accel:
            if (self.frame == 0):
                actor.changeSprite("run",0)
            elif (self.frame == 2):
                actor.changeSpriteImage(1)
            elif (self.frame == 4):
                actor.changeSpriteImage(2)
            elif (self.frame == 6):
                actor.changeSpriteImage(3)
            elif (self.frame == 8):
                actor.changeSpriteImage(4)
        else:
            if (self.frame == 0):
                actor.changeSprite("run",4)
              
        if actor.grounded == False:
            actor.current_action = Fall()
        baseActions.Dash.update(self, actor)
      
class Run(baseActions.Run):
    def __init__(self):
        baseActions.Run.__init__(self,2)
      
    def update(self, actor):
        actor.changeSprite("run",4)
              
        if actor.grounded == False:
            actor.current_action = Fall()
        baseActions.Run.update(self, actor)
        if (self.frame == self.lastFrame):
            self.frame = 1
                 
class Pivot(baseActions.Pivot):
    def __init__(self):
        baseActions.Pivot.__init__(self,10)
      
    def update(self,actor):
        if self.frame == 0:
            actor.changeSprite("pivot",4)
        elif self.frame == 2:
            actor.changeSpriteImage(3)
        elif self.frame == 4:
            actor.changeSpriteImage(2)
        elif self.frame == 6:
            actor.changeSpriteImage(1)
        elif self.frame == 8:
            actor.changeSpriteImage(0)
        baseActions.Pivot.update(self, actor)
      
    def tearDown(self,actor,newAction):
        if isinstance(newAction, Move):
            newAction.accel = False
      
class NeutralAction(baseActions.NeutralAction):
    def __init__(self):
        baseActions.NeutralAction.__init__(self,1)
      
    def update(self, actor):
        if self.frame == 0:
            actor.changeSprite("idle")
            self.frame += 1
        if actor.grounded == False: actor.current_action = Fall()

class Grabbing(baseActions.Grabbing):
    def __init__(self):
        baseActions.Grabbing.__init__(self,1)

    def update(self, actor):
        actor.change_x = 0
        if self.frame == 0:
            actor.changeSprite("pivot", 4)
        self.frame += 1
      
class Stop(baseActions.Stop):
    def __init__(self):
        baseActions.Stop.__init__(self, 9)
  
    def update(self, actor):
        if self.frame == 0:
            actor.changeSprite("pivot",0)
        elif self.frame == 3:
            actor.changeSpriteImage(1)
        elif self.frame == 6:
            actor.changeSpriteImage(2)
        elif self.frame == self.lastFrame:
            if actor.inputBuffer.contains('jump',5):
                actor.current_action = Jump()
            else: actor.current_action = NeutralAction()
        if actor.grounded == False: actor.current_action = Fall()
        baseActions.Stop.update(self, actor)
      
class HitStun(baseActions.HitStun):
    def __init__(self,hitstun,direction):
        baseActions.HitStun.__init__(self, hitstun, direction)
      
    def update(self,actor):        
        if self.frame == self.lastFrame:
            actor.current_action = Fall()
        baseActions.HitStun.update(self, actor)
      
        if self.frame == 1:
            if actor.grounded:
                actor.changeSprite("land",1)
            else:
                actor.changeSprite("jump")
           
class Jump(baseActions.Jump):
    def __init__(self):
        baseActions.Jump.__init__(self,9,8)
      
    def update(self,actor):
        if self.frame == 0:
            actor.changeSprite("land",0)
        elif self.frame == 2:
            actor.changeSpriteImage(1)
        elif self.frame == 4:
            actor.changeSpriteImage(2)
        elif self.frame == 6:
            actor.changeSpriteImage(3)
        elif self.frame == 8:
            actor.changeSprite("jump")
        elif self.frame == self.lastFrame:
            actor.current_action = Fall()
        baseActions.Jump.update(self, actor)
      

class AirJump(baseActions.AirJump):
    def __init__(self):
        baseActions.AirJump.__init__(self,10,4)
      
    def update(self,actor):
        if self.frame == 0:
            actor.changeSprite("airjump",0)
        elif self.frame == 2:
            actor.changeSpriteImage(1)
        elif self.frame == 4:
            actor.changeSpriteImage(2)
        elif self.frame == 6:
            actor.changeSpriteImage(3)
        elif self.frame == 8:
            actor.changeSpriteImage(4)
        elif self.frame == self.lastFrame:
            actor.current_action = Fall()
        baseActions.AirJump.update(self, actor)
          
class Fall(baseActions.Fall):
    def __init__(self):
        baseActions.Fall.__init__(self)
      
    def update(self,actor):
        actor.changeSprite("jump")
        baseActions.Fall.update(self, actor)
          
class Land(baseActions.Land):
    def __init__(self):
        baseActions.Land.__init__(self)
      
    def update(self,actor):
        if self.frame == 0:
            actor.changeSprite("land",0)
        else:
            if self.frame < 12:
                if self.frame % 3 == 0:
                    actor.changeSpriteImage(self.frame / 3)
      
        baseActions.Land.update(self, actor)
      
class Shield(baseActions.Shield):
    def __init__(self):
        baseActions.Shield.__init__(self)
  
    def update(self,actor):
        if self.frame == 0:
            actor.changeSprite("jump")
        baseActions.Shield.update(self, actor)
      
class ShieldBreak(baseActions.ShieldBreak):
    def __init__(self):
        baseActions.ShieldBreak.__init__(self)
  
    def tearDown(self,actor,newAction):
        actor.mask = None
      
      
    def update(self,actor):
        if self.frame == 0:
            actor.createMask([255,0,255],999,True,8)
        baseActions.ShieldBreak.update(self, actor)
      
class ForwardRoll(baseActions.ForwardRoll):
    def __init__(self):
        baseActions.ForwardRoll.__init__(self)
      
    def update(self,actor):
        if self.frame == 0:
            actor.changeSprite("land",1)
        elif self.frame == self.endInvulnFrame:
            actor.changeSprite("land",0)
        baseActions.ForwardRoll.update(self, actor)
      
class BackwardRoll(baseActions.BackwardRoll):
    def __init__(self):
        baseActions.BackwardRoll.__init__(self)
      
    def update(self,actor):
        if self.frame == 0:
            actor.changeSprite("land",1)
        elif self.frame == self.endInvulnFrame:
            actor.changeSprite("land",0)
        baseActions.BackwardRoll.update(self, actor)
      
class SpotDodge(baseActions.SpotDodge):
    def __init__(self):
        baseActions.SpotDodge.__init__(self)
      
    def update(self,actor):
        if self.frame == 0:
            actor.changeSprite("land",0)
        if self.frame < 4:
            actor.changeSpriteImage(self.frame)
        elif self.frame == 21:
            actor.changeSpriteImage(2)
        elif self.frame == 22:
            actor.changeSpriteImage(1)
        elif self.frame == 23:
            actor.changeSpriteImage(0)
        baseActions.SpotDodge.update(self, actor)
      
class AirDodge(baseActions.AirDodge):
    def __init__(self):
        baseActions.AirDodge.__init__(self)
      
    def update(self,actor):
        if self.frame == 0:
            actor.changeSprite("nair",0)
        elif self.frame == self.startInvulnFrame:
            actor.changeSpriteImage(-round(abs(actor.change_x)))
        elif self.frame == self.endInvulnFrame:
            actor.changeSpriteImage(0)
        baseActions.AirDodge.update(self, actor)

class Grabbed(baseActions.Grabbed):
    def __init__(self,height):
        baseActions.Grabbed.__init__(self, height)

    def update(self,actor):
        actor.changeSprite("idle")
        baseActions.Grabbed.update(self, actor)

class Release(baseActions.Release):
    def __init__(self,height):
        baseActions.Release.__init__(self)

    def update(self,actor):
        if self.frame == 0:
            actor.changeSprite("pivot",4)
        elif self.frame == 2:
            actor.changeSpriteImage(3)
        elif self.frame == 4:
            actor.changeSpriteImage(2)
        baseActions.Release.update(self, actor)

class LedgeGrab(baseActions.LedgeGrab):
    def __init__(self,ledge):
        baseActions.LedgeGrab.__init__(self, ledge)
        self.sweetSpotLocation = [64,64]
      
    def update(self,actor):
        actor.changeSprite('jump')
        if self.ledge.side == 'left':
            if actor.facing == -1:
                actor.flip()
            actor.hurtbox.rect.topright = self.ledge.rect.midtop
            actor.rect.center = actor.hurtbox.rect.center
        else:
            if actor.facing == 1:
                actor.flip()
            actor.hurtbox.rect.topleft = self.ledge.rect.midtop
            actor.rect.center = actor.hurtbox.rect.center
        baseActions.LedgeGrab.update(self, actor)

class LedgeGetup(baseActions.LedgeGetup):
    def __init__(self):
        baseActions.LedgeGetup.__init__(self)

    def setUp(self,actor):
        baseActions.LedgeGetup.setUp(self,actor)

    def tearDown(self,actor,other):
        actor.change_x = 0

    def update(self,actor):
        if self.frame == 0:
            actor.changeSprite("getup",0)
            actor.rect.y -= 92
        if (self.frame >= 0) & (self.frame <= 6):
            actor.changeSpriteImage(self.frame)
            actor.change_y = -1.001 #Just barely
            actor.change_x = 0
        if (self.frame >= 8) & (self.frame <= 14):
            actor.change_y = 0
            actor.change_x = 11.5*actor.facing
            if (self.frame % 2 == 0):
                actor.changeSpriteImage(self.frame/2+4)
        if (self.frame > 15):
            if (self.frame % 3 == 2):
                actor.changeSpriteImage(self.frame/3+6)
            actor.change_x = actor.var['maxGroundSpeed']*actor.facing
        baseActions.LedgeGetup.update(self, actor)

########################################################
#             BEGIN HELPER METHODS                     #
########################################################
 

digiholic

Smash Ace
Joined
Nov 13, 2009
Messages
678
Location
Albuquerque, New Mexico
NNID
digiholic
I've checked, yes they're in the same directory. Here's what I got from opening it up:

Code:
import engine.action as action
import engine.baseActions as baseActions
import engine.hitbox as hitbox
import engine.article as article
import engine.abstractFighter as abstractFighter
import math

import settingsManager #TEMPORARY until I figure out article sprites

class SplatArticle(article.AnimatedArticle):
    def __init__(self, owner, origin, direction):
        article.AnimatedArticle.__init__(self, settingsManager.createPath('sprites/hitboxie_projectile.png'), owner, origin, imageWidth=16,length=120)
        self.direction = direction
        self.change_y = 0
        self.hitbox = hitbox.DamageHitbox(self.rect.center, [12,12], self.owner, 6, 2, 0, 270, 1, hitbox.HitboxLock())
        self.hitbox.article = self
         
    # Override the onCollision of the hitbox
    def onCollision(self, other):
        othersClasses = map(lambda(x):x.__name__,other.__class__.__bases__) + [other.__class__.__name__]
        print othersClasses
        if 'AbstractFighter' in othersClasses or 'Platform' in othersClasses:
            self.hitbox.kill()
            self.kill()
        #TODO check for verticality of platform landing
         
         
    def update(self):
        self.rect.x += 24 * self.direction
        self.rect.y += self.change_y
        self.change_y += 0.5
        self.hitbox.rect.center = self.rect.center
        self.frame += 1
         
        if self.frame > 120:
            self.kill()
            self.hitbox.kill()

class NeutralGroundSpecial(action.Action):
    def __init__(self):
        action.Action.__init__(self,40)
             
    def setUp(self, actor):
        self.projectile = SplatArticle(actor,(actor.sprite.boundingRect.centerx + (32 * actor.facing), actor.sprite.boundingRect.centery), actor.facing)
        actor.change_x = 0
        actor.preferred_xspeed = 0
        actor.changeSprite("nspecial",0)
     
    def tearDown(self, actor, new):
        pass
 
    def stateTransitions(self, actor):
        pass
            
    def update(self, actor):
        actor.changeSpriteImage(math.floor(self.frame/4))
        if self.frame == 24:
            self.projectile.rect.center = (actor.sprite.boundingRect.centerx + (24 * actor.facing),actor.sprite.boundingRect.centery-8)
            actor.articles.add(self.projectile)
            actor.active_hitboxes.add(self.projectile.hitbox)
            print actor.active_hitboxes
         
        if self.frame == self.lastFrame:
            actor.doIdle()
        self.frame += 1

class NeutralAirSpecial(action.Action):
    def __init__(self):
        action.Action.__init__(self,40)
             
    def setUp(self, actor):
        self.projectile = SplatArticle(actor,(actor.sprite.boundingRect.centerx + (32 * actor.facing), actor.sprite.boundingRect.centery), actor.facing)
        actor.changeSprite("nspecial",0)


    def stateTransitions(self, actor):
        if actor.bufferContains('down'):
            if actor.change_y >= 0:
                actor.change_y = actor.var['maxFallSpeed']
        baseActions.airControl(actor)
     
    def tearDown(self, actor, new):
        pass
            
    def update(self, actor):
        actor.landingLag = 25
        actor.changeSpriteImage(math.floor(self.frame/4))
        if self.frame == 24:
            self.projectile.rect.center = (actor.sprite.boundingRect.centerx + (24 * actor.facing),actor.sprite.boundingRect.centery-8)
            actor.articles.add(self.projectile)
            actor.active_hitboxes.add(self.projectile.hitbox)
            print actor.active_hitboxes
            if actor.inputBuffer.contains('special', 10):
                actor.changeAction(NeutralAirSpecial())
         
        if self.frame == self.lastFrame:
            actor.landingLag = 12
            actor.changeAction(Fall())
        self.frame += 1

class ForwardSpecial(action.Action):
    def __init__(self):
        action.Action.__init__(self,44)

    def setUp(self, actor):
        actor.change_x = 0
        actor.preferred_xspeed = 0
        actor.changeSprite("nair",0)
     
        
class NeutralAttack(action.Action):
    def __init__(self):
        action.Action.__init__(self,22)
 
    def setUp(self, actor):
        actor.change_x = 0
        actor.preferred_xspeed = 0
        actor.changeSprite("neutral",0)
        self.jabHitbox = self.outwardHitbox(actor)
     
    def tearDown(self, actor, new):
        self.jabHitbox.kill()
 
    def stateTransitions(self, actor):
        if self.frame == self.lastFrame:
            if actor.keysContain('attack'):
                self.jabHitbox.hitbox_lock = hitbox.HitboxLock()
                self.frame = 0
             
    # Here's an example of creating an anonymous hitbox class.
    # This one calculates its trajectory based off of the angle between the two fighters.
    # Since this hitbox if specifically for this attack, we can hard code in the values.
    class outwardHitbox(hitbox.DamageHitbox):
        def __init__(self,actor):
            hitbox.DamageHitbox.__init__(self, [0,0], [80,80], actor, 2, 8, 0.02, 0, 1, hitbox.HitboxLock())
         
        def onCollision(self,other):
            self.trajectory = abstractFighter.getDirectionBetweenPoints(self.owner.rect.midbottom, other.rect.center)
            hitbox.DamageHitbox.onCollision(self, other)
         
    def update(self, actor):
        if self.frame < 9:
            actor.changeSpriteImage(self.frame)
        elif self.frame == 9:
            actor.active_hitboxes.add(self.jabHitbox)
        elif self.frame >= 10 and self.frame <= 13:
            actor.changeSpriteImage(9)
        elif self.frame > 13:
            self.jabHitbox.kill()
            if not (self.frame) > 18:
                actor.changeSpriteImage(self.frame - 4)
        if self.frame == self.lastFrame:
            actor.doIdle()
        self.frame += 1

class DashAttack(action.Action):
    def __init__(self):
        action.Action.__init__(self,32)

    def setUp(self, actor):
        actor.setSpeed(actor.change_x, actor.facing)
        actor.changeSprite("nair")

        self.dashHitbox = hitbox.DamageHitbox([0,0],[70,70],actor,2,10,0.07,20,1,hitbox.HitboxLock())
        self.chainHitbox = hitbox.AutolinkHitbox([0,0],[70,70],actor,2,1,hitbox.HitboxLock(),1,1.5)

    def tearDown(self,actor,other):
        self.dashHitbox.kill()
        self.chainHitbox.kill()
        actor.setSpeed(0, actor.facing)

    def update(self,actor):
        if self.frame%2 == 0 and self.frame <= 8:
            actor.changeSpriteImage(self.frame/2)
        elif self.frame <= 24:
            actor.changeSpriteImage((self.frame-4)%16)
        elif self.frame%2 == 0:
            actor.changeSpriteImage((self.frame/2-8)%16)

        self.dashHitbox.rect.center = [actor.rect.center[0], actor.rect.center[1]]
        self.chainHitbox.rect.center = [actor.rect.center[0], actor.rect.center[1]]

        if self.frame == 8:
            actor.active_hitboxes.add(self.chainHitbox)
        if self.frame == 12:
            self.chainHitbox.hitbox_lock = hitbox.HitboxLock()
        if self.frame == 16:
            self.chainHitbox.hitbox_lock = hitbox.HitboxLock()
        if self.frame == 20:
            self.chainHitbox.kill()
            actor.active_hitboxes.add(self.dashHitbox)
        if self.frame == 24:
            self.dashHitbox.kill()
            actor.setSpeed(0, actor.facing)

        if self.frame == self.lastFrame:
            actor.doIdle()
        self.frame += 1
     
class DownAttack(action.Action):
    def __init__(self):
        action.Action.__init__(self, 34)
 
    def setUp(self, actor):
        actor.change_x = 0
        actor.preferred_xspeed = 0
        actor.changeSprite("dsmash",0)
        hitbox_lock = hitbox.HitboxLock()
        self.dsmashHitbox1 = hitbox.SakuraiAngleHitbox([34,26],[24,52],actor,12,8,0.075,40,1,hitbox_lock)
        self.dsmashHitbox2 = hitbox.SakuraiAngleHitbox([-34,26],[24,52],actor,12,8,0.075,140,1,hitbox_lock)
 
    def tearDown(self,actor,other):
        self.dsmashHitbox1.kill()
        self.dsmashHitbox2.kill()
     
    def update(self,actor):
        if self.frame == 0:
            actor.changeSpriteImage(0)
        elif self.frame == 2:
            actor.changeSpriteImage(1)
        elif self.frame == 4:
            actor.changeSpriteImage(2)
        elif self.frame == 6:
            actor.changeSpriteImage(3)
        elif self.frame == 8:
            actor.changeSpriteImage(4)
        elif self.frame == 12:
            actor.changeSpriteImage(5)
        elif self.frame == 14:
            actor.changeSpriteImage(6)
        elif self.frame == 15:
            actor.changeSpriteImage(7)
            actor.active_hitboxes.add(self.dsmashHitbox1)
            actor.active_hitboxes.add(self.dsmashHitbox2)
            #create hitbox
        elif self.frame == 17:
            actor.changeSpriteImage(8)
        elif self.frame == 19:
            actor.changeSpriteImage(9)
            #create sweetspot hitbox
        elif self.frame == 23:
            actor.changeSpriteImage(10)
        elif self.frame == 25:
            actor.changeSpriteImage(11)
        elif self.frame == 27:
            actor.changeSpriteImage(12)
        elif self.frame == 29:
            actor.changeSpriteImage(13)
        elif self.frame == 31:
            actor.changeSpriteImage(14)
        if self.frame == self.lastFrame:
            actor.doIdle()
        self.frame += 1
                    
class ForwardAttack(action.Action):
    def __init__(self):
        action.Action.__init__(self, 24)
 
    def setUp(self,actor):
        self.fSmashHitbox = hitbox.DamageHitbox([20,0],[120,40],actor,8,2.0,0.3,40,1,hitbox.HitboxLock())
         
    def update(self,actor):
        if self.frame == 0:
            actor.change_x = 0
            actor.preferred_xspeed = 0
            actor.changeSprite("fsmash",0)
        elif self.frame == 2:
            actor.changeSpriteImage(1)
        elif self.frame == 4:
            actor.changeSpriteImage(2)
        elif self.frame == 6:
            actor.changeSpriteImage(3)
        elif self.frame == 8:
            actor.changeSpriteImage(4)
        elif self.frame == 10:
            actor.changeSpriteImage(5)
        elif self.frame == 12:
            actor.changeSpriteImage(6)
        elif self.frame == 14:
            actor.changeSpriteImage(7)
            actor.active_hitboxes.add(self.fSmashHitbox)
        elif self.frame == 16:
            actor.changeSpriteImage(8)
            self.fSmashHitbox.kill()
        elif self.frame == 18:
            actor.changeSpriteImage(9)
        elif self.frame == self.lastFrame:
            actor.doIdle()
     
        self.frame += 1
     
        def stateTransitions(self,actor):
            if self.frame > 20:
                baseActions.neutralState(actor) 

class ForwardSmash(action.Action):
    def __init__(self):
        action.Action.__init__(self, 42)
        self.chargeLevel = 0
     
    def setUp(self,actor):
        self.fSmashHitbox = hitbox.DamageHitbox([20,0],[120,40],actor,12,0.8,.35,40,1,hitbox.HitboxLock())
         
    def update(self,actor):
     
        if self.frame >= 3 and self.frame <= 8 and not actor.keysContain('attack') and self.chargeLevel > 0:
            self.frame = 9
            actor.mask = None
         
        if self.frame == 0:
            actor.change_x = 0
            actor.preferred_xspeed = 0
            actor.changeSprite("fsmash",0)
        elif self.frame == 3:
            if actor.keysContain('attack') and self.chargeLevel == 0:
                actor.createMask([255,255,0],72,True,32)
            actor.changeSpriteImage(1)
        elif self.frame == 6:
            actor.changeSpriteImage(2)
        elif self.frame == 8:
            if actor.keysContain('attack') and self.chargeLevel <= 5:
                print("charging...")
                self.chargeLevel += 1
                self.fSmashHitbox.damage += 1
                self.fSmashHitbox.baseKnockback += 0.05
                self.frame = 3
        elif self.frame == 9:
            actor.changeSpriteImage(3)
        elif self.frame == 12:
            actor.changeSpriteImage(4)
        elif self.frame == 15:
            actor.mask = None
            actor.changeSpriteImage(5)
        elif self.frame == 18:
            actor.changeSpriteImage(6)
        elif self.frame == 21:
            actor.changeSpriteImage(7)
            actor.active_hitboxes.add(self.fSmashHitbox)
        elif self.frame == 36:
            actor.changeSpriteImage(8)
            self.fSmashHitbox.kill()
        elif self.frame == 39:
            actor.changeSpriteImage(9)
        elif self.frame == self.lastFrame:
            actor.doIdle()
     
        self.frame += 1
     
        def stateTransitions(self,actor):
            if self.frame > 36:
                baseActions.neutralState(actor) 

class NeutralAir(action.Action):
    def __init__(self):
        action.Action.__init__(self, 34)
 
    def setUp(self, actor):
        actor.change_x = 0
        actor.preferred_xsped = 0
        actor.changeSprite("nair",0)
        self.subImage = 0
        self.nairHitbox = hitbox.DamageHitbox([0,0],[72,72],actor,10,4,0.06,361,1,hitbox.HitboxLock())
 
    def stateTransitions(self, actor):
        if actor.bufferContains('down'):
            if actor.change_y >= 0:
                actor.change_y = actor.var['maxFallSpeed']
        baseActions.airControl(actor)
 
    def tearDown(self,actor,other):
        self.nairHitbox.kill()
 
    def update(self,actor):
        actor.landingLag = 28
        actor.changeSpriteImage(self.subImage % 16)
        self.subImage += 1
        if self.frame == 2:
            actor.active_hitboxes.add(self.nairHitbox)
        if self.frame > 2:
            self.nairHitbox.rect.center = actor.rect.center
        if self.frame == 6:
            self.nairHitbox.damage = 8
            self.nairHitbox.baseKnockback = 5
        elif self.frame == 18:
            self.nairHitbox.damage = 4
            self.nairHitbox.baseKnockback = 2
        if self.frame == self.lastFrame:
            self.nairHitbox.kill()
            actor.landingLag = 14
            actor.changeAction(Fall())
        self.frame += 1

class GroundGrab(action.Action):
    def __init__(self):
        action.Action.__init__(self, 35)

    def setUp(self, actor):
        self.grabHitbox = hitbox.GrabHitbox([40,0], [40,40], actor, hitbox.HitboxLock(), 30)

    def tearDown(self, actor, other):
        self.grabHitbox.kill()

    def update(self,actor):
        if self.frame == 0:
            actor.changeSprite("pivot", 0)
        elif self.frame == 4:
            actor.changeSpriteImage(1)
        elif self.frame == 8:
            actor.changeSpriteImage(2)
        elif self.frame == 12:
            actor.changeSpriteImage(3)
            actor.active_hitboxes.add(self.grabHitbox)
        elif self.frame == 16:
            actor.changeSpriteImage(4)
        elif self.frame == 20:
            actor.changeSpriteImage(3)
            self.grabHitbox.kill()
        elif self.frame == 24:
            actor.changeSpriteImage(2)
        elif self.frame == 28:
            actor.changeSpriteImage(1)
        elif self.frame == 32:
            actor.changeSpriteImage(0)
        if self.frame == self.lastFrame:
            actor.doIdle()
        self.frame += 1

class Pummel(baseActions.BaseGrabbing):
    def __init__(self):
        baseActions.BaseGrabbing.__init__(self,22)

    def update(self, actor):
        if self.frame == 0:
            actor.changeSprite("neutral", self.frame)
        elif self.frame < 9:
            actor.changeSpriteImage(self.frame)
        elif isinstance(actor.grabbing.current_action, baseActions.Grabbed) and self.frame == 9:
            actor.grabbing.dealDamage(2)
        elif self.frame >= 10 and self.frame <= 13:
            actor.changeSpriteImage(9)
        elif self.frame > 13:
            if not (self.frame) > 18:
                actor.changeSpriteImage(self.frame - 4)
        if self.frame == self.lastFrame:
            actor.doGrabbing()
        self.frame += 1
     
class Throw(baseActions.BaseGrabbing):
    def __init__(self):
        baseActions.BaseGrabbing.__init__(self,20)

    def setUp(self,actor):
        self.fSmashHitbox = hitbox.DamageHitbox([20,0],[120,40],actor,10,20.0,0.20,40,1,hitbox.HitboxLock())

    def tearDown(self, actor, other):
        self.fSmashHitbox.kill()
        baseActions.BaseGrabbing.tearDown(self, actor, other)

    def update(self, actor):
        actor.change_y = 0
        if self.frame == 0:
            actor.change_x = 0
            actor.preferred_xspeed = 0
            actor.changeSprite("fsmash",0)
        elif self.frame == 2:
            actor.changeSpriteImage(1)
        elif self.frame == 4:
            actor.changeSpriteImage(2)
        elif self.frame == 6:
            actor.changeSpriteImage(3)
        elif self.frame == 8:
            actor.changeSpriteImage(4)
        elif self.frame == 10:
            actor.changeSpriteImage(5)
        elif self.frame == 12:
            actor.changeSpriteImage(6)
        elif self.frame == 14:
            actor.changeSpriteImage(7)
            actor.active_hitboxes.add(self.fSmashHitbox)
        elif self.frame == 16:
            actor.changeSpriteImage(8)
            self.fSmashHitbox.kill()
        elif self.frame == 18:
            actor.changeSpriteImage(9)
        elif self.frame == self.lastFrame:
            actor.doIdle()
     
        self.frame += 1
     
        def stateTransitions(self,actor):
            if self.frame > 20:
                baseActions.neutralState(actor)

########################################################
#            BEGIN OVERRIDE CLASSES                    #
########################################################

class Move(baseActions.Move):
    def __init__(self,accel = True):
        baseActions.Move.__init__(self,15)
        self.accel = accel
     
    def update(self, actor):
        if self.accel:
            if (self.frame == 0):
                actor.changeSprite("run",0)
            elif (self.frame == 3):
                actor.changeSpriteImage(1)
            elif (self.frame == 6):
                actor.changeSpriteImage(2)
            elif (self.frame == 9):
                actor.changeSpriteImage(3)
            elif (self.frame == 12):
                actor.changeSpriteImage(4)
        else:
            if (self.frame == 0):
                actor.changeSprite("run",4)
             
        if actor.grounded == False:
            actor.current_action = Fall()
        baseActions.Move.update(self, actor)
        if (self.frame == self.lastFrame):
            self.frame = 12

class Dash(baseActions.Dash):
    def __init__(self,accel = True):
        baseActions.Dash.__init__(self,15)
        self.accel = accel
     
    def update(self, actor):
        if self.accel:
            if (self.frame == 0):
                actor.changeSprite("run",0)
            elif (self.frame == 2):
                actor.changeSpriteImage(1)
            elif (self.frame == 4):
                actor.changeSpriteImage(2)
            elif (self.frame == 6):
                actor.changeSpriteImage(3)
            elif (self.frame == 8):
                actor.changeSpriteImage(4)
        else:
            if (self.frame == 0):
                actor.changeSprite("run",4)
             
        if actor.grounded == False:
            actor.current_action = Fall()
        baseActions.Dash.update(self, actor)
     
class Run(baseActions.Run):
    def __init__(self):
        baseActions.Run.__init__(self,2)
     
    def update(self, actor):
        actor.changeSprite("run",4)
             
        if actor.grounded == False:
            actor.current_action = Fall()
        baseActions.Run.update(self, actor)
        if (self.frame == self.lastFrame):
            self.frame = 1
                
class Pivot(baseActions.Pivot):
    def __init__(self):
        baseActions.Pivot.__init__(self,10)
     
    def update(self,actor):
        if self.frame == 0:
            actor.changeSprite("pivot",4)
        elif self.frame == 2:
            actor.changeSpriteImage(3)
        elif self.frame == 4:
            actor.changeSpriteImage(2)
        elif self.frame == 6:
            actor.changeSpriteImage(1)
        elif self.frame == 8:
            actor.changeSpriteImage(0)
        baseActions.Pivot.update(self, actor)
     
    def tearDown(self,actor,newAction):
        if isinstance(newAction, Move):
            newAction.accel = False
     
class NeutralAction(baseActions.NeutralAction):
    def __init__(self):
        baseActions.NeutralAction.__init__(self,1)
     
    def update(self, actor):
        if self.frame == 0:
            actor.changeSprite("idle")
            self.frame += 1
        if actor.grounded == False: actor.current_action = Fall()

class Grabbing(baseActions.Grabbing):
    def __init__(self):
        baseActions.Grabbing.__init__(self,1)

    def update(self, actor):
        actor.change_x = 0
        if self.frame == 0:
            actor.changeSprite("pivot", 4)
        self.frame += 1
     
class Stop(baseActions.Stop):
    def __init__(self):
        baseActions.Stop.__init__(self, 9)
 
    def update(self, actor):
        if self.frame == 0:
            actor.changeSprite("pivot",0)
        elif self.frame == 3:
            actor.changeSpriteImage(1)
        elif self.frame == 6:
            actor.changeSpriteImage(2)
        elif self.frame == self.lastFrame:
            if actor.inputBuffer.contains('jump',5):
                actor.current_action = Jump()
            else: actor.current_action = NeutralAction()
        if actor.grounded == False: actor.current_action = Fall()
        baseActions.Stop.update(self, actor)
     
class HitStun(baseActions.HitStun):
    def __init__(self,hitstun,direction):
        baseActions.HitStun.__init__(self, hitstun, direction)
     
    def update(self,actor):       
        if self.frame == self.lastFrame:
            actor.current_action = Fall()
        baseActions.HitStun.update(self, actor)
     
        if self.frame == 1:
            if actor.grounded:
                actor.changeSprite("land",1)
            else:
                actor.changeSprite("jump")
          
class Jump(baseActions.Jump):
    def __init__(self):
        baseActions.Jump.__init__(self,9,8)
     
    def update(self,actor):
        if self.frame == 0:
            actor.changeSprite("land",0)
        elif self.frame == 2:
            actor.changeSpriteImage(1)
        elif self.frame == 4:
            actor.changeSpriteImage(2)
        elif self.frame == 6:
            actor.changeSpriteImage(3)
        elif self.frame == 8:
            actor.changeSprite("jump")
        elif self.frame == self.lastFrame:
            actor.current_action = Fall()
        baseActions.Jump.update(self, actor)
     

class AirJump(baseActions.AirJump):
    def __init__(self):
        baseActions.AirJump.__init__(self,10,4)
     
    def update(self,actor):
        if self.frame == 0:
            actor.changeSprite("airjump",0)
        elif self.frame == 2:
            actor.changeSpriteImage(1)
        elif self.frame == 4:
            actor.changeSpriteImage(2)
        elif self.frame == 6:
            actor.changeSpriteImage(3)
        elif self.frame == 8:
            actor.changeSpriteImage(4)
        elif self.frame == self.lastFrame:
            actor.current_action = Fall()
        baseActions.AirJump.update(self, actor)
         
class Fall(baseActions.Fall):
    def __init__(self):
        baseActions.Fall.__init__(self)
     
    def update(self,actor):
        actor.changeSprite("jump")
        baseActions.Fall.update(self, actor)
         
class Land(baseActions.Land):
    def __init__(self):
        baseActions.Land.__init__(self)
     
    def update(self,actor):
        if self.frame == 0:
            actor.changeSprite("land",0)
        else:
            if self.frame < 12:
                if self.frame % 3 == 0:
                    actor.changeSpriteImage(self.frame / 3)
     
        baseActions.Land.update(self, actor)
     
class Shield(baseActions.Shield):
    def __init__(self):
        baseActions.Shield.__init__(self)
 
    def update(self,actor):
        if self.frame == 0:
            actor.changeSprite("jump")
        baseActions.Shield.update(self, actor)
     
class ShieldBreak(baseActions.ShieldBreak):
    def __init__(self):
        baseActions.ShieldBreak.__init__(self)
 
    def tearDown(self,actor,newAction):
        actor.mask = None
     
     
    def update(self,actor):
        if self.frame == 0:
            actor.createMask([255,0,255],999,True,8)
        baseActions.ShieldBreak.update(self, actor)
     
class ForwardRoll(baseActions.ForwardRoll):
    def __init__(self):
        baseActions.ForwardRoll.__init__(self)
     
    def update(self,actor):
        if self.frame == 0:
            actor.changeSprite("land",1)
        elif self.frame == self.endInvulnFrame:
            actor.changeSprite("land",0)
        baseActions.ForwardRoll.update(self, actor)
     
class BackwardRoll(baseActions.BackwardRoll):
    def __init__(self):
        baseActions.BackwardRoll.__init__(self)
     
    def update(self,actor):
        if self.frame == 0:
            actor.changeSprite("land",1)
        elif self.frame == self.endInvulnFrame:
            actor.changeSprite("land",0)
        baseActions.BackwardRoll.update(self, actor)
     
class SpotDodge(baseActions.SpotDodge):
    def __init__(self):
        baseActions.SpotDodge.__init__(self)
     
    def update(self,actor):
        if self.frame == 0:
            actor.changeSprite("land",0)
        if self.frame < 4:
            actor.changeSpriteImage(self.frame)
        elif self.frame == 21:
            actor.changeSpriteImage(2)
        elif self.frame == 22:
            actor.changeSpriteImage(1)
        elif self.frame == 23:
            actor.changeSpriteImage(0)
        baseActions.SpotDodge.update(self, actor)
     
class AirDodge(baseActions.AirDodge):
    def __init__(self):
        baseActions.AirDodge.__init__(self)
     
    def update(self,actor):
        if self.frame == 0:
            actor.changeSprite("nair",0)
        elif self.frame == self.startInvulnFrame:
            actor.changeSpriteImage(-round(abs(actor.change_x)))
        elif self.frame == self.endInvulnFrame:
            actor.changeSpriteImage(0)
        baseActions.AirDodge.update(self, actor)

class Grabbed(baseActions.Grabbed):
    def __init__(self,height):
        baseActions.Grabbed.__init__(self, height)

    def update(self,actor):
        actor.changeSprite("idle")
        baseActions.Grabbed.update(self, actor)

class Release(baseActions.Release):
    def __init__(self,height):
        baseActions.Release.__init__(self)

    def update(self,actor):
        if self.frame == 0:
            actor.changeSprite("pivot",4)
        elif self.frame == 2:
            actor.changeSpriteImage(3)
        elif self.frame == 4:
            actor.changeSpriteImage(2)
        baseActions.Release.update(self, actor)

class LedgeGrab(baseActions.LedgeGrab):
    def __init__(self,ledge):
        baseActions.LedgeGrab.__init__(self, ledge)
        self.sweetSpotLocation = [64,64]
     
    def update(self,actor):
        actor.changeSprite('jump')
        if self.ledge.side == 'left':
            if actor.facing == -1:
                actor.flip()
            actor.hurtbox.rect.topright = self.ledge.rect.midtop
            actor.rect.center = actor.hurtbox.rect.center
        else:
            if actor.facing == 1:
                actor.flip()
            actor.hurtbox.rect.topleft = self.ledge.rect.midtop
            actor.rect.center = actor.hurtbox.rect.center
        baseActions.LedgeGrab.update(self, actor)

class LedgeGetup(baseActions.LedgeGetup):
    def __init__(self):
        baseActions.LedgeGetup.__init__(self)

    def setUp(self,actor):
        baseActions.LedgeGetup.setUp(self,actor)

    def tearDown(self,actor,other):
        actor.change_x = 0

    def update(self,actor):
        if self.frame == 0:
            actor.changeSprite("getup",0)
            actor.rect.y -= 92
        if (self.frame >= 0) & (self.frame <= 6):
            actor.changeSpriteImage(self.frame)
            actor.change_y = -1.001 #Just barely
            actor.change_x = 0
        if (self.frame >= 8) & (self.frame <= 14):
            actor.change_y = 0
            actor.change_x = 11.5*actor.facing
            if (self.frame % 2 == 0):
                actor.changeSpriteImage(self.frame/2+4)
        if (self.frame > 15):
            if (self.frame % 3 == 2):
                actor.changeSpriteImage(self.frame/3+6)
            actor.change_x = actor.var['maxGroundSpeed']*actor.facing
        baseActions.LedgeGetup.update(self, actor)

########################################################
#             BEGIN HELPER METHODS                     #
########################################################
Darnit, that's the importer code again. I thought I had it fixed. It's nearly impossible for me to test, because it only gives errors if you don't have all the Python libraries installed. Thanks for bringing this up, I'm going to have to work on this a bit more. In the mean time, I've pushed an updated version to GitHub that won't crash, until I can fix the dynamic importing. If you re-download, it will work.
 

TheAmazingMemer

Smash Apprentice
Joined
Apr 18, 2015
Messages
75
Location
Who knows
NNID
Faves3000
3DS FC
4940-5681-4585
Darnit, that's the importer code again. I thought I had it fixed. It's nearly impossible for me to test, because it only gives errors if you don't have all the Python libraries installed. Thanks for bringing this up, I'm going to have to work on this a bit more. In the mean time, I've pushed an updated version to GitHub that won't crash, until I can fix the dynamic importing. If you re-download, it will work.
I've redownloaded, and I got it working. Thanks for helping
 

digiholic

Smash Ace
Joined
Nov 13, 2009
Messages
678
Location
Albuquerque, New Mexico
NNID
digiholic

HermitHelmet

Smash Journeyman
Joined
May 2, 2014
Messages
290
Location
Newcastle, UK
NNID
Hermit_Helmet1

Frozn~

Smash Apprentice
Joined
Sep 20, 2014
Messages
112
Wow this is a great idea! I've been working on a smash-like game in Construct, but this would make life a lot easier :D
Looking forward to what you come up with!
 

digiholic

Smash Ace
Joined
Nov 13, 2009
Messages
678
Location
Albuquerque, New Mexico
NNID
digiholic

digiholic

Smash Ace
Joined
Nov 13, 2009
Messages
678
Location
Albuquerque, New Mexico
NNID
digiholic
Today's Feature Friday is an informative one! It goes over all the new XML tags for actions, and will be updated over time as more are added. http://projecttussle.com/post/145381595584/feature-friday-actions-subactions-and-you


I get an error when starting main.exe
http://i.imgur.com/lU8g4KT.png
What's up?
(I also ran updater.exe before running main.exe)
Looks like it doesn't know what to do with that non-ASCII character in your file path. Try moving the folder to somewhere that is not in your user folder and see if you get the same error. I'll add unicode support to the bug tracker. Thanks for bringing this to my attention!
 

Radical Larry

Smash Lord
Joined
Mar 19, 2014
Messages
1,994
Location
The Pocket Dimension
NNID
Crimson-Vulcan
3DS FC
1822-3761-9326
You know, this engine looks so promising that I might make it the official engine for my upcoming project, the game Chronocide! This looks like it has so much promise that I have to give it a try, so I decidedly downloaded it, and I'll be testing out its features here and there with the first character for my game. If this engine proves to be something very wonderful, I'll keep using it up to the full release.

Hey, I'm going to ask this. If there's no Final Smash Meter coding in this yet, mind if you do it? I'm kind of wanting to do something akin to that, so...yeah, if it's possible, would you implement it?
 

digiholic

Smash Ace
Joined
Nov 13, 2009
Messages
678
Location
Albuquerque, New Mexico
NNID
digiholic
You know, this engine looks so promising that I might make it the official engine for my upcoming project, the game Chronocide! This looks like it has so much promise that I have to give it a try, so I decidedly downloaded it, and I'll be testing out its features here and there with the first character for my game. If this engine proves to be something very wonderful, I'll keep using it up to the full release.

Hey, I'm going to ask this. If there's no Final Smash Meter coding in this yet, mind if you do it? I'm kind of wanting to do something akin to that, so...yeah, if it's possible, would you implement it?
That's great! If the engine suits your needs, do whatever you like with it. I'm always around for help if you need any advice on how to work it. If you get the download from the website, know that a recent GitHub change means the updater is currently not working, so you'll have to update it manually (by downloading the zip from GitHub and extracting it to your download directory)

Final Smashes are honestly a good bit of time away, but if you know a bit of python, the team is always accepting new members who want to contribute code to the game.
 

Radical Larry

Smash Lord
Joined
Mar 19, 2014
Messages
1,994
Location
The Pocket Dimension
NNID
Crimson-Vulcan
3DS FC
1822-3761-9326
Well I am only someone who understands how to copy and manipulate coding, haha.
But my project is a completely original game with original characters and stages, so this template will certainly be the best suited for my needs to fully make my game. I'll try it after my sleep and if it's amazing, I'll surely spread word of it to literally everywhere I can!

And I can wait for Final Smashes, but I hope you can bring in something like EX Charges for Final Smashes. Oh, and is it possible to include coding for something like Charging Aerials?
 

Radical Larry

Smash Lord
Joined
Mar 19, 2014
Messages
1,994
Location
The Pocket Dimension
NNID
Crimson-Vulcan
3DS FC
1822-3761-9326
Well this is actually wonderful! Can't wait to get started working on the fighter attributes for my own game! I'll have a hell of a lot of fun with it! Thank you so much for the update.
 
Top Bottom