NPC shop implimented

This commit is contained in:
2025-11-29 01:16:46 -06:00
parent 32af625d14
commit 8bd494a52f
17 changed files with 4265 additions and 17 deletions

View File

@@ -103,6 +103,15 @@ Displays character stats, resource bars, and action buttons
⚔️ Equipment & Gear
</button>
{# Shop - Opens shop modal #}
<button class="action-btn action-btn--special"
hx-get="{{ url_for('game.shop_modal', session_id=session_id) }}"
hx-target="#modal-container"
hx-swap="innerHTML">
<span class="action-icon">&#128176;</span>
Shop
</button>
{# Skill Trees - Direct link to skills page #}
<a class="action-btn action-btn--special"
href="{{ url_for('character_views.view_skills', character_id=character.character_id) }}">

View File

@@ -0,0 +1,180 @@
{#
Shop Modal
Browse and purchase items from the general store
#}
<div class="modal-overlay" onclick="if(event.target===this) closeModal()"
role="dialog" aria-modal="true" aria-labelledby="shop-title">
<div class="modal-content shop-modal">
{# Header #}
<div class="modal-header">
<div class="shop-header-info">
<h2 class="modal-title" id="shop-title">
{{ shop.shop_name|default('General Store') }}
</h2>
<span class="shop-keeper">{{ shop.shopkeeper_name|default('Merchant') }}</span>
</div>
<div class="shop-gold-display">
<span class="gold-icon">&#128176;</span>
<span class="gold-amount">{{ gold }}</span>
</div>
<button class="modal-close" onclick="closeModal()" aria-label="Close shop">&times;</button>
</div>
{# Success/Error Message #}
{% if message %}
<div class="shop-message shop-message--success">
{{ message }}
</div>
{% endif %}
{% if error %}
<div class="shop-message shop-message--error">
{{ error }}
</div>
{% endif %}
{# Tab Filter Bar #}
<div class="shop-tabs" role="tablist">
<button class="tab {% if filter == 'all' %}active{% endif %}"
role="tab"
aria-selected="{{ 'true' if filter == 'all' else 'false' }}"
hx-get="{{ url_for('game.shop_modal', session_id=session_id, filter='all') }}"
hx-target=".shop-modal"
hx-swap="outerHTML">
All
</button>
<button class="tab {% if filter == 'weapon' %}active{% endif %}"
role="tab"
aria-selected="{{ 'true' if filter == 'weapon' else 'false' }}"
hx-get="{{ url_for('game.shop_modal', session_id=session_id, filter='weapon') }}"
hx-target=".shop-modal"
hx-swap="outerHTML">
Weapons
</button>
<button class="tab {% if filter == 'armor' %}active{% endif %}"
role="tab"
aria-selected="{{ 'true' if filter == 'armor' else 'false' }}"
hx-get="{{ url_for('game.shop_modal', session_id=session_id, filter='armor') }}"
hx-target=".shop-modal"
hx-swap="outerHTML">
Armor
</button>
<button class="tab {% if filter == 'consumable' %}active{% endif %}"
role="tab"
aria-selected="{{ 'true' if filter == 'consumable' else 'false' }}"
hx-get="{{ url_for('game.shop_modal', session_id=session_id, filter='consumable') }}"
hx-target=".shop-modal"
hx-swap="outerHTML">
Consumables
</button>
<button class="tab {% if filter == 'accessory' %}active{% endif %}"
role="tab"
aria-selected="{{ 'true' if filter == 'accessory' else 'false' }}"
hx-get="{{ url_for('game.shop_modal', session_id=session_id, filter='accessory') }}"
hx-target=".shop-modal"
hx-swap="outerHTML">
Accessories
</button>
</div>
{# Body - Item Grid #}
<div class="modal-body shop-body">
<div class="shop-grid">
{% for entry in inventory %}
{% set item = entry.item %}
{% set price = entry.shop_price %}
{% set can_afford = entry.can_afford|default(gold >= price) %}
<div class="shop-item rarity-{{ item.rarity|default('common') }}">
{# Item Header #}
<div class="shop-item-header">
<span class="shop-item-name">{{ item.name }}</span>
<span class="shop-item-type">{{ item.item_type|default('item')|replace('_', ' ')|title }}</span>
</div>
{# Rarity Tag #}
<span class="shop-item-rarity rarity-tag--{{ item.rarity|default('common') }}">
{{ item.rarity|default('common')|title }}
</span>
{# Item Description #}
<p class="shop-item-desc">{{ item.description|default('A useful item.')|truncate(80) }}</p>
{# Item Stats #}
<div class="shop-item-stats">
{% if item.item_type == 'weapon' %}
{% if item.damage %}
<span class="stat">Damage: {{ item.damage }}</span>
{% endif %}
{% if item.damage_type %}
<span class="stat">{{ item.damage_type|title }}</span>
{% endif %}
{% elif item.item_type == 'armor' or item.item_type == 'shield' %}
{% if item.defense %}
<span class="stat">Defense: +{{ item.defense }}</span>
{% endif %}
{% if item.slot %}
<span class="stat">{{ item.slot|replace('_', ' ')|title }}</span>
{% endif %}
{% elif item.item_type == 'consumable' %}
{% if item.hp_restore %}
<span class="stat">HP +{{ item.hp_restore }}</span>
{% endif %}
{% if item.mp_restore %}
<span class="stat">MP +{{ item.mp_restore }}</span>
{% endif %}
{% if item.effect %}
<span class="stat">{{ item.effect }}</span>
{% endif %}
{% elif item.item_type == 'accessory' %}
{% if item.stat_bonuses %}
{% for stat, bonus in item.stat_bonuses.items() %}
<span class="stat">{{ stat|title }}: +{{ bonus }}</span>
{% endfor %}
{% endif %}
{% endif %}
</div>
{# Price and Buy Button #}
<div class="shop-item-footer">
<span class="shop-item-price {% if not can_afford %}unaffordable{% endif %}">
<span class="gold-icon">&#128176;</span> {{ price }}
</span>
<button class="btn-purchase {% if can_afford %}btn-purchase--available{% else %}btn-purchase--disabled{% endif %}"
{% if can_afford %}
hx-post="{{ url_for('game.shop_purchase', session_id=session_id) }}"
hx-vals='{"item_id": "{{ item.item_id }}", "quantity": 1}'
hx-target=".shop-modal"
hx-swap="outerHTML"
{% else %}
disabled
{% endif %}
aria-label="{% if can_afford %}Purchase {{ item.name }} for {{ price }} gold{% else %}Not enough gold{% endif %}">
{% if can_afford %}
Buy
{% else %}
Can't Afford
{% endif %}
</button>
</div>
</div>
{% else %}
<p class="shop-empty">
{% if filter == 'all' %}
No items available in this shop.
{% else %}
No {{ filter|replace('_', ' ') }}s available.
{% endif %}
</p>
{% endfor %}
</div>
</div>
{# Footer #}
<div class="modal-footer">
<div class="shop-footer-gold">
<span class="gold-icon">&#128176;</span>
<span class="gold-amount">{{ gold }} gold</span>
</div>
<button class="btn btn--secondary" onclick="closeModal()">Close</button>
</div>
</div>
</div>

View File

@@ -5,6 +5,7 @@
{% block extra_head %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/play.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/inventory.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/shop.css') }}">
{% endblock %}
{% block content %}