Pokémon Essentials All questions and discussion about Pokémon Essentials, the Pokémon game kit for RPG Maker XP, go in here. Also contains links to the latest downloads and the Essentials Wiki.

Draconius GO
Reply
 
Thread Tools
  #1    
Old July 27th, 2013 (10:47 AM).
MC50 MC50 is online now
     
    Join Date: Aug 2006
    Gender:
    Nature: Adamant
    Posts: 28
    Hey all,

    I've been playing around with editing the trainer AI to take more situations into account, and it has been helping it - It's just simple stuff like if the holder has Choice Scarf, they are more likely to Trick, or if the user is an electric type, they are more likely to use Soak. Mostly just stuff copied from the other attacks function codes.

    But is there a way to make it ALWAYS use the move I want on the first turn?

    Basically, to summarize, I have a Pokemon with Normalize and Skill Swap, that is a ghost type Pokemon. I want the enemy trainer to ALWAYS use Skill Swap first turn if they have Normalize, so I figured just shooting the chance to use it up would basically guarantee it - but it doesn't seem to work that way, no matter how high I put the chance to use it.

    Is there a way to see the Debug Logs for how the game does AI Calculations while you are playing? That would help a lot.

    Anyway, the code for it is below - Maybe I didn't even do it right. But it definitely worked for some moves. Thanks if anyone knows.

    when 0x67
    if isConst?(opponent.ability(true),PBAbilities,:WONDERGUARD) ||
    isConst?(attacker.ability(true),PBAbilities,:WONDERGUARD)
    score-=80
    end
    if isConst?(attacker.ability(true),PBAbilities,:NORMALIZE)
    score+=150
    end
    if isConst?(opponent.ability(true),PBAbilities,:NORMALIZE)
    score-=150
    end
    if skill>=80
    if opponent.ability==attacker.ability ||
    (isConst?(opponent.ability(true),PBAbilities,:TRUANT) &&
    attacker.pbIsOpposing?(opponent.index)) ||
    (isConst?(opponent.ability(true),PBAbilities,:SLOWSTART) &&
    attacker.pbIsOpposing?(opponent.index))
    score-=90
    end
    end
    score-=40 # don't prefer this move
    Reply With Quote

    Relevant Advertising!

      #2    
    Old July 27th, 2013 (12:26 PM).
    Maruno's Avatar
    Maruno Maruno is offline
    Lead Dev of Pokémon Essentials
       
      Join Date: Jan 2008
      Location: England
      Posts: 5,189
      Set $INTERNAL=true before the battle, and then do the battle. There should be a debug log created in the Data folder which shows the calculated scores for each opponent's move, along with various other information.

      From your code, in your scenario Skill Swap would only gain 110 points. A move is only auto-chosen if the standard deviation of the moves' scores is 100 or greater (and even then, only 90% of the time); otherwise it's simply more likely that higher-scored moves will be chosen (but it's not a certainty). Skill Swap's score isn't high enough to qualify unless the other known moves have really low scores.

      It's definitely true that the AI in Essentials needs fixing and improving (and it'd be great if you could help with that!). I never really sat down to figure out all the numbers - there are many calculations involved and many more factors that could/should be taken into account.

      Perhaps the standard deviation threshold simply needs to be lowered. Maybe to 75? I don't know if that would be a suitable threshold in the majority of situations, though. Upping the score boost too would be a good idea (to something ridiculous just to make sure it's at least very highly likely to be chosen), so long as it doesn't also happen in situations you don't want it to.
      __________________
      Go to the Pokémon Essentials Wiki

      | Downloads | Tutorials | Fangames |
      | All Animations Project |

      Follow me on Twitter: @maruno42


      Reply With Quote
        #3    
      Old July 27th, 2013 (4:06 PM). Edited July 27th, 2013 by MC50.
      MC50 MC50 is online now
         
        Join Date: Aug 2006
        Gender:
        Nature: Adamant
        Posts: 28
        Basically, here's what I think the AI Should work like.

        Stat Boosting moves are very tough to balance. The CPU either never uses them, or spams them and dies before doing anything.

        The following factors should be taken into account, if they are possible.

        - How much of a threat is the opposing pokemon?
        - If they have already increased their attack or are viciously strong and will knock the CPU's pokemon out in one shot, why would they ever waste time with Swords Dance or Barrier? Assuming the CPU is faster, They should at least try to inflict a status (probably the ideal) or get some damage in.

        - How much have they already statted up?
        - In most cases, unless their attacks really arne't hurting the opponent much, there is no need to go to +6. The AI seems to already have this considered, but it is important! Usually, one swords dance, or two Dragon Dances, are enough. Unless the opponent is completely harmless to you.

        That's the biggest thing - When is it a good idea to stat up? When the opponent is no threat to you. If you are behind a Substitute. Things like that. If they can 2HK0 you and are faster hten you, boosting becomes a strange choice. There are of course situations where it can work - If the opponent does 80% of your life, you Swords Dance and your Salac activates, you may be able to OHKO them.

        - How much HP do you have left?
        - If your health is low, don't buff. Its as simple as that. If the opponent will KO you with their next move, DON'T use Dragon Dance!

        - Turn "1" is a good time to stat up. If the playing fields are even, starting out with a Dragon Dance or Swords Dance or similar move is usually a good idea.

        Reduce Randomness
        - Some randomness is good to catch the opponent off guard with a surprise move - You don't want the CPU to be completely predictable. But why would you ever use, say, a 1/4 effectiveness move against an opponent? That's not a gamble, that's idiocy.

        If you want to go really in-depth, think about what the foe could switch to. Let's go simple.

        CPU Pokemon
        - Honchkrow
        - Drill Peck/Night Slash/Superpower

        Opposing Pokemon
        - Machamp

        - Obviously, Drill Peck should be the move chosen most often. However, if the computer decides to go "Random" they should NOT pick Night Slash, but they COULD choose Superpower. Why?

        Because if the player is switching, they are probably trying to avoid a bad matchup. Honchkrow's types are dark and flying - So they won't switch in anything weak to those moves. So Night Slash will almost always be a terrible choice. Superpower, though it won't do a LOT to Machamp, will still be neutrally effective.

        I like that there's a chance of randomness to catch people off guard, but sometimes it does really dumb things. Maybe when the AI chooses a Random move, make them NOT use a move that will be hilariously ineffective to the opposing Pokemon, or that will fail completely? Not sure how it currently works.

        Well, this is all theoretical - I'm not sure how possible any of this stuff is to do. Like I said, I've been working in little quirks in the function codes and that seems to have been helping. Things like increasing the chance to use Light Screen or Reflect if the user holds Light Clay, or increasing the chance to use MudSport if the opponent is an electric type, or increasing the chance to use Seismic Toss if the foe has less HP then the users level. Simple things like that. I'm not worried about Trainer Skill right now.

        Another thing is when the AI loses a Pokemon and decides what Pokemon to switch in - This seems generally good, but I think it should take into consideration if you have a pokemon massively statted up - Say a Garchomp with 4 Dragon Dances. When deciding what pokemon to send out, it should try to pick ones that can actually take an action!

        The best two choices are of course

        Say, Pokemon that are faster than that Garchomp (somehow)
        A pokemon that can actually survive a hit from that Garchomp
        It should look at things like the Abilities of its own Pokemon. If a Pokemon has Sturdy or a Focus Sash. Things like that.

        If all of this is impossible, rather then just sending out Pokemon in a random order, it should send out first hte ones that COULD beat that Garchomp.

        For example, a Pokemon with Brightpowder. It COULD get lucky.
        Or, and this might be complex to code... If the Garchomps attacking moves are Earthquake and Dragon Rush, send out a Pokemon that it HAS to use Dragon Rush to KO FIRST, because that move is not 100% Accurate - It might miss. This might not be easy to code in, of course.


        That's all I can think of right now. Obviously this is really tough to balance, outside of simple stuff like, if Light Screen is currently up, ABSOLUTELY NEVER use Light Screen again - The AI has done that before, too. Though I know how to use RPG Maker (Spent a lot of time on RPG Maker 2000) I'm pretty useless with scripting. I've been trying to figure it out and am learning a LITTLE, but that's just it - a little.

        like, I couldn't even figure out how to turn that $INTERNAL on. I tried just making a script comment with it and putting it in an event, but I got an error. I see where it's supposed to write all the data for it in the AI script, though. Still, if you need more ideas I have plenty. I could post ideas to add to a lot of the function codes (I've done it for quite a few in my game, though it's still a ways off being done progress is being made) that would make the AI "Appear" smarter.


        As a side note, I guess this should go in the bug report topic, but I'll put it here - You changed the Stealth Rock code in Essentials 12, and now it fails completely. It does no damage, and it actually ends the turn as well when a Pokemon is switched in. Switching it back to the code from the old version works, but no clue if that will cause any other bugs. Spikes and such may also be effected - not confirmed.
        Reply With Quote
          #4    
        Old July 28th, 2013 (4:21 AM).
        Maruno's Avatar
        Maruno Maruno is offline
        Lead Dev of Pokémon Essentials
           
          Join Date: Jan 2008
          Location: England
          Posts: 5,189
          Like I said, there's obviously a lot going on with AI, and a lot that could go on. Part of my looking around revealed that any score changes due to effects are likely to be completely overshadowed by the score change due to potential damage dealt (a Geodude's Head Smash against my Pikachu got a score of 384, for example, which seems way too extreme a change), and status moves simply get -10 to their score by default so are almost bound to be overshadowed by any damaging move.

          I'm not working on Essentials at the moment (I've got something better to play with), but I would welcome any code tweaks you want to offer. An overhaul of the AI system would be a good idea at some point. The first part would be to figure out what ranges of numbers should be treated as "very preferred" or "not preferred", and how much of an effect potential damage (damage calculation including type effectiveness, etc.) should have on the score.

          Putting $INTERNAL=true in an event's script command (and then interacting with the event) will work. I've discovered that the log isn't written to a file unless it's long enough, which explains why you may not have seen anything. To make the log print out immediately, go into the script section PBDebug, find the following method and quote out the two lines shown:

          Code:
            def PBDebug.log(msg)
              if $DEBUG && $INTERNAL
                @@log.push("#{msg}\r\n")
          #      if @@log.length>1024
                  PBDebug.flush
          #      end
              end
            end
          The Stealth Rock bug you mentioned is true, and also applies to Rough Skin and a few other effects that inflict damage. I've fixed it now. Thanks for letting me know about it.
          Attached Thumbnails
          Untitled.png‎  
          __________________
          Go to the Pokémon Essentials Wiki

          | Downloads | Tutorials | Fangames |
          | All Animations Project |

          Follow me on Twitter: @maruno42


          Reply With Quote
            #5    
          Old July 28th, 2013 (5:32 AM). Edited July 28th, 2013 by Wootius.
          Wootius's Avatar
          Wootius Wootius is offline
          Glah
             
            Join Date: Mar 2013
            Gender: Male
            Posts: 306
            One thing I want to ask as I want to start improving the AI myself is does it do hard damage calculations? It looks like it doesn't.

            The "!" in "!opponent.pbCanSleep?(false)" changes a "TRUE" return to "FALSE" correct?


            Code:
            if move.basedamage>0 && pbTypeModifier(move.type,attacker,opponent) > 1
                       score += 200
            else pbTypeModifier(move.type,attacker,opponent) = 1
                       score += 50
            else pbTypeModifier(move.type,attacker,opponent) > 0 && pbTypeModifier(move.type,attacker,opponent) < 1
                       score -= 50
            end
            Would add 200 to the score if the move is super effective yes?
            Reply With Quote
              #6    
            Old July 28th, 2013 (5:41 AM).
            Maruno's Avatar
            Maruno Maruno is offline
            Lead Dev of Pokémon Essentials
               
              Join Date: Jan 2008
              Location: England
              Posts: 5,189
              No, it doesn't perform full damage calculations. It ignores various effects like held items and abilities that could affect damage (but does consider stat stages and type effectiveness), and comes up with a rough idea of the damage from that. This is then modified by various considerations, such as halving it if it's a two turn move and a couple of other effects which are part of regular damage calculation. Of course, how in-depth it goes with these effects can be changed.

              ! means "not". So yes, what you said means "if the opponent can't be inflicted with sleep".
              __________________
              Go to the Pokémon Essentials Wiki

              | Downloads | Tutorials | Fangames |
              | All Animations Project |

              Follow me on Twitter: @maruno42


              Reply With Quote
                #7    
              Old July 28th, 2013 (5:47 AM). Edited July 28th, 2013 by Wootius.
              Wootius's Avatar
              Wootius Wootius is offline
              Glah
                 
                Join Date: Mar 2013
                Gender: Male
                Posts: 306
                Ohhhh, it's just a shortcut instead of typing. I thought it was reversing the return itself.

                For

                Code:
                when 0x19
                       party=pbParty(opponent.index)
                       statuses=0
                       for i in 0...party.length
                          statuses+=1 if party[i] && party[i].status!=0
                       end
                        score = 0 if statuses==0
                       ##check
                shouldn't "opponent" be "attacker", in order to check the attacker's(the users of the move yes?) party?
                Reply With Quote
                  #8    
                Old July 28th, 2013 (7:30 AM).
                Maruno's Avatar
                Maruno Maruno is offline
                Lead Dev of Pokémon Essentials
                   
                  Join Date: Jan 2008
                  Location: England
                  Posts: 5,189
                  Yes, I suppose it should be attacker.


                  Quote:
                  Originally Posted by lauerolus View Post
                  Code:
                  if move.basedamage>0 && pbTypeModifier(move.type,attacker,opponent) > 1
                             score += 200
                  else pbTypeModifier(move.type,attacker,opponent) = 1
                             score += 50
                  else pbTypeModifier(move.type,attacker,opponent) > 0 && pbTypeModifier(move.type,attacker,opponent) < 1
                             score -= 50
                  end
                  Would add 200 to the score if the move is super effective yes?
                  pbTypeModifier returns a value of 4 for normal effectiveness. It's 8 for super-effective, and 16 for super-super effective (i.e. against both types). Similarly, 2 is not very effective, and 1 is really not very effective. 0 is immune.

                  It'd also be a good idea to put the if move.basedamage>0 in an outer if statement, since all three calculations should only apply to damaging moves. Something like this:
                  Code:
                  if move.basedamage>0
                    eff=pbTypeModifier(move.type,attacker,opponent)
                    if eff > 4   # Super-effective
                      score += 200
                    elsif eff == 4   # Normal effectiveness
                      score += 50
                    elsif eff > 0   # Not very effective
                      score -= 50
                    elsif eff == 0   # Immune
                      score -= 80
                    end
                  end
                  __________________
                  Go to the Pokémon Essentials Wiki

                  | Downloads | Tutorials | Fangames |
                  | All Animations Project |

                  Follow me on Twitter: @maruno42


                  Reply With Quote
                    #9    
                  Old July 28th, 2013 (8:20 AM). Edited July 28th, 2013 by Wootius.
                  Wootius's Avatar
                  Wootius Wootius is offline
                  Glah
                     
                    Join Date: Mar 2013
                    Gender: Male
                    Posts: 306
                    I wasn't trying to be funny, just making sure I understand how things work. Thanks for improving that, it keep throwing a syntax error for the normal effectiveness check in my (bad) code.

                    Code:
                    obsts = Array.new
                    obsts = opponent.pokemon.baseStats()
                    should set that to be an array of the opponent's BSTs correct? I'm using this for a Toxic scoring, where Toxic should get a large boost if the HP BST is high.
                    Reply With Quote
                      #10    
                    Old July 28th, 2013 (8:58 AM).
                    Maruno's Avatar
                    Maruno Maruno is offline
                    Lead Dev of Pokémon Essentials
                       
                      Join Date: Jan 2008
                      Location: England
                      Posts: 5,189
                      opponent.hp or opponent.totalhp should be fine for that. You might also want to check opponent.defense and opponent.spdef to figure out if they're going to be difficult to hurt, with Toxic being more favourable if those values are higher.
                      __________________
                      Go to the Pokémon Essentials Wiki

                      | Downloads | Tutorials | Fangames |
                      | All Animations Project |

                      Follow me on Twitter: @maruno42


                      Reply With Quote
                        #11    
                      Old July 28th, 2013 (9:22 AM). Edited July 28th, 2013 by Wootius.
                      Wootius's Avatar
                      Wootius Wootius is offline
                      Glah
                         
                        Join Date: Mar 2013
                        Gender: Male
                        Posts: 306
                        You may be right. I was worried about it spamming Toxic on full HP, low total HP but high level pokemon. A current HP % check combined with a BST check may be the thing to do for Toxic. Along with a high def/sdef check.

                        Code:
                        			when 0x06
                        			obsts = Array.new
                        			obsts = opponent.pokemon.baseStats()
                        			
                        			if move.basedamage==0				
                        				if !opponent.pbCanPoison?(false) 
                        					score = 0
                        				elsif opponent.status!=0
                        					score = 0
                        				elsif obsts[3] >= 90
                        					score += 130
                        				elsif isConst?(opponent.ability,PBAbilities,:GUTS)
                        					score -= 100
                        				elsif isConst?(opponent.ability,PBAbilities,:FLAREBOOST)
                        					score -= 100
                        				elsif isConst?(opponent.ability,PBAbilities,:TOXICBOOST)  
                        					score -= 100
                        				elsif opponent.effects[PBEffects::Yawn]>0
                        					score-=100
                        				else
                        					score += 25
                        				end		
                        				
                        			end
                        			
                        			if move.basedamage>0
                        				eff=pbTypeModifier(move.type,attacker,opponent)
                        				if eff > 4   # Super-effective
                        					score += 50
                        				elsif eff == 4   # Normal effectiveness
                        					score += 0
                        				elsif eff > 0   # Not very effective
                        					score -= 25
                        				elsif eff == 0   # Immune
                        					score == 0
                        				end
                        			end
                        That's what my current Toxic effect scoring looks like, with score = 50 as the base. When I look at the debug log, a Magikarp values Tackle at 62, Toxic at 65 and Twave at 65 on a target that don't fit the BST scoring increases.

                        I'm basically making my way down the list and redoing the scoring to take into account status/abilities(stuff like steadfast and motor drive)/attack effectiveness now
                        Reply With Quote
                          #12    
                        Old July 28th, 2013 (2:17 PM).
                        Maruno's Avatar
                        Maruno Maruno is offline
                        Lead Dev of Pokémon Essentials
                           
                          Join Date: Jan 2008
                          Location: England
                          Posts: 5,189
                          The elsif opponent.status!=0 part is a check included in the pbCanPoison method, so you don't need to include it here too.

                          You also shouldn't be checking the base stats of the Pokémon's species. You want to care about their actual stats (which are much easier to get to and more relevant). As I said, opponent.defense and opponent.spdef.

                          You don't need to care about Flare Boost, because you're inflicting poison, not a burn.

                          The type modifier stuff is a separate calculation below, so you don't need it here. This part of the code is solely about the effect and whether it'd be a good or a bad thing to try to to (i.e. inflict toxic).

                          With that all said, here's my take on the AI:

                          Code:
                          when 0x06
                            if opponent.pbCanPoison?(false)
                              score += 40
                              score += 40 if opponent.defense > 90
                              score += 40 if opponent.spdef > 90
                              score -= 50 if isConst?(opponent.ability,PBAbilities,:GUTS)
                              score -= 50 if isConst?(opponent.ability,PBAbilities,:TOXICBOOST) 
                              score -= 30 if opponent.effects[PBEffects::Yawn]>0
                            else
                              score = 0 if move.basedamage==0
                            end
                          The first part to look at is whether the opponent can be poisoned. If they can't, the score is set to 0 if the move does no damage. There's no such move, but if a damaging toxic move existed, it would be a move with no effect if it couldn't do its toxic part, and would need a score of 100 like the rest of the effectless moves.

                          If it can poison the target, then that's a good thing and you get an automatic score boost. You get an even higher score boost if the opponent is harder to hit (looking at each stat separately, where Bulbapedia says 83 is the average defensive stat value between all fully-evolved species). Conversely, you have a score reduction if the opponent will benefit from being poisoned or you know is going to promptly lose that status.

                          As far as numbers, they're smaller than what you stated, but they can be scaled to suit all the other numbers and that's not the thing to focus on. What's more important is the size of each boost/reduction compared to each other. I figured that, since toxic is slow to start up, the move's automatic bonus should be outweighed by the benefit the opponent gets from the status. Yawn is a slightly lower drop because (as far as I remember) it'll do at least a bit of damage before the opponent falls asleep.

                          I didn't include a number of things which would be nice, such as:
                          • Boost the score if the target can't switch out (either because of some effect or because there's no other Pokémon to switch to) - the opponent would be stuck with the toxic in that case and couldn't get rid of it by switching out.
                          • A check of what the opponent could do to heal itself of toxic (moves/held item/trainer's items) and lower the score if they have a counter.
                          • An analysis of which damaging physical/special moves the user has, which would affect the score boosts for each of the two defensive stats (either a bigger boost if the user's attacks will tend to go against a high defensive stat and/or a lower stat value threshold for applying this boost). Said analysis should also include looking at the opponent's stat modifiers.
                          • Check the state of the battle, and prefer to inflict toxic if the opponent is well off and needs grinding down - the existing AI does something like this by providing a boost during the first few turns, but that doesn't seem like the best way to go about it.
                          • The score changes should depend on the trainer's skill level, e.g. only highly skilled trainers would consider the Guts/Toxic Boost part or some of the other points in this list.
                          • Any other factors revolving around poisoning and statuses that I can't think of.
                          I'm no expert in AI. Those are just my thoughts.
                          __________________
                          Go to the Pokémon Essentials Wiki

                          | Downloads | Tutorials | Fangames |
                          | All Animations Project |

                          Follow me on Twitter: @maruno42


                          Reply With Quote
                            #13    
                          Old July 28th, 2013 (2:54 PM). Edited July 28th, 2013 by Wootius.
                          Wootius's Avatar
                          Wootius Wootius is offline
                          Glah
                             
                            Join Date: Mar 2013
                            Gender: Male
                            Posts: 306
                            That's a much better way. I need to stop reinventing the wheel and read the rest of the class before I do any more. The .sp/defense is just their flat stats and still has my problem of not applying early on. A percentage check(x% higher then your attacking stat) would be better for choosing when to use stat lowing moves. But I'm going to read everything before I continue on.

                            I see Toxic as being useful against either high HP(that still has it) and high defenses. Otherwise a Burn, Freeze(-SpA instead of incapacitate in my hack), or Paralyze would be more useful for dealing with said pokemon.

                            But I see evaluating attacking moves with a chance of status effect differently. Unless you're Graced, the effect isn't really why you're using said move so there shouldn't be a bonus. I'll give the AI Toxic/Poison Gas if I care about the status. Having the AI favor moves with an effect chance could mess up TWave/Spore usage as an example. What I'm trying to say is you don't want to favor a neutral move with an effect chance over a same BP neutral move with no effect chance.


                            Thanks for putting up with me.
                            __________________
                            Pokemon Rebalanced: Red
                            Where every Pokemon is useful.
                            Reply With Quote
                              #14    
                            Old July 29th, 2013 (3:48 AM).
                            Maruno's Avatar
                            Maruno Maruno is offline
                            Lead Dev of Pokémon Essentials
                               
                              Join Date: Jan 2008
                              Location: England
                              Posts: 5,189
                              Quote:
                              Originally Posted by lauerolus View Post
                              What I'm trying to say is you don't want to favor a neutral move with an effect chance over a same BP neutral move with no effect chance.
                              I entirely disagree. All other things being equal (type/category/power), you'd want to go with the move that also has a chance of doing another bad thing to your opponent. The whole point of the function code part of the AI is to decide whether these extra effects are worth doing, and to make those moves more/less likely to be chosen.
                              __________________
                              Go to the Pokémon Essentials Wiki

                              | Downloads | Tutorials | Fangames |
                              | All Animations Project |

                              Follow me on Twitter: @maruno42


                              Reply With Quote
                                #15    
                              Old July 29th, 2013 (2:23 PM).
                              Wootius's Avatar
                              Wootius Wootius is offline
                              Glah
                                 
                                Join Date: Mar 2013
                                Gender: Male
                                Posts: 306
                                I'm not disagreeing with the +40s on S/Def > 90 checks, as then Poison is good.

                                I'm disagreeing with the flat +40. I feel if the target doesn't fit "poisoning" criteria then it shouldn't get a bonus.

                                What if the AI is fighting a speedy pokemon and because the AI weighed the neutral Poison effect move higher then a same range no effect move it was poisoned when the AI's next pokemon had the ability to Paralyze it?

                                I know the ideal answer to the above is to either switch in to paralyze it or to switch in something that has resisted its so far revealed moves/typing and I will be looking at that.
                                __________________
                                Pokemon Rebalanced: Red
                                Where every Pokemon is useful.
                                Reply With Quote
                                Reply

                                Quick Reply

                                Join the conversation!

                                Create an account to post a reply in this thread, participate in other discussions, and more!

                                Create a PokéCommunity Account

                                Sponsored Links
                                Thread Tools

                                Posting Rules
                                You may not post new threads
                                You may not post replies
                                You may not post attachments
                                You may not edit your posts

                                BB code is On
                                Smilies are On
                                [IMG] code is On
                                HTML code is Off

                                Forum Jump


                                All times are GMT -8. The time now is 10:36 PM.