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

combat points and replenish #88

Merged
merged 3 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions tale/accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ def _create_database(self) -> None:
strength integer NOT NULL,
dexterity integer NOT NULL,
weapon_skills varchar NOT NULL,
combat_points integer NOT NULL,
max_combat_points integer NOT NULL,
FOREIGN KEY(account) REFERENCES Account(id)
);
""")
Expand Down
22 changes: 22 additions & 0 deletions tale/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -975,6 +975,8 @@ def __init__(self) -> None:
self.dexterity = 3
self.unarmed_attack = Weapon(UnarmedAttack.FISTS.name, weapon_type=WeaponType.UNARMED)
self.weapon_skills = {} # type: Dict[WeaponType, int] # weapon type -> skill level
self.combat_points = 0 # combat points
self.max_combat_points = 5 # max combat points

def __repr__(self):
return "<Stats: %s>" % self.__dict__
Expand Down Expand Up @@ -1007,6 +1009,22 @@ def get_weapon_skill(self, weapon_type: WeaponType) -> int:
def set_weapon_skill(self, weapon_type: WeaponType, value: int) -> None:
self.weapon_skills[weapon_type] = value

def replenish_hp(self, amount: int = None) -> None:
if amount:
self.hp += amount
else:
self.hp = self.max_hp
if self.hp > self.max_hp:
self.hp = self.max_hp

def replenish_combat_points(self, amount: int = None) -> None:
if amount:
self.combat_points += amount
else:
self.combat_points = self.max_combat_points
if self.combat_points > self.max_combat_points:
self.combat_points = self.max_combat_points

class Living(MudObject):
"""
A living entity in the mud world (also known as an NPC).
Expand Down Expand Up @@ -1422,6 +1440,10 @@ def locate_item(self, name: str, include_inventory: bool=True, include_location:

def start_attack(self, defender: 'Living', target_body_part: wearable.WearLocation= None) -> combat.Combat:
"""Starts attacking the given living for one round."""
if self.stats.combat_points < 1:
self.tell("You are too tired to attack.")
return
self.stats.combat_points -= 1
attacker_name = lang.capital(self.title)
victim_name = lang.capital(defender.title)
attackers = [self]
Expand Down
2 changes: 1 addition & 1 deletion tale/combat.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def _calculate_block_success(self, actor1: 'base.Living', actor2: 'base.Living')
if actor2.wielding.type in weapon_type.ranged:
# can't block with a ranged weapon
return 100
return random.randrange(0, 100) - actor2.stats.get_weapon_skill(actor2.wielding.type)
return random.randrange(0, 100) - actor2.stats.get_weapon_skill(actor2.wielding.type) * (0.8 if actor2.stats.combat_points < 1 else 1)

def _calculate_weapon_bonus(self, actor: 'base.Living'):
weapon = actor.wielding
Expand Down
9 changes: 8 additions & 1 deletion tale/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ def start(self, game_file_or_path: str) -> None:
x._bind_target(self.zones)
self.unbound_exits = []
sys.excepthook = util.excepthook # install custom verbose crash reporter
self.register_periodicals(self)
self.start_main_loop() # doesn't exit! (unless game is killed)
self._stop_driver()

Expand Down Expand Up @@ -958,4 +959,10 @@ def build_location(self, targetLocation: base.Location, zone: Zone, player: play
return True

def do_on_player_death(self, player: player.Player) -> None:
pass
pass

@util.call_periodically(10)
def replenish(self):
for player in self.all_players.values():
player.player.stats.replenish_hp(1)
player.player.stats.replenish_combat_points(1)
4 changes: 2 additions & 2 deletions tale/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from .tio.iobase import strip_text_styles, IoAdapterBase
from .vfs import VirtualFileSystem, Resource
from tale.player_utils import TextBuffer
from tale.util import call_periodically


class Player(base.Living, pubsub.Listener):
Expand Down Expand Up @@ -408,5 +409,4 @@ def destroy(self) -> None:
self.io = None # type: ignore
if self.player:
self.player.destroy(ctx)
self.player = None # type: ignore

self.player = None # type: ignore
11 changes: 9 additions & 2 deletions tests/test_combat.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from tale.llm.LivingNpc import LivingNpc
from tale.combat import Combat
from tale.llm.contexts.CombatContext import CombatContext
from tale.player import Player
from tale.races import BodyType
from tale.weapon_type import WeaponType
from tale.wearable import WearLocation
Expand All @@ -16,8 +17,6 @@
class TestCombat():

def test_resolve_attack(self):


attacker = LivingNpc(name='attacker', gender='m', age=42, personality='A fierce fighter')
defender = LivingNpc(name='defender', gender='f', age=37, personality='A fierce fighter')

Expand Down Expand Up @@ -238,3 +237,11 @@ def test_resolve_attack_group(self):
self._assert_combat(attacker, defender, text)
assert('attacker hits' in text or 'attacker performs a critical hit' in text)
assert('attacker2 hits' in text or 'attacker2 performs a critical hit' in text)

def test_start_attack_no_combat_points(self):
attacker = Player(name='att', gender='m')
attacker.stats.combat_points = 0
defender = LivingNpc(name='lucky rat', gender='m', age=2, personality='A squeeky fighter')

assert attacker.start_attack(defender) == None
assert ['You are too tired to attack.\n'] == attacker.test_get_output_paragraphs()
32 changes: 32 additions & 0 deletions tests/test_stats.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@


from tale.base import Stats


class TestStats:

def test_replenish_hp(self):
stats = Stats()
stats.max_hp = 100
stats.hp = 0

stats.replenish_hp(10)

assert stats.hp == 10

stats.replenish_hp()

assert stats.hp == 100

def test_replenish_combat_points(self):
stats = Stats()
stats.max_combat_points = 100
stats.combat_points = 0

stats.replenish_combat_points(10)

assert stats.combat_points == 10

stats.replenish_combat_points()

assert stats.combat_points == 100
Loading