Combat foundation complete
This commit is contained in:
@@ -119,6 +119,14 @@ Displays character stats, resource bars, and action buttons
|
||||
hx-swap="innerHTML">
|
||||
🗺️ Travel to...
|
||||
</button>
|
||||
|
||||
{# Search for Monsters - Opens modal with encounter options #}
|
||||
<button class="action-btn action-btn--special action-btn--combat"
|
||||
hx-get="{{ url_for('game.monster_modal', session_id=session_id) }}"
|
||||
hx-target="#modal-container"
|
||||
hx-swap="innerHTML">
|
||||
⚔️ Search for Monsters
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{# Actions Section #}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
{#
|
||||
Combat Abandoned Success Message
|
||||
Shows after successfully abandoning a combat session
|
||||
#}
|
||||
<div class="combat-abandoned-success">
|
||||
<div class="success-icon">✔</div>
|
||||
<p class="success-message">{{ message }}</p>
|
||||
<p class="success-hint">
|
||||
<small>Click "Search for Monsters" to find a new encounter.</small>
|
||||
</p>
|
||||
<button class="btn btn-secondary" onclick="closeModal()">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.combat-abandoned-success {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.success-icon {
|
||||
font-size: 3rem;
|
||||
color: var(--color-success, #28a745);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.success-message {
|
||||
font-size: 1.1rem;
|
||||
color: var(--color-text, #e5e7eb);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.success-hint {
|
||||
color: var(--color-text-secondary, #aaa);
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
</style>
|
||||
@@ -50,8 +50,8 @@
|
||||
{# Flee Button - Direct action #}
|
||||
<button class="combat-action-btn combat-action-btn--flee"
|
||||
hx-post="{{ url_for('combat.combat_flee', session_id=session_id) }}"
|
||||
hx-target="body"
|
||||
hx-swap="innerHTML"
|
||||
hx-target="#combat-log"
|
||||
hx-swap="beforeend"
|
||||
hx-disabled-elt="this"
|
||||
hx-confirm="Are you sure you want to flee from combat?"
|
||||
title="Attempt to escape from battle">
|
||||
|
||||
220
public_web/templates/game/partials/combat_conflict_modal.html
Normal file
220
public_web/templates/game/partials/combat_conflict_modal.html
Normal file
@@ -0,0 +1,220 @@
|
||||
{#
|
||||
Combat Conflict Modal
|
||||
Shows when player tries to start combat but already has an active combat session
|
||||
#}
|
||||
<div class="modal-overlay" onclick="if(event.target === this) closeModal()">
|
||||
<div class="modal-content combat-conflict-modal">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title">Active Combat Detected</h3>
|
||||
<button class="modal-close" onclick="closeModal()">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="conflict-warning">
|
||||
<p>You have an active combat session in progress:</p>
|
||||
</div>
|
||||
|
||||
<div class="combat-summary">
|
||||
<div class="combat-summary-header">
|
||||
<span class="combat-round">Round {{ combat_info.round_number }}</span>
|
||||
<span class="combat-status combat-status--{{ combat_info.status }}">{{ combat_info.status|capitalize }}</span>
|
||||
</div>
|
||||
|
||||
<div class="combatants-section">
|
||||
<div class="combatants-group combatants-group--players">
|
||||
<h4>Your Party</h4>
|
||||
{% for player in combat_info.players %}
|
||||
<div class="combatant-summary {% if not player.is_alive %}combatant-summary--dead{% endif %}">
|
||||
<span class="combatant-name">{{ player.name }}</span>
|
||||
<span class="combatant-hp">
|
||||
{{ player.current_hp }}/{{ player.max_hp }} HP
|
||||
</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="combatants-vs">VS</div>
|
||||
|
||||
<div class="combatants-group combatants-group--enemies">
|
||||
<h4>Enemies</h4>
|
||||
{% for enemy in combat_info.enemies %}
|
||||
<div class="combatant-summary {% if not enemy.is_alive %}combatant-summary--dead{% endif %}">
|
||||
<span class="combatant-name">{{ enemy.name }}</span>
|
||||
<span class="combatant-hp">
|
||||
{% if enemy.is_alive %}
|
||||
{{ enemy.current_hp }}/{{ enemy.max_hp }} HP
|
||||
{% else %}
|
||||
Defeated
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="conflict-options">
|
||||
<p>What would you like to do?</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer conflict-actions">
|
||||
<a href="{{ url_for('combat.combat_view', session_id=session_id) }}"
|
||||
class="btn btn-primary btn-resume">
|
||||
Resume Combat
|
||||
</a>
|
||||
<button class="btn btn-danger btn-abandon"
|
||||
hx-post="{{ url_for('game.abandon_and_start_combat', session_id=session_id) }}"
|
||||
hx-vals='{"enemy_ids": {{ pending_enemy_ids|tojson }}}'
|
||||
hx-swap="none"
|
||||
hx-confirm="Are you sure you want to abandon your current combat? You will not receive any rewards.">
|
||||
Abandon & Start New
|
||||
</button>
|
||||
<button class="btn btn-secondary" onclick="closeModal()">
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.combat-conflict-modal {
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.conflict-warning {
|
||||
background: var(--color-warning-bg, rgba(255, 193, 7, 0.1));
|
||||
border: 1px solid var(--color-warning, #ffc107);
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.conflict-warning p {
|
||||
margin: 0;
|
||||
color: var(--color-warning, #ffc107);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.combat-summary {
|
||||
background: var(--color-surface, #2a2a2a);
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.combat-summary-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px solid var(--color-border, #444);
|
||||
}
|
||||
|
||||
.combat-round {
|
||||
font-weight: 600;
|
||||
color: var(--color-text-secondary, #aaa);
|
||||
}
|
||||
|
||||
.combat-status {
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.combat-status--active {
|
||||
background: var(--color-success-bg, rgba(40, 167, 69, 0.2));
|
||||
color: var(--color-success, #28a745);
|
||||
}
|
||||
|
||||
.combatants-section {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.combatants-group {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.combatants-group h4 {
|
||||
font-size: 0.85rem;
|
||||
color: var(--color-text-secondary, #aaa);
|
||||
margin: 0 0 0.5rem 0;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.combatants-vs {
|
||||
padding: 0.5rem;
|
||||
color: var(--color-text-muted, #666);
|
||||
font-weight: 600;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.combatant-summary {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.35rem 0.5rem;
|
||||
background: var(--color-bg, #1a1a1a);
|
||||
border-radius: 4px;
|
||||
margin-bottom: 0.25rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.combatant-summary--dead {
|
||||
opacity: 0.5;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.combatant-name {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.combatant-hp {
|
||||
color: var(--color-text-secondary, #aaa);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.combatants-group--players .combatant-hp {
|
||||
color: var(--color-health, #4ade80);
|
||||
}
|
||||
|
||||
.combatants-group--enemies .combatant-hp {
|
||||
color: var(--color-danger, #ef4444);
|
||||
}
|
||||
|
||||
.conflict-options {
|
||||
text-align: center;
|
||||
color: var(--color-text-secondary, #aaa);
|
||||
}
|
||||
|
||||
.conflict-options p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.conflict-actions {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.conflict-actions .btn {
|
||||
flex: 1;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.btn-resume {
|
||||
background: var(--color-primary, #8b5cf6);
|
||||
}
|
||||
|
||||
.btn-abandon {
|
||||
background: var(--color-danger, #ef4444);
|
||||
}
|
||||
|
||||
.btn-abandon:hover {
|
||||
background: var(--color-danger-hover, #dc2626);
|
||||
}
|
||||
</style>
|
||||
@@ -1,12 +1,5 @@
|
||||
{% extends "base.html" %}
|
||||
{# Combat Defeat Partial - Swapped into combat log when player loses #}
|
||||
|
||||
{% block title %}Defeated - Code of Conquest{% endblock %}
|
||||
|
||||
{% block extra_head %}
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/combat.css') }}">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="combat-result combat-result--defeat">
|
||||
<div class="combat-result__icon">💀</div>
|
||||
<h1 class="combat-result__title">Defeated</h1>
|
||||
@@ -52,4 +45,3 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
{% extends "base.html" %}
|
||||
{# Combat Victory Partial - Swapped into combat log when player wins #}
|
||||
|
||||
{% block title %}Victory! - Code of Conquest{% endblock %}
|
||||
|
||||
{% block extra_head %}
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/combat.css') }}">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="combat-result combat-result--victory">
|
||||
<div class="combat-result__icon">🏆</div>
|
||||
<h1 class="combat-result__title">Victory!</h1>
|
||||
@@ -47,12 +40,12 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{# Loot Items #}
|
||||
{% if rewards.items %}
|
||||
{# Loot Items - use bracket notation to avoid conflict with dict.items() method #}
|
||||
{% if rewards.get('items') %}
|
||||
<div class="loot-section">
|
||||
<h3 class="loot-title">Items Obtained</h3>
|
||||
<div class="loot-list">
|
||||
{% for item in rewards.items %}
|
||||
{% for item in rewards.get('items', []) %}
|
||||
<div class="loot-item loot-item--{{ item.rarity|default('common') }}">
|
||||
<span>
|
||||
{% if item.type == 'weapon' %}⚔
|
||||
@@ -63,7 +56,7 @@
|
||||
{% endif %}
|
||||
</span>
|
||||
<span>{{ item.name }}</span>
|
||||
{% if item.quantity > 1 %}
|
||||
{% if item.get('quantity', 1) > 1 %}
|
||||
<span style="color: var(--text-muted);">x{{ item.quantity }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -81,4 +74,3 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
53
public_web/templates/game/partials/monster_modal.html
Normal file
53
public_web/templates/game/partials/monster_modal.html
Normal file
@@ -0,0 +1,53 @@
|
||||
{#
|
||||
Monster Selection Modal
|
||||
Shows random encounter options for the current location
|
||||
#}
|
||||
<div class="modal-overlay" onclick="if(event.target === this) closeModal()">
|
||||
<div class="modal-content monster-modal">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title">⚔️ Search for Monsters</h3>
|
||||
<button class="modal-close" onclick="closeModal()">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p class="monster-modal-location">
|
||||
Searching near <strong>{{ location_name }}</strong>...
|
||||
</p>
|
||||
{% if encounters %}
|
||||
<div class="encounter-options">
|
||||
{% for enc in encounters %}
|
||||
<button class="encounter-option encounter-option--{{ enc.challenge|lower }}"
|
||||
hx-post="{{ url_for('game.start_combat', session_id=session_id) }}"
|
||||
hx-vals='{"enemy_ids": {{ enc.enemies|tojson }}}'
|
||||
hx-target="closest .modal-overlay"
|
||||
hx-swap="outerHTML">
|
||||
<div class="encounter-info">
|
||||
<div class="encounter-name">{{ enc.display_name }}</div>
|
||||
<div class="encounter-enemies">
|
||||
{% for name in enc.enemy_names %}
|
||||
<span class="enemy-badge">{{ name }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="encounter-challenge challenge--{{ enc.challenge|lower }}">
|
||||
{{ enc.challenge }}
|
||||
</div>
|
||||
</button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<p class="monster-modal-hint">
|
||||
<small>Select an encounter to begin combat. Challenge level indicates difficulty.</small>
|
||||
</p>
|
||||
{% else %}
|
||||
<div class="encounter-empty">
|
||||
<p>No monsters found in this area.</p>
|
||||
<p><small>Try exploring somewhere more dangerous!</small></p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-secondary" onclick="closeModal()">
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user