diff --git a/cards/models.py b/cards/models.py index c754da5..d0ac024 100644 --- a/cards/models.py +++ b/cards/models.py @@ -102,9 +102,17 @@ class Dinosaur(models.Model): """Convert a 0-100 percent into a 0-5 star score in 0.5 increments.""" if percent is None: return 0.0 - val = float(percent) * 0.05 # scale 0-100 -> 0-5 + pct = float(percent) + # scale 0-100 -> 0-5 + val = pct * 0.05 # round to nearest 0.5 - return round(val * 2) / 2.0 + rounded = round(val * 2) / 2.0 + # Avoid promoting near-100 values to 5.0 via rounding. Only an + # exact 100% should map to a full 5.0. Anything less will be + # capped at 4.5 to keep 5-star visually reserved for true maxima. + if pct >= 100.0: + return 5.0 + return min(rounded, 4.5) def _stars_string(self, percent): """Return a simple star string like '★★★★½' and trailing empty stars '☆'.""" diff --git a/cards/templates/cards/_card_inner_design4.html b/cards/templates/cards/_card_inner_design4.html index aca1c89..53e947f 100644 --- a/cards/templates/cards/_card_inner_design4.html +++ b/cards/templates/cards/_card_inner_design4.html @@ -49,48 +49,48 @@
{% with s=card.speed_percent w=card.weight_percent it=card.intelligence_percent h=card.height_percent %} -
+
Speed
{{ card.speed_display }}
-
+
Weight
{{ card.weight_display }}
-
+
Intelligence
{{ card.intelligence_display }}
-
+
Height
{{ card.height_display }}
diff --git a/cards/templates/cards/_card_item.html b/cards/templates/cards/_card_item.html index bb1154b..4f26ddb 100644 --- a/cards/templates/cards/_card_item.html +++ b/cards/templates/cards/_card_item.html @@ -34,7 +34,7 @@
{{ card.speed_display }}
-
{% for t in card.speed_star_types %}{% if t == 'full' %}★{% else %}☆{% endif %}{% endfor %}
+
{% for t in card.speed_star_types_adj|default:card.speed_star_types %}{% if t == 'full' %}★{% else %}☆{% endif %}{% endfor %}
(orig: {{ card.speed_orig }})
@@ -44,7 +44,7 @@
{{ card.weight_display }}
-
{% for t in card.weight_star_types %}{% if t == 'full' %}★{% else %}☆{% endif %}{% endfor %}
+
{% for t in card.weight_star_types_adj|default:card.weight_star_types %}{% if t == 'full' %}★{% else %}☆{% endif %}{% endfor %}
(orig: {{ card.weight_orig }})
@@ -53,7 +53,7 @@
Intelligence (score)
-
{% for t in card.intelligence_star_types %}{% if t == 'full' %}★{% else %}☆{% endif %}{% endfor %}
+
{% for t in card.intelligence_star_types_adj|default:card.intelligence_star_types %}{% if t == 'full' %}★{% else %}☆{% endif %}{% endfor %}
(orig: {{ card.intelligence_orig }})
diff --git a/cards/templates/cards/_card_preview.html b/cards/templates/cards/_card_preview.html index 06e55b1..7c6e442 100644 --- a/cards/templates/cards/_card_preview.html +++ b/cards/templates/cards/_card_preview.html @@ -41,20 +41,20 @@
Speed (km/h)
{{ card.speed_display }}
-
{% for t in card.speed_star_types %}{% if t == 'full' %}★{% else %}☆{% endif %}{% endfor %}
(orig: {{ card.speed_orig }})
+
{% for t in card.speed_star_types_adj|default:card.speed_star_types %}{% if t == 'full' %}★{% else %}☆{% endif %}{% endfor %}
(orig: {{ card.speed_orig }})
Weight (kg)
{{ card.weight_display }}
-
{% for t in card.weight_star_types %}{% if t == 'full' %}★{% else %}☆{% endif %}{% endfor %}
(orig: {{ card.weight_orig }})
+
{% for t in card.weight_star_types_adj|default:card.weight_star_types %}{% if t == 'full' %}★{% else %}☆{% endif %}{% endfor %}
(orig: {{ card.weight_orig }})
Intelligence (score)
-
{% for t in card.intelligence_star_types %}{% if t == 'full' %}★{% else %}☆{% endif %}{% endfor %}
+
{% for t in card.intelligence_star_types_adj|default:card.intelligence_star_types %}{% if t == 'full' %}★{% else %}☆{% endif %}{% endfor %}
(orig: {{ card.intelligence_orig }})
@@ -62,7 +62,7 @@
Height (m)
{{ card.height_display }}
-
{% for t in card.height_star_types %}{% if t == 'full' %}★{% else %}☆{% endif %}{% endfor %}
(orig: {{ card.height_orig }})
+
{% for t in card.height_star_types_adj|default:card.height_star_types %}{% if t == 'full' %}★{% else %}☆{% endif %}{% endfor %}
(orig: {{ card.height_orig }})
diff --git a/cards/templates/cards/_card_stats_rows.html b/cards/templates/cards/_card_stats_rows.html index 8bf31eb..e4cec92 100644 --- a/cards/templates/cards/_card_stats_rows.html +++ b/cards/templates/cards/_card_stats_rows.html @@ -3,28 +3,28 @@
Speed (km/h)
{{ card.speed_display }}
-
{% for t in card.speed_star_types %}{% if t == 'full' %}★{% else %}☆{% endif %}{% endfor %}
(orig: {{ card.speed_orig }})
+
{% for t in card.speed_star_types_adj|default:card.speed_star_types %}{% if t == 'full' %}★{% else %}☆{% endif %}{% endfor %}
(orig: {{ card.speed_orig }})
Weight (kg)
{{ card.weight_display }}
-
{% for t in card.weight_star_types %}{% if t == 'full' %}★{% else %}☆{% endif %}{% endfor %}
(orig: {{ card.weight_orig }})
+
{% for t in card.weight_star_types_adj|default:card.weight_star_types %}{% if t == 'full' %}★{% else %}☆{% endif %}{% endfor %}
(orig: {{ card.weight_orig }})
Intelligence (score)
{{ card.intelligence_display }}
-
{% for t in card.intelligence_star_types %}{% if t == 'full' %}★{% else %}☆{% endif %}{% endfor %}
(orig: {{ card.intelligence_orig }})
+
{% for t in card.intelligence_star_types_adj|default:card.intelligence_star_types %}{% if t == 'full' %}★{% else %}☆{% endif %}{% endfor %}
(orig: {{ card.intelligence_orig }})
Height (m)
{{ card.height_display }}
-
{% for t in card.height_star_types %}{% if t == 'full' %}★{% else %}☆{% endif %}{% endfor %}
(orig: {{ card.height_orig }})
+
{% for t in card.height_star_types_adj|default:card.height_star_types %}{% if t == 'full' %}★{% else %}☆{% endif %}{% endfor %}
(orig: {{ card.height_orig }})
diff --git a/cards/views.py b/cards/views.py index 4f10f61..8cbaa48 100644 --- a/cards/views.py +++ b/cards/views.py @@ -460,17 +460,100 @@ def print_preview(request): # For each card compute its best stat percent (0-100) and the stat(s) # that achieve that percent. Then find the global maximum best-value and # mark any card whose best-value equals that maximum as a top-trump. + # Compute selection maxima for speed/weight/height/intelligence so we + # can scale these metrics relative to the selected cards. This prevents + # many large absolute values from all clamping to 100% and getting 5★. + max_speed = 0.0 + max_weight = 0.0 + max_height = 0.0 + max_intel = 0.0 + for c in ordered: + try: + sv = float(c.speed or 0) + except Exception: + sv = 0.0 + try: + wv = float(c.weight or 0) + except Exception: + wv = 0.0 + try: + hv = float(c.height or 0) + except Exception: + hv = 0.0 + try: + iv = float(c.intelligence or 0) + except Exception: + iv = 0.0 + if sv > max_speed: + max_speed = sv + if wv > max_weight: + max_weight = wv + if hv > max_height: + max_height = hv + if iv > max_intel: + max_intel = iv + all_best_values = [] for c in ordered: - # compute percents using model helpers - s = c.speed_percent() - w = c.weight_percent() - it = c.intelligence_percent() - h = c.height_percent() - # map names to values + # raw values + try: + raw_s = float(c.speed or 0) + except Exception: + raw_s = 0.0 + try: + raw_w = float(c.weight or 0) + except Exception: + raw_w = 0.0 + try: + raw_h = float(c.height or 0) + except Exception: + raw_h = 0.0 + try: + raw_it = float(c.intelligence or 0) + except Exception: + raw_it = 0.0 + + # Compute selection-relative percents so the largest in the + # selection maps to 100% for each metric. + if max_speed > 0: + s = min(100, int(round((raw_s / max_speed) * 100))) + else: + s = 0 + if max_weight > 0: + w = min(100, int(round((raw_w / max_weight) * 100))) + else: + w = 0 + if max_height > 0: + h = min(100, int(round((raw_h / max_height) * 100))) + else: + h = 0 + if max_intel > 0: + it = min(100, int(round((raw_it / max_intel) * 100))) + else: + it = 0 + + # Attach adjusted star visuals for the printable preview so + # templates reflect selection-relative scaling. These instance + # attributes shadow the class properties during template rendering. + setattr(c, 'speed_star_types_adj', c._star_types_from_percent(s)) + setattr(c, 'speed_stars_adj', c._stars_string(s)) + setattr(c, 'weight_star_types_adj', c._star_types_from_percent(w)) + setattr(c, 'weight_stars_adj', c._stars_string(w)) + setattr(c, 'height_star_types_adj', c._star_types_from_percent(h)) + setattr(c, 'height_stars_adj', c._stars_string(h)) + setattr(c, 'intelligence_star_types_adj', c._star_types_from_percent(it)) + setattr(c, 'intelligence_stars_adj', c._stars_string(it)) + + setattr(c, 'speed_percent_adj', s) + setattr(c, 'weight_percent_adj', w) + setattr(c, 'height_percent_adj', h) + setattr(c, 'intelligence_percent_adj', it) + + # map names to values (independent stats) using adjusted percents stats = {'speed': s, 'weight': w, 'intelligence': it, 'height': h} max_val = max(stats.values()) best_stats = [name for name, val in stats.items() if val == max_val] + # attach computed attributes so templates can read them setattr(c, 'best_value', max_val) setattr(c, 'best_stats', best_stats)