RPG Battle Engine - Alpha 7.5, downloads in first post

A place for Ren'Py tutorials and reusable Ren'Py code.
Forum rules
Do not post questions here!

This forum is for example code you want to show other people. Ren'Py questions should be asked in the Ren'Py Questions and Announcements forum.
Post Reply
Message
Author
User avatar
azureXtwilight
Megane Procrastinator
Posts: 4118
Joined: Fri Mar 28, 2008 4:54 am
Completed: Fantasia series (ROT and ROTA), Doppleganger: Dawn of The Inverted Soul, a2 (a due), Time Labyrinth
Projects: At Regime's End
Organization: Memento-Mori VNs, Team Sleepyhead
Location: Yogyakarta, Indonesia.
Contact:

Re: RPG Battle Engine - Alpha 7.5, downloads in first post

#61 Post by azureXtwilight »

Yes, that's true! There are people who don't want to go through a lot of trouble making difficult battles, so I'm sure your solution would work well for free-game makers! :D
Image

User avatar
AERenoir
Veteran
Posts: 320
Joined: Fri May 27, 2011 8:23 pm
Contact:

Re: RPG Battle Engine - Alpha 7.5, downloads in first post

#62 Post by AERenoir »

So I was wondering how to make certain stats and inventory items affect your attack and defense. If I have an armor, I will receive less damage. If I have 100 strength, I cause more damage on the enemy than if I have 0 strength.

Jake
Support Hero
Posts: 3826
Joined: Sat Jun 17, 2006 7:28 pm
Contact:

Re: RPG Battle Engine - Alpha 7.5, downloads in first post

#63 Post by Jake »

AERenoir wrote:So I was wondering how to make certain stats and inventory items affect your attack and defense. If I have an armor, I will receive less damage. If I have 100 strength, I cause more damage on the enemy than if I have 0 strength.
There's a class called 'AttackResolver', which calculates how the strength of an attack turns into damage - the attacker, defender, and the attack-strength that came out of the skill are all available in there.
The AttackResolver base class is in engine-schema.rpy, along with a DefaultAttackResolver (which is the really basic one in the active demo) and an example ElementalAttackResolver (as used in the grid demo). The 'right' approach is to copy one of those classes out into your own script files, rename it and modify it there. Then create a new CustomSchema based on one of the existing schemata (e.g. ActiveSchema), and add in your new AttackResolver and use that CustomSchema for your battle.

(If you edit the class in the engine-schema.rpy file then it'll get lost if you update the engine package.)

[Edited to correct my apparently lousy memory.]

(As to items: stat-modifying items should be added to the fighter as equipment - and equipment items have the facility to modify particular fighter stats, so an equipped shield may make the fighter's Defence stat higher. There's examples of this in the equipment demo which is packed in with the engine.)
Server error: user 'Jake' not found

Jake
Support Hero
Posts: 3826
Joined: Sat Jun 17, 2006 7:28 pm
Contact:

Re: RPG Battle Engine - Alpha 7.5, downloads in first post

#64 Post by Jake »

Right, here's an example of how to create your own attack resolver. The AttackResolver is the thing which handles any attacks made through the function call:

Code: Select all

    battle.Attack(attacker, attackStrength, attackAttributes, target, range)
Any 'normal' attacks should call this function directly - you can find an example in the AttackSkill class in engine-skills.rpy, which uses this method to commit the actual physical attack when the skill gets used.

The skill class can determine the 'attack strength' any way it wants, including referring to the stats of the attacker or defender. All that matters is that a single attack-magnitude value gets passed to the Attack method.

So, here's the AttackResolver class:

Code: Select all

    class AttackResolver(object):
        def __init__(self):
            None
        def SetUpFighter(self, fighter):
            None
        def ResolveAttack(self, attacker, attack, attributes, target, range=1, **parameters):
            None
The SetUpFighter method is called once for every fighter in the battle - so if there's any stats that your attack resolver relies on, you should register them here via the RegisterSkill method. If the fighter already has that stat it won't be changed, but if they don't then RegisterSkill makes sure that they have it, and it has a default value, because otherwise the code may fall over when you try and reference the skill.

The ResolveAttack method is called whenever an attack is made. The 'attacker' parameter is the fighter making the attack; 'attack' is the strength of the attack, 'attributes' is a list of text attributes that apply to that attack, such as 'magic' or 'fire' or 'melee' (see the ElementalAttackResolver for an example of use), 'target' is the fighter being attacked, and 'range' is the distance between the attacker and defender.

ResolveAttack should all the Damage method below on the target fighter to apply any damage that occurs from the attack - or make any other changes to its stats etc. directly.

Code: Select all

(on fighter)
    def Damage(self, damage, damager)
Here the 'damage' parameter is literally the amount of the target's health to subtract, and 'damager' is the fighter who inflicted that damage upon them (which is needed for various events).





So here's an example of creating a new attack resolver, and using it in a battle:

Code: Select all

init python:
        class MyAttackResolver(AttackResolver):
                    
            def SetUpFighter(self, fighter):
                fighter.RegisterStat("Dexterity", 10)
                fighter.RegisterStat("Armour", 0)
                fighter.RegisterStat("Agility", 10) 
                
            def ResolveAttack(self, attacker, attack, attributes, target, range=1):
                
                damage = 0
                
                #calculate chance of scoring a hit - multiplying the ratio between attacker's dexterity and target's agility by 50 to get a percentage
                # So if the two stats are equal, it's a 50/50 chance; a lower agility means more chance of hitting, and a lower dexterity means less chance.
                # Someone with double the agility will always be able to dodge their inept attacker.
                hitChance = (attacker.Stats.Dexterity / target.Stats.Agility) * 50

                # Check for a hit - random number between 0 and 1, multiplied by 100 to get a random num between 0 and 100, compared to the chance:
                if (renpy.random.random() * 100) <= hitChance:
                    # It's a hit
                    
                    # Armour here is a percentage chance of avoiding damage - so armour 0 is "pretty much always take damage", 
                    # armour 50 is "take damage half of the time", etc.
                    
                    if (renpy.random.random() * 100) > target.Stats.Armour:
                        # armour fails to stop damage
                        
                        damage = attack

                # Whatever damage value has been reached, apply that to the target.
                target.Damage(damage, attacker)

I've just made up a quick system - it uses Dexterity and Agility stats to decide whether a hit is scored, and then gives a chance of Armour nullifying that hit. The strength of the attack passed from the attack skill is used directly as the damage scored.

So, in the SetUpFighter method, I need to register my three new stats, with reasonable default values for each. These are the values a fighter will have if that stat isn't explicitly set up when you create the fighter.

In the ResolveAttack method, I calculate a value for damage, setting it to 0 as a default and then checking that the attack hits, and then that the armour doesn't deflect it, using Ren'Py's random number facility to perform virtual 'dice rolling' or whatever.

Then finally, the 'Damage' method is called on the target fighter to actually inflict that damage against their health.

(It's important to call the Damage method and not just change the Health directly - you can change it, but if you don't call Damage then the system won't check to see whether the fighter is dead or not!)

Next, we create a CustomSchema based on ActiveSchema, and insert the new attack resolver in... and then use that when constructing the battle. You could use any of the other schemata instead of ActiveSchema if you want, the attack resolver you specify will just take the place of whatever the default for that schema is.

Code: Select all

    python:
        mySchema = CustomSchema(ActiveSchema, attackResolver=MyAttackResolver)
        battle = Battle(mySchema)
        battle.SetBattlefield(testBattlefield)
        ...


I'll write all this up into a proper tutorial on the battle engine website at some point - if you have any questions, feel free to ask.
Server error: user 'Jake' not found

Friendbot2000
Regular
Posts: 161
Joined: Tue Feb 15, 2011 8:00 pm
Projects: Mutagen : Journey to Haven's Landing
Contact:

Re: RPG Battle Engine - Alpha 7.5, downloads in first post

#65 Post by Friendbot2000 »

So I am fiddling with the BattleEngine to create my own custom engine for my game and I just want a reality check here for my peace of mind. So I noticed in the ActiveBattleDemo that when the engine sets up the battle it adds the player character along with his stats in the actual battle setup code. So I fiddled around with the code and got the idea to make each player character a separate module that you can just reference and have all the stats loaded without duplicating code, which is always a bad idea. The only thing you really have to do is specify the anchor for the sprites position and you are golden. I am not sure if I wrote the module correctly so correct any mistakes I may have made. It is just a rough draft right now.

Code: Select all

def geoff ()
    geoffSprite = BattleSprite('geoff')
    geoff = PlayerFighter("Geoff", Speed=13, Attack=7, Defence=10, MP=20, sprite=geoffSprite)
    geoff.RegisterSkill(Library.Skills.SwoardAttack)
    geoff.RegisterSkill(Library.Skills.Skip)
    geoff.RegisterSkill(Library.Skills.Fire1)

I do have a question about autoscrolling though. How would I set up the battle to scroll when the cursor reaches the edge of the screen instead of using the arrows in the demo?
Visit my game development group's Facebook page : Timekeeper Games
Mutagen : Journey to Haven's Landing Facebook Page
Follow our Twitter feed too : TK Games

Jake
Support Hero
Posts: 3826
Joined: Sat Jun 17, 2006 7:28 pm
Contact:

Re: RPG Battle Engine - Alpha 7.5, downloads in first post

#66 Post by Jake »

Friendbot2000 wrote: I noticed in the ActiveBattleDemo that when the engine sets up the battle it adds the player character along with his stats in the actual battle setup code.
This is largely just for the purposes of having all the demo code in the one file... the best approach in most cases - RPG-style games where the same character is used over and over again - will be to simply create the fighter once right at the start of your game (after the start label, not in an init block, to avoid problems with save/load) and then re-use the same instance over and over again.

If you're doing a game in which there's no stat advancement (levelling etc.) and you just want generic reusable units (a la Advance Wars) then your idea of writing a function which returns a unit is probably the right idea. That's what I did for Tristan and Iseult, and it worked for me. ;-)

I've made a couple of changes (below), for two reasons. Firstly because you had your function name and your variable name the same, which can be problematic. Secondly I changed it to return the fighter created, because otherwise you'd be dealing with a global variable, and they're usually messy.

Code: Select all

def getGeoff ():
    geoffSprite = BattleSprite('geoff')
    geoff = PlayerFighter("Geoff", Speed=13, Attack=7, Defence=10, MP=20, sprite=geoffSprite)
    geoff.RegisterSkill(Library.Skills.SwordAttack)
    geoff.RegisterSkill(Library.Skills.Skip)
    geoff.RegisterSkill(Library.Skills.Fire1)

    return geoff
then in your battle, you could simply call:

Code: Select all

    battle.AddFighter(GetGeoff())
(Obviously passing in position and whatnot as well if you're using anything other than a simple face-to-face battlefield.)
Friendbot2000 wrote: I do have a question about autoscrolling though. How would I set up the battle to scroll when the cursor reaches the edge of the screen instead of using the arrows in the demo?
I think there's an extra for this somewhere in the engine code, but I don't recall for certain, I'll look it up at lunch. I think it works, but it's been a long time since I played with it.

Bear in mind that doing it this way makes it very awkward for people who play in windowed mode to scroll, and makes it annoying to click on things around the edges of the screen.
Server error: user 'Jake' not found

Friendbot2000
Regular
Posts: 161
Joined: Tue Feb 15, 2011 8:00 pm
Projects: Mutagen : Journey to Haven's Landing
Contact:

Re: RPG Battle Engine - Alpha 7.5, downloads in first post

#67 Post by Friendbot2000 »

Oh, I totally realized why you put it in one file :)

You are right though, for actual characters it would probably be best to place all their characteristics and stats at the beginning rather than modularize them. Hmmm doing enemies ala Advance Wars seems like a good idea.

As for autoscrolling...I can see your point about windowed mode, but I also would find it a bit annoying if I had to keep clicking arrows...however, I could always map to the arrow keys or other keyboard keys to move the camera...It is worth looking into because most likely my game won't be fullscreen.

Although, if you define the characters after the label start, you edit their stats as they level up by appending the data with the xp/level up code right?
Visit my game development group's Facebook page : Timekeeper Games
Mutagen : Journey to Haven's Landing Facebook Page
Follow our Twitter feed too : TK Games

Jake
Support Hero
Posts: 3826
Joined: Sat Jun 17, 2006 7:28 pm
Contact:

Re: RPG Battle Engine - Alpha 7.5, downloads in first post

#68 Post by Jake »

OK! The autoscroll code is in the file called (who knew?) autoscroll.rpy. To add it to your battle, simply call:

Code: Select all

    battle.AddExtra(AutoScroll())
It works fine on my in-development codebase, and I've only been working on the scripting since the last release so I expect you'll have no problems.



The other option you may consider is dynamic panning - a combination of using the ActionPanner extra (which pans to 'points of interest' generated by various skills/extras/etc.) and the 'poi=True' parameter to the MoveSkill, which generates a point of interest at every single step along the route.

The ScrollMove skill is set up with the poi parameter set, although it looks like I got distracted and used PathMove for that demo in the end! You can see it in assets.rpy, anyway:

Code: Select all

    Library.Skills.ScrollMove = MoveSkill(endTurn=True, poi=True)
If you use that parameter for everyone's move skill, and add the ActionPanner extra to all your battles, then the camera should keep up with the current fighter to the point that the user has to do a lot less panning anyway.

(I recommend using ActionPanner whenever you have panning in your game anyway.)
Friendbot2000 wrote: Although, if you define the characters after the label start, you edit their stats as they level up by appending the data with the xp/level up code right?
Yep - if you want to allow your fighters to level up, you need to do two things:

- Set up level plans for them as in the xp-demo.rpy demo, so they gain particular bonuses when they hit particular XP thresholds, and
- Add some kind of XP-generating extra to your battles. Again, xp-demo.rpy explains these, you have the choice between gaining XP for doing damage, gaining XP for killing enemies, or writing your own XP-gain extra.
Server error: user 'Jake' not found

Friendbot2000
Regular
Posts: 161
Joined: Tue Feb 15, 2011 8:00 pm
Projects: Mutagen : Journey to Haven's Landing
Contact:

Re: RPG Battle Engine - Alpha 7.5, downloads in first post

#69 Post by Friendbot2000 »

Code: Select all

# Order a list to have the items with a higher Priority nearer the beginning of the list.
    def Battle_Priority_Compare(a, b):
        if a.Priority > b.Priority:
            return -1
        elif b.Priority > a.Priority:
            return 1
        else: #equal
            return 0
       
 # Order a list to have the items with a Position nearer the bottom of the screen appear
    # later in the list (nearer left to break ties)
    def Battle_Draw_Compare(a, b):
        if (a.Position is None and b.Position is None):
            return 0
        elif (a.Position is None):
            return 1
        elif (b.Position is None):
            return -1
        elif (a.Position.Y < b.Position.Y):
            return -1
        elif (b.Position.Y < a.Position.Y):
            return 1
        elif (a.Position.X < b.Position.X):
            return -1
        elif (b.Position.X < a.Position.Y):
            return 1
        else:
            return 0
Jake, I have a question about the code listed above that is part of the engine's schema. I am bit confused by the highlighted comments. What exactly are you talking about when you say "Position". Is it screen position? Is the code supposed to determine placement of graphics?
Visit my game development group's Facebook page : Timekeeper Games
Mutagen : Journey to Haven's Landing Facebook Page
Follow our Twitter feed too : TK Games

Jake
Support Hero
Posts: 3826
Joined: Sat Jun 17, 2006 7:28 pm
Contact:

Re: RPG Battle Engine - Alpha 7.5, downloads in first post

#70 Post by Jake »

Friendbot2000 wrote: Jake, I have a question about the code listed above that is part of the engine's schema. I am bit confused by the highlighted comments. What exactly are you talking about when you say "Position". Is it screen position? Is the code supposed to determine placement of graphics?
Those are just utility functions to sort lists of things using the list's sort method. They're in the engine-schema file, but technically they're not part of the BattleSchema class.

The first one compares two items by their priority - which is the field on a Fighter which determines how 'ready' they are for their next turn, and this is used in ActiveSchema battles in particular

The second one compares two items by their position, which used to be used for ordering the fighters for display on the screen - it's basically ordering the list so that things nearer the top of the screen are at one end of the list, and things nearer the bottom of the screen are at the other, because the engine used to use this for making sure that things which looked 'in front' were actually drawn in front of other things. I can't remember, it may even have been that the engine was originally written before PyTom added the z parameter to renpy.show.

Anyway, the 'Position's that it talks about are instances of the BattlePosition class, which describes the position of particular fighters/scenery/whatever in the battle. It's not always literally a screen (x,y) coordinate, but it is sometimes. For the most part, positions are hidden from the game-maker; you only have to create and deal with them specifically in path battles, where they do actually represent particular screen coordinates.



I'm not sure what you mean by "determine placement of graphics", so I don't know if that answers your question specifically, but if you have any further questions feel free to ask.
Server error: user 'Jake' not found

Friendbot2000
Regular
Posts: 161
Joined: Tue Feb 15, 2011 8:00 pm
Projects: Mutagen : Journey to Haven's Landing
Contact:

Re: RPG Battle Engine - Alpha 7.5, downloads in first post

#71 Post by Friendbot2000 »

Thanks Jake, you answered my question perfectly :)
Visit my game development group's Facebook page : Timekeeper Games
Mutagen : Journey to Haven's Landing Facebook Page
Follow our Twitter feed too : TK Games

Friendbot2000
Regular
Posts: 161
Joined: Tue Feb 15, 2011 8:00 pm
Projects: Mutagen : Journey to Haven's Landing
Contact:

Re: RPG Battle Engine - Alpha 7.5, downloads in first post

#72 Post by Friendbot2000 »

Code: Select all

# Note that we're just passing the name of the image defined in assets.rpy; we could equally pass a Displayable instance if we preferred. 
So I have yet another question for you Jake, sorry for the bombardment of questions.

I was wondering what the benefits are of passing the name of the background image in the assets.rpy versus creating a displayable instance. What benefits do you get from either of them and what drawbacks are there to either method?
Visit my game development group's Facebook page : Timekeeper Games
Mutagen : Journey to Haven's Landing Facebook Page
Follow our Twitter feed too : TK Games

Jake
Support Hero
Posts: 3826
Joined: Sat Jun 17, 2006 7:28 pm
Contact:

Re: RPG Battle Engine - Alpha 7.5, downloads in first post

#73 Post by Jake »

There's no reason to apologise for questions!
Friendbot2000 wrote: I was wondering what the benefits are of passing the name of the background image in the assets.rpy versus creating a displayable instance. What benefits do you get from either of them and what drawbacks are there to either method?
There's no real drawback to passing in a displayable - it's just more code, so since the battle engine doesn't care, it's easier to pass in the image name.

If you're using ATL or image statements to set up your images anyway, then there's no real advantage to passing in a displayable either - the only time I can think of off-hand that it would be a benefit is if you want to code a custom displayable and pass that in.



(This goes for anywhere you pass an image into the battle engine, by the way, not just backgrounds. Or at least, it should - if you find anywhere it's not the case, let me know!)
Server error: user 'Jake' not found

Friendbot2000
Regular
Posts: 161
Joined: Tue Feb 15, 2011 8:00 pm
Projects: Mutagen : Journey to Haven's Landing
Contact:

Re: RPG Battle Engine - Alpha 7.5, downloads in first post

#74 Post by Friendbot2000 »

Haha, okay. I was just asking because your comment kind of alluded to there being a benefit to using the reference to the asset file. At least that was my inference :P
Visit my game development group's Facebook page : Timekeeper Games
Mutagen : Journey to Haven's Landing Facebook Page
Follow our Twitter feed too : TK Games

Jake
Support Hero
Posts: 3826
Joined: Sat Jun 17, 2006 7:28 pm
Contact:

Re: RPG Battle Engine - Alpha 7.5, downloads in first post

#75 Post by Jake »

Friendbot2000 wrote:Haha, okay. I was just asking because your comment kind of alluded to there being a benefit to using the reference to the asset file. At least that was my inference :P
It wasn't intended that way - it was just easier to set up that way for demo purposes. ;-)
Server error: user 'Jake' not found

Post Reply

Who is online

Users browsing this forum: No registered users