Files
rrttm/bullshit.py
2026-09-16 18:52:00 +02:00

306 lines
10 KiB
Python

import math
K_NORMAL = 100
K_DEFUP = 50
MIN_DMG = 1
class Combatant:
def __init__(self, name, maxhp, attack, speed, def_phys, def_fire, def_ice, abs_fire, abs_ice):
self.name = name
self.max_atb = 1000/speed
self.atb = self.max_atb
self.maxhp = maxhp
self.hp = maxhp
self.attack = attack
self.ko = False
self.stone = False
self.def_up = False
self.def_phys = def_phys
self.def_fire = def_fire
self.def_ice = def_ice
self.abs_fire = abs_fire
self.abs_ice = abs_ice
# POTENTIAL damage before defense effects
class Damage:
def __init__(self, phys, fire, ice, healing):
self.phys= phys
self.fire= fire
self.ice= ice
self.healing = healing
# 0 = keep, 1 = inflict, 2 = remove
class StatusChange:
def __init__(self, ko, stone, def_up):
self.ko = ko
self.stone = stone
self.def_up = def_up
# where = reference to the target, who = reference to the inflictor
class Action:
def __init__(self, name, who, where, damage, status):
self.name = name
self.who = who
self.where = where
self.damage = damage
self.status = status
def __str__(self):
return (
f"[{self.name}] {self.who.name} -> {self.where.name} "
f"(HP influence: ({self.damage.phys},{self.damage.fire},{self.damage.ice},{self.damage.healing}), Status: {self.status.ko}-{self.status.stone}-{self.status.def_up})"
)
def __repr__(self):
return self.__str__()
def execute(self):
target = self.where
if self.who.ko or self.who.stone:
print(f" -> {self.who.name} cannot move!")
return
self.who.atb = self.who.max_atb
if target.ko and self.status.ko != 2:
print(f" -> {target.name} is KO'd, move cancelled!")
return
K = K_DEFUP if target.def_up else K_NORMAL
delta_hp = self.damage.healing
if self.damage.phys:
delta_hp += -max(MIN_DMG, self.damage.phys * (K / (K + target.def_phys)))
if self.damage.fire:
delta_hp += self.damage.fire if target.abs_fire else -max(MIN_DMG, self.damage.fire * (K / (K + target.def_fire)))
if self.damage.ice:
delta_hp += self.damage.ice if target.abs_ice else -max(MIN_DMG, self.damage.ice * (K / (K + target.def_ice )))
if self.status.ko == 1:
delta_hp = -target.hp
elif self.status.ko == 2 and target.ko:
target.ko = False
print(f" -> {target.name}'s consciousness returned!")
delta_hp = round(delta_hp)
if delta_hp > 0:
print(f" -> {target.name} got healed for {delta_hp} HP!")
elif delta_hp < 0:
print(f" -> {target.name} suffered {-delta_hp} damage!")
target.hp = max(0, min(target.maxhp, target.hp + delta_hp))
if self.status.stone == 1 and not target.stone:
target.stone = True
print(f" -> {target.name}'s body petrified!")
elif self.status.stone == 2 and target.stone:
target.stone = False
print(f" -> {target.name}'s body softened back up!")
if self.status.def_up == 1 and not target.def_up:
target.def_up = True
print(f" -> {target.name}'s defense was boosted!")
elif self.status.def_up == 2 and target.def_up:
target.def_up = False
print(f" -> {target.name}'s defense returned to normal!")
if target.hp == 0:
target.ko = True
target.stone = False
target.def_up = False
print(f" -> {target.name} got hurt and collapsed!")
# !!! CHATGPT !!! CSAK TESZT !!!
enemies = [
# name hp atk spd dphys dfire dice absF absI
Combatant("Six-Seven kid", 120, 30, 80, 20, 0, 50, False, False),
Combatant("Ice Sprite", 60, 15, 150, 10, 0, 200, False, True ), # ice-immune, fast, fragile
Combatant("Magma Brute", 200, 40, 40, 60, 999, -50, True, False), # absorbs fire, weak to ice, slow tank
]
party_members = [
# name hp atk spd dphys dfire dice absF absI
Combatant("Terra", 100, 35, 100, 30, 30, 30, False, False),
Combatant("Locke", 80, 45, 130, 15, 10, 10, False, False), # glass cannon, fast, low def
Combatant("Celes", 110, 25, 90, 50, 40, 40, False, False), # tanky
Combatant("Cyan", 130, 38, 70, 40, 20, 20, False, False),
]
tick_counter = 0
action_queue = []
# ---------------------------------------------------------------------------
# Move library — a spread of physical/elemental/status/healing effects
# ---------------------------------------------------------------------------
def move_attack(user, target):
return Action("Attack", user, target, Damage(user.attack, 0, 0, 0), StatusChange(0, 0, 0))
def move_fire(user, target):
return Action("Fira", user, target, Damage(0, 45, 0, 0), StatusChange(0, 0, 0))
def move_ice(user, target):
return Action("Blizzara", user, target, Damage(0, 0, 45, 0), StatusChange(0, 0, 0))
def move_cure(user, target):
return Action("Cure", user, target, Damage(0, 0, 0, 42), StatusChange(0, 0, 0))
def move_defend(user, target):
return Action("Defend", user, target, Damage(0, 0, 0, 0), StatusChange(0, 0, 1))
def move_break(user, target):
return Action("Break", user, target, Damage(0, 0, 0, 0), StatusChange(0, 1, 0))
def move_death(user, target):
return Action("Death", user, target, Damage(0, 0, 0, 0), StatusChange(1, 0, 0))
def move_phoenix(user, target):
# revive w/ modest healing; execute() floors it at +1 HP if this isn't enough
return Action("Phoenix Down", user, target, Damage(0, 0, 0, 30), StatusChange(2, 0, 0))
def move_softenup(user, target):
return Action("Esuna (remove stoneskin)", user, target, Damage(0, 0, 0, 0), StatusChange(2, 0, 0))
MOVES = {
"attack": ("Physical attack", move_attack, True),
"fire": ("Fira (fire magic)", move_fire, True),
"ice": ("Blizzara (ice magic)", move_ice, True),
"cure": ("Cure (heal ally)", move_cure, False),
"defend": ("Defend (self def_up)", move_defend, None), # self-only, forced
"break": ("Break (inflict stone)", move_break, True),
"death": ("Death (instant KO)", move_death, True),
"phoenix": ("Phoenix Down (revive ally)",move_phoenix, False),
"unstone": ("Esuna (cure stone)", move_softenup,False),
}
HELP_TEXT = (
"\nAvailable moves:\n" +
"\n".join(f" {k:8s} - {v[0]}" for k, v in MOVES.items()) +
"\n help - show this list\n"
)
def choose_target(user, candidates, prompt):
alive = [c for c in candidates]
print(prompt)
for i, c in enumerate(alive):
tags = []
if c.ko: tags.append("KO")
if c.stone: tags.append("STONE")
if c.def_up: tags.append("DEF_UP")
tagstr = f" [{', '.join(tags)}]" if tags else ""
print(f" {i}: {c.name} ({c.hp}/{c.maxhp} HP){tagstr}")
while True:
raw = input("Target #: ").strip()
if raw.isdigit() and 0 <= int(raw) < len(alive):
return alive[int(raw)]
print("Invalid target index.")
def player_turn(p):
print(f"\n{p.name} is ready to act! ({p.hp}/{p.maxhp} HP, def_up={p.def_up}, stone={p.stone})")
while True:
act = input("Action (or 'help'): ").strip().lower()
if act == "help":
print(HELP_TEXT)
continue
if act not in MOVES:
print("Unknown move. Type 'help' for the list.")
continue
label, builder, needs_enemy_target = MOVES[act]
if act == "defend":
return builder(p, p)
if needs_enemy_target is True:
target = choose_target(p, enemies, "Choose an enemy target:")
elif needs_enemy_target is False:
target = choose_target(p, party_members, "Choose a party target:")
else:
target = p
return builder(p, target)
import random
def enemy_turn(e):
living = [p for p in party_members if not p.ko]
if not living:
return None
# weighted move pool -- enemies get the same toolkit as the party
offensive = ["attack", "fire", "ice", "death"]
support = ["break"]
self_only = ["defend"]
# if an ally is KO'd, occasionally try to revive/cure instead of attacking
ko_allies = [a for a in enemies if a.ko and a is not e]
hurt_allies = [a for a in enemies if not a.ko and a.hp < a.maxhp]
roll = random.random()
if ko_allies and roll < 0.15:
act = "phoenix"
target = random.choice(ko_allies)
elif hurt_allies and roll < 0.30:
act = "cure"
target = random.choice(hurt_allies)
elif not e.def_up and roll < 0.40:
act = "defend"
target = e
elif roll < 0.55:
act = "break"
target = random.choice(living)
elif roll < 0.65:
act = "death"
target = random.choice(living)
else:
act = random.choice(["attack", "fire", "ice"])
target = random.choice(living)
_, builder, _ = MOVES[act]
print(f" ({e.name} chooses {act} -> {target.name})")
return builder(e, target)
while True:
print(f"\n=== Tick #{tick_counter} ===")
for e in enemies:
if e.ko or e.stone:
continue
e.atb -= 1
if e.atb <= 0:
act = enemy_turn(e)
if act:
action_queue.append(act)
for p in party_members:
if p.ko or p.stone:
continue
p.atb -= 1
if p.atb <= 0:
action_queue.append(player_turn(p))
if len(action_queue) != 0:
action = action_queue.pop(0)
print(f"Executing: {action}")
action.execute()
if all(e.ko for e in enemies):
print("\nVICTORY! all enemies were defeated.")
break
if all(p.ko for p in party_members):
print("\nGAME OVER! the party was defeated.")
break
tick_counter += 1