Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

contrib: Magic, items systems for 'turnbattle' #1518

Merged
merged 46 commits into from Jun 12, 2018
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
84c8284
Added tb_magic.py - only basic input parsing
FlutterSprite Nov 10, 2017
d87be0c
Added functional 'cure wounds' spell
FlutterSprite Nov 13, 2017
792c549
Added attack spells, more healing spell variants
FlutterSprite Nov 13, 2017
19c278a
Formatting and documentation
FlutterSprite Nov 15, 2017
473fbe8
Comments and documentation, CmdStatus() added
FlutterSprite Nov 15, 2017
89ed833
Final touches
FlutterSprite Nov 15, 2017
7fbd473
Merge pull request #1 from evennia/develop
FlutterSprite Nov 16, 2017
97ab816
Create tb_items.py
FlutterSprite Nov 16, 2017
57cfb8e
Added 'use' command, item functions, example items
FlutterSprite Nov 16, 2017
fd5f6ba
Proper implementation of spend_item_use()
FlutterSprite Nov 16, 2017
9f8c92e
Move some item logic from CmdUse to new func use_item
FlutterSprite Nov 18, 2017
c3ffa7b
Start porting in condition code from coolbattles
FlutterSprite Nov 18, 2017
b54a167
Fix weird spacing in use_item()
FlutterSprite Nov 18, 2017
d9d4a82
Added functional condition, TickerHandler countdown
FlutterSprite Nov 18, 2017
228c474
Fix condition ticking
FlutterSprite Nov 18, 2017
df36be1
Merge pull request #2 from FlutterSprite/develop
FlutterSprite Nov 18, 2017
9f39f2e
Merge pull request #3 from FlutterSprite/tb_items
FlutterSprite Nov 18, 2017
0a9d23c
Add "Poisoned" condition, more condition items
FlutterSprite Nov 19, 2017
93d5cb6
More documentation, 'True' duration for indefinite conditions
FlutterSprite Nov 19, 2017
d60f23e
More documentation, fix error in at_update()
FlutterSprite Nov 19, 2017
ad27246
More conditions and documentation
FlutterSprite Nov 19, 2017
ea12145
Fixed all conditions lasting indefinitely
FlutterSprite Nov 19, 2017
fcac893
More item prototypes - probably ready to go!
FlutterSprite Nov 19, 2017
6417aaa
Update readme
FlutterSprite Nov 19, 2017
0b714a2
Fix readme spacing
FlutterSprite Nov 19, 2017
0fae112
Added options for conditions at top of module
FlutterSprite Nov 29, 2017
4ae2938
Merge pull request #4 from evennia/develop
FlutterSprite Nov 29, 2017
9119ffa
Merge pull request #5 from FlutterSprite/develop
FlutterSprite Nov 29, 2017
273b662
Unit tests for tb_items
FlutterSprite Nov 29, 2017
24866df
Attempt to fix TickerHandler error in unit tests
FlutterSprite Nov 29, 2017
d6a5af1
Manually unsubscribe ticker handler
FlutterSprite Nov 29, 2017
8cd3326
TickerHandler stuff, more
FlutterSprite Nov 29, 2017
36070c8
Ugh!!! TickerHandler changes, more
FlutterSprite Nov 29, 2017
3576219
Comment out tb_items tests for now
FlutterSprite Nov 30, 2017
4234ad2
Also remove ticker handler for 'attacker' and 'defender'
FlutterSprite Nov 30, 2017
619bf01
Just comment it all out.
FlutterSprite Nov 30, 2017
ccdc097
Comment it out right this time
FlutterSprite Nov 30, 2017
961a86b
Test character class without tickerhandler
FlutterSprite Dec 11, 2017
81ac38e
Add tickerhandler-free character class for tests
FlutterSprite Dec 11, 2017
f550c03
Use test character class instead
FlutterSprite Dec 11, 2017
3423cc9
Merge branch 'tb_magic' of http://github.com/FlutterSprite/evennia in…
FlutterSprite Dec 11, 2017
9e6de08
Unit tests for tb_magic
FlutterSprite Dec 11, 2017
812d256
Proper formatting for comments and notes
FlutterSprite May 22, 2018
f3031fe
Start splitting unit tests, add setUp/tearDown
FlutterSprite May 22, 2018
f7ae912
More setUp/tearDown
FlutterSprite May 22, 2018
d550f3e
Finish splitting TB test classes + adding setUp/tearDown
FlutterSprite May 22, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
219 changes: 218 additions & 1 deletion evennia/contrib/tests.py
Expand Up @@ -916,7 +916,7 @@ def test_outroroom(self):


# test turnbattle
from evennia.contrib.turnbattle import tb_basic, tb_equip, tb_range
from evennia.contrib.turnbattle import tb_basic, tb_equip, tb_range, tb_items, tb_magic
from evennia.objects.objects import DefaultRoom


Expand Down Expand Up @@ -962,6 +962,30 @@ def test_turnbattlerangecmd(self):
self.call(tb_range.CmdDisengage(), "", "You can only do that in combat. (see: help fight)")
self.call(tb_range.CmdRest(), "", "Char rests to recover HP.")

# Test item commands
def test_turnbattleitemcmd(self):
testitem = create_object(key="test item")
testitem.move_to(self.char1)
self.call(tb_items.CmdUse(), "item", "'Test item' is not a usable item.")
# Also test the commands that are the same in the basic module
self.call(tb_items.CmdFight(), "", "You can't start a fight if you've been defeated!")
self.call(tb_items.CmdAttack(), "", "You can only do that in combat. (see: help fight)")
self.call(tb_items.CmdPass(), "", "You can only do that in combat. (see: help fight)")
self.call(tb_items.CmdDisengage(), "", "You can only do that in combat. (see: help fight)")
self.call(tb_items.CmdRest(), "", "Char rests to recover HP.")

# Test magic commands
def test_turnbattlemagiccmd(self):
self.call(tb_magic.CmdStatus(), "", "You have 100 / 100 HP and 20 / 20 MP.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These all appear to be failure-state tests. Are these just intended to test that the commands exist without causing immediate syntax errors?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, yes. This is how it is for all the 'turnbattle' modules - I think going through and cleaning them up and making the tests more thorough and consistent might be its own project, part of making all the 'turnbattle' modules more streamlined.

self.call(tb_magic.CmdLearnSpell(), "test spell", "There is no spell with that name.")
self.call(tb_magic.CmdCast(), "", "Usage: cast <spell name> = <target>, <target2>")
# Also test the commands that are the same in the basic module
self.call(tb_magic.CmdFight(), "", "There's nobody here to fight!")
self.call(tb_magic.CmdAttack(), "", "You can only do that in combat. (see: help fight)")
self.call(tb_magic.CmdPass(), "", "You can only do that in combat. (see: help fight)")
self.call(tb_magic.CmdDisengage(), "", "You can only do that in combat. (see: help fight)")
self.call(tb_magic.CmdRest(), "", "Char rests to recover HP and MP.")


class TestTurnBattleFunc(EvenniaTest):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For future reference, unit tests are better split into more methods than this; the magic and items would be better as separate classes with multiple methods testing the different pieces of functionality that you mention in your comments. That way you can easily (re)run individual tests (you can run individual tests down to the method level) if one fails without having to rerun all of them.


Expand Down Expand Up @@ -1214,6 +1238,199 @@ def test_tbrangefunc(self):
self.assertTrue(tb_range.get_range(attacker, defender) == 1)
# Remove the script at the end
turnhandler.stop()

# Test functions in tb_items.
def test_tbitemsfunc(self):
attacker = create_object(tb_items.TBItemsCharacterTest, key="Attacker")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These creation-lines appear to be boiler-plate code. Put it in a setUp method of the tester class so you don't have to put this at the beginning of each test. You can also have a tearDown method if you want to do any house cleaning after each test.

defender = create_object(tb_items.TBItemsCharacterTest, key="Defender")
testroom = create_object(DefaultRoom, key="Test Room")
attacker.location = testroom
defender.loaction = testroom
# Initiative roll
initiative = tb_items.roll_init(attacker)
self.assertTrue(initiative >= 0 and initiative <= 1000)
# Attack roll
attack_roll = tb_items.get_attack(attacker, defender)
self.assertTrue(attack_roll >= 0 and attack_roll <= 100)
# Defense roll
defense_roll = tb_items.get_defense(attacker, defender)
self.assertTrue(defense_roll == 50)
# Damage roll
damage_roll = tb_items.get_damage(attacker, defender)
self.assertTrue(damage_roll >= 15 and damage_roll <= 25)
# Apply damage
defender.db.hp = 10
tb_items.apply_damage(defender, 3)
self.assertTrue(defender.db.hp == 7)
# Resolve attack
defender.db.hp = 40
tb_items.resolve_attack(attacker, defender, attack_value=20, defense_value=10)
self.assertTrue(defender.db.hp < 40)
# Combat cleanup
attacker.db.Combat_attribute = True
tb_items.combat_cleanup(attacker)
self.assertFalse(attacker.db.combat_attribute)
# Is in combat
self.assertFalse(tb_items.is_in_combat(attacker))
# Set up turn handler script for further tests
attacker.location.scripts.add(tb_items.TBItemsTurnHandler)
turnhandler = attacker.db.combat_TurnHandler
self.assertTrue(attacker.db.combat_TurnHandler)
# Set the turn handler's interval very high to keep it from repeating during tests.
turnhandler.interval = 10000
# Force turn order
turnhandler.db.fighters = [attacker, defender]
turnhandler.db.turn = 0
# Test is turn
self.assertTrue(tb_items.is_turn(attacker))
# Spend actions
attacker.db.Combat_ActionsLeft = 1
tb_items.spend_action(attacker, 1, action_name="Test")
self.assertTrue(attacker.db.Combat_ActionsLeft == 0)
self.assertTrue(attacker.db.Combat_LastAction == "Test")
# Initialize for combat
attacker.db.Combat_ActionsLeft = 983
turnhandler.initialize_for_combat(attacker)
self.assertTrue(attacker.db.Combat_ActionsLeft == 0)
self.assertTrue(attacker.db.Combat_LastAction == "null")
# Start turn
defender.db.Combat_ActionsLeft = 0
turnhandler.start_turn(defender)
self.assertTrue(defender.db.Combat_ActionsLeft == 1)
# Next turn
turnhandler.db.fighters = [attacker, defender]
turnhandler.db.turn = 0
turnhandler.next_turn()
self.assertTrue(turnhandler.db.turn == 1)
# Turn end check
turnhandler.db.fighters = [attacker, defender]
turnhandler.db.turn = 0
attacker.db.Combat_ActionsLeft = 0
turnhandler.turn_end_check(attacker)
self.assertTrue(turnhandler.db.turn == 1)
# Join fight
joiner = create_object(tb_items.TBItemsCharacterTest, key="Joiner")
turnhandler.db.fighters = [attacker, defender]
turnhandler.db.turn = 0
turnhandler.join_fight(joiner)
self.assertTrue(turnhandler.db.turn == 1)
self.assertTrue(turnhandler.db.fighters == [joiner, attacker, defender])
# Remove the script at the end
turnhandler.stop()
# Now time to test item stuff.
user = create_object(tb_items.TBItemsCharacterTest, key="User")
testroom = create_object(DefaultRoom, key="Test Room")
user.location = testroom
test_healpotion = create_object(key="healing potion")
test_healpotion.db.item_func = "heal"
test_healpotion.db.item_uses = 3
# Spend item use
tb_items.spend_item_use(test_healpotion, user)
self.assertTrue(test_healpotion.db.item_uses == 2)
# Use item
user.db.hp = 2
tb_items.use_item(user, test_healpotion, user)
self.assertTrue(user.db.hp > 2)
# Add contition
tb_items.add_condition(user, user, "Test", 5)
self.assertTrue(user.db.conditions == {"Test":[5, user]})
# Condition tickdown
tb_items.condition_tickdown(user, user)
self.assertTrue(user.db.conditions == {"Test":[4, user]})
# Test item functions now!
# Item heal
user.db.hp = 2
tb_items.itemfunc_heal(test_healpotion, user, user)
# Item add condition
user.db.conditions = {}
tb_items.itemfunc_add_condition(test_healpotion, user, user)
self.assertTrue(user.db.conditions == {"Regeneration":[5, user]})
# Item cure condition
user.db.conditions = {"Poisoned":[5, user]}
tb_items.itemfunc_cure_condition(test_healpotion, user, user)
self.assertTrue(user.db.conditions == {})
# Delete the test character
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This kind of cleanup belongs in a tearDown method, it will then be called after automatically after each test-method in the class.

user.delete()

# Test combat functions in tb_magic.
def test_tbbasicfunc(self):
attacker = create_object(tb_magic.TBMagicCharacter, key="Attacker")
defender = create_object(tb_magic.TBMagicCharacter, key="Defender")
testroom = create_object(DefaultRoom, key="Test Room")
attacker.location = testroom
defender.loaction = testroom
# Initiative roll
initiative = tb_magic.roll_init(attacker)
self.assertTrue(initiative >= 0 and initiative <= 1000)
# Attack roll
attack_roll = tb_magic.get_attack(attacker, defender)
self.assertTrue(attack_roll >= 0 and attack_roll <= 100)
# Defense roll
defense_roll = tb_magic.get_defense(attacker, defender)
self.assertTrue(defense_roll == 50)
# Damage roll
damage_roll = tb_magic.get_damage(attacker, defender)
self.assertTrue(damage_roll >= 15 and damage_roll <= 25)
# Apply damage
defender.db.hp = 10
tb_magic.apply_damage(defender, 3)
self.assertTrue(defender.db.hp == 7)
# Resolve attack
defender.db.hp = 40
tb_magic.resolve_attack(attacker, defender, attack_value=20, defense_value=10)
self.assertTrue(defender.db.hp < 40)
# Combat cleanup
attacker.db.Combat_attribute = True
tb_magic.combat_cleanup(attacker)
self.assertFalse(attacker.db.combat_attribute)
# Is in combat
self.assertFalse(tb_magic.is_in_combat(attacker))
# Set up turn handler script for further tests
attacker.location.scripts.add(tb_magic.TBMagicTurnHandler)
turnhandler = attacker.db.combat_TurnHandler
self.assertTrue(attacker.db.combat_TurnHandler)
# Set the turn handler's interval very high to keep it from repeating during tests.
turnhandler.interval = 10000
# Force turn order
turnhandler.db.fighters = [attacker, defender]
turnhandler.db.turn = 0
# Test is turn
self.assertTrue(tb_magic.is_turn(attacker))
# Spend actions
attacker.db.Combat_ActionsLeft = 1
tb_magic.spend_action(attacker, 1, action_name="Test")
self.assertTrue(attacker.db.Combat_ActionsLeft == 0)
self.assertTrue(attacker.db.Combat_LastAction == "Test")
# Initialize for combat
attacker.db.Combat_ActionsLeft = 983
turnhandler.initialize_for_combat(attacker)
self.assertTrue(attacker.db.Combat_ActionsLeft == 0)
self.assertTrue(attacker.db.Combat_LastAction == "null")
# Start turn
defender.db.Combat_ActionsLeft = 0
turnhandler.start_turn(defender)
self.assertTrue(defender.db.Combat_ActionsLeft == 1)
# Next turn
turnhandler.db.fighters = [attacker, defender]
turnhandler.db.turn = 0
turnhandler.next_turn()
self.assertTrue(turnhandler.db.turn == 1)
# Turn end check
turnhandler.db.fighters = [attacker, defender]
turnhandler.db.turn = 0
attacker.db.Combat_ActionsLeft = 0
turnhandler.turn_end_check(attacker)
self.assertTrue(turnhandler.db.turn == 1)
# Join fight
joiner = create_object(tb_magic.TBMagicCharacter, key="Joiner")
turnhandler.db.fighters = [attacker, defender]
turnhandler.db.turn = 0
turnhandler.join_fight(joiner)
self.assertTrue(turnhandler.db.turn == 1)
self.assertTrue(turnhandler.db.fighters == [joiner, attacker, defender])
# Remove the script at the end
turnhandler.stop()


# Test tree select

Expand Down
13 changes: 13 additions & 0 deletions evennia/contrib/turnbattle/README.md
Expand Up @@ -21,6 +21,19 @@ implemented and customized:
the battle system, including commands for wielding weapons and
donning armor, and modifiers to accuracy and damage based on
currently used equipment.

tb_items.py - Adds usable items and conditions/status effects, and gives
a lot of examples for each. Items can perform nearly any sort of
function, including healing, adding or curing conditions, or
being used to attack. Conditions affect a fighter's attributes
and options in combat and persist outside of fights, counting
down per turn in combat and in real time outside combat.

tb_magic.py - Adds a spellcasting system, allowing characters to cast
spells with a variety of effects by spending MP. Spells are
linked to functions, and as such can perform any sort of action
the developer can imagine - spells for attacking, healing and
conjuring objects are included as examples.

tb_range.py - Adds a system for abstract positioning and movement, which
tracks the distance between different characters and objects in
Expand Down