Skip to content
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
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ local_settings.py
db.sqlite3
db.sqlite3-journal
fixtures/
game/

# Flask stuff:
instance/
Expand Down
1 change: 1 addition & 0 deletions NoobRPG/NoobRPG/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ def load_bool_from_env(name: str, default: bool):
'locations.apps.LocationsConfig',
'rarity.apps.RarityConfig',
'sellers_offers.apps.SellersOffersConfig',
'game.apps.GameConfig',
]

MIDDLEWARE = [
Expand Down
1 change: 1 addition & 0 deletions NoobRPG/NoobRPG/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
path('api/v1/auth/', include('djoser.urls')),
re_path(r'^auth/', include('djoser.urls.authtoken')),
path('api/v1/entities/', include('entities.urls')),
path('api/v1/game/', include('game.urls')),
path('api/v1/items/', include('items.urls')),
path('api/v1/locations/', include('locations.urls')),
path('api/v1/rarity/', include('rarity.urls')),
Expand Down
32 changes: 27 additions & 5 deletions NoobRPG/entities/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,27 @@ def taking_damage(self, damage: int) -> str | None:
self.hp = 0
self.save()
if self.hp == 0:
return self.drop_items()
self.is_in_battle = False
self.save()
return f'{self.drop_items()}'
return None

def self_damage(self) -> int:
return self.base_damage

def attack(self, player: models.QuerySet) -> str:
damage = self.self_damage()
player_return = player.taking_damage(damage, 0)
if player_return is None:
self.is_in_battle = False
self.save()
return (
'Enemy attacked. You have lost. '
f'You have been sent to the {player.start_location} location.'
)

return player_return


class PlayerManager(models.Manager):
def all_fields(self) -> models.QuerySet:
Expand Down Expand Up @@ -163,17 +178,19 @@ def equip_weapon(self, item: models.QuerySet) -> str:
return f'Equipped weapon: {self.weapon}.'

def taking_damage(self, damage_hp: int, damage_mana: int) -> str:
if self.hp >= damage_hp or self.mana >= damage_mana:
message = 'Enemy attacked. '
if self.hp >= damage_hp and self.mana >= damage_mana:
self.hp -= damage_hp
self.mana -= damage_mana
message = (
message += (
f'Your health is now {self.hp} and your mana is {self.mana}.'
)
else:
self.hp = self.max_hp
self.mana = self.max_mana
self.is_in_battle = False
self.to_start_location()
message = (
message += (
'You have lost. '
f'You have been sent to the {self.start_location} location.'
)
Expand Down Expand Up @@ -206,5 +223,10 @@ def self_damage(self) -> int:

def attack(self, enemy: models.QuerySet) -> str:
damage = self.self_damage()
enemy.taking_damage(damage)
enemy_return = enemy.taking_damage(damage)
if enemy_return is not None:
self.is_in_battle = False
self.save()
return f'You won. Drop from target: {enemy_return}'

return f'You dealt {damage} damage to an enemy.'
Empty file added NoobRPG/game/__init__.py
Empty file.
8 changes: 8 additions & 0 deletions NoobRPG/game/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
__all__ = ()

from django.apps import AppConfig


class GameConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'game'
Empty file.
21 changes: 21 additions & 0 deletions NoobRPG/game/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from django.urls import path
from game import views


urlpatterns = [
path(
'change-location/',
views.ChangeLocationView.as_view(),
name='change-location',
),
path(
'start-battle/',
views.StartBattleView.as_view(),
name='start-battle',
),
path(
'attack/',
views.AttackView.as_view(),
name='attack',
),
]
263 changes: 263 additions & 0 deletions NoobRPG/game/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
__all__ = ()

from entities.models import NonPlayerCharacter as NPCModel
from entities.models import Player
from locations.models import Location
from rest_framework import permissions
from rest_framework.response import Response
from rest_framework.views import APIView


class ChangeLocationView(APIView):
permission_classes = [permissions.IsAuthenticated]

def post(self, request):
data = request.data

location_id = data.get('location_id')
player_id = data.get('player_id')

if not location_id:
return Response({'error': 'Location ID is required'}, status=400)

try:
location_id = int(location_id)
if location_id <= 0:
return Response(
{'error': 'Location ID must be a positive integer'},
status=400,
)
except (ValueError, TypeError):
return Response(
{'error': 'Location ID must be a valid integer'},
status=400,
)

if player_id:
try:
player_id = int(player_id)
if player_id <= 0:
return Response(
{'error': 'Player ID must be a positive integer'},
status=400,
)
except (ValueError, TypeError):
return Response(
{'error': 'Player ID must be a valid integer'},
status=400,
)

try:
if player_id:
player = Player.objects.get(id=player_id, user=request.user)
else:
player = Player.objects.get(user=request.user)

location = Location.objects.get(id=location_id)

player.current_location = location
player.save()

return Response(
{
'message': (
f'Location updated successfully to {location.name}'
),
'current_location': location.name,
'location_id': location.id,
'player_id': player.id,
},
)

except Player.DoesNotExist:
return Response(
{
'error': (
'Player not found. Either the player does not exist '
'or you do not have permission to update this player.',
),
},
status=404,
)
except Location.DoesNotExist:
return Response({'error': 'Location not found'}, status=404)
except Exception as e:
print(f'Error updating player location: {e}')
return Response(
{'error': 'An error occurred while updating the location'},
status=500,
)


class StartBattleView(APIView):
permission_classes = [permissions.IsAuthenticated]

def post(self, request):
data = request.data

npc_id = data.get('npc_id')
player_id = data.get('player_id')

if not npc_id:
return Response({'error': 'NPC ID is required'}, status=400)

try:
npc_id = int(npc_id)
if npc_id <= 0:
return Response(
{'error': 'NPC ID must be a positive integer'},
status=400,
)
except (ValueError, TypeError):
return Response(
{'error': 'NPC ID must be a valid integer'},
status=400,
)

if player_id:
try:
player_id = int(player_id)
if player_id <= 0:
return Response(
{'error': 'Player ID must be a positive integer'},
status=400,
)
except (ValueError, TypeError):
return Response(
{'error': 'Player ID must be a valid integer'},
status=400,
)

try:
if player_id:
player = Player.objects.get(id=player_id, user=request.user)
else:
player = Player.objects.get(user=request.user)

npc = NPCModel.objects.get(id=npc_id)

player.is_in_battle = True
npc.is_in_battle = True
player.save()
npc.save()

return Response(
{
'message': f'You are now in battle with {npc.name}',
'player is in battle': player.is_in_battle,
'NPC is in battle': npc.is_in_battle,
'npc_id': npc.id,
'player_id': player.id,
},
)

except Player.DoesNotExist:
return Response(
{
'error': (
'Player not found. Either the player does not exist '
'or you do not have permission to update this player.',
),
},
status=404,
)
except NPCModel.DoesNotExist:
return Response({'error': 'NPC not found'}, status=404)
except Exception as e:
print(f'Error updating player is_in_battle: {e}')
return Response(
{'error': 'An error occurred while starting battle'},
status=500,
)


class AttackView(APIView):
permission_classes = [permissions.IsAuthenticated]

def post(self, request):
data = request.data

npc_id = data.get('npc_id')
player_id = data.get('player_id')

if not npc_id:
return Response({'error': 'NPC ID is required'}, status=400)

try:
npc_id = int(npc_id)
if npc_id <= 0:
return Response(
{'error': 'NPC ID must be a positive integer'},
status=400,
)
except (ValueError, TypeError):
return Response(
{'error': 'NPC ID must be a valid integer'},
status=400,
)

if player_id:
try:
player_id = int(player_id)
if player_id <= 0:
return Response(
{'error': 'Player ID must be a positive integer'},
status=400,
)
except (ValueError, TypeError):
return Response(
{'error': 'Player ID must be a valid integer'},
status=400,
)

try:
if player_id:
player = Player.objects.get(id=player_id, user=request.user)
else:
player = Player.objects.get(user=request.user)

if player.is_in_battle is False:
return Response(
{'error': 'First we need to start the battle'},
status=400,
)

npc = NPCModel.objects.get(id=npc_id)

if npc.is_in_battle is False:
return Response(
{'error': 'NPC is_in_battle must be True'},
status=400,
)

player_attack_res = player.attack(npc)
npc_attack_res = npc.attack(player)

return Response(
{
'message': f'{player_attack_res} {npc_attack_res}',
'player is in battle': player.is_in_battle,
'NPC is in battle': npc.is_in_battle,
'npc_id': npc.id,
'player_id': player.id,
},
)

except Player.DoesNotExist:
return Response(
{
'error': (
'Player not found. Either the player does not exist '
'or you do not have permission to update this player.',
),
},
status=404,
)
except NPCModel.DoesNotExist:
return Response({'error': 'NPC not found'}, status=404)
except Exception as e:
print(f'Error updating player is_in_battle: {e}')
return Response(
{'error': 'An error occurred while starting battle'},
status=500,
)