From 3296617c04cbb1ce367394862895b97fecd75296 Mon Sep 17 00:00:00 2001 From: Jesusaves Date: Thu, 5 Aug 2021 15:01:11 -0300 Subject: Move a lot of battle logic to battle/common.py (which is no longer just an util file) --- battle/common.py | 272 +++++++++++++++++++++++++++++++++++++++++++++++++++++- battle/main.py | 270 +++-------------------------------------------------- battle/summons.py | 39 ++------ 3 files changed, 290 insertions(+), 291 deletions(-) diff --git a/battle/common.py b/battle/common.py index f359597..ba3361c 100644 --- a/battle/common.py +++ b/battle/common.py @@ -16,10 +16,19 @@ # along with this program. If not, see . ######################################################################################## # Battle Module - Common functions -from utils import stdout, Battle -from consts import (SC_ATKUP, SC_DEFDOWN, SC_DEFUP, SC_ATKDOWN, Ele_Fire, - Ele_Water, Ele_Nature, Ele_Light, Ele_Shadow) +from utils import stdout, Battle, compress, dl_search +from consts import (SC_ATKUP, SC_DEFDOWN, SC_DEFUP, SC_ATKDOWN, + Ele_Fire, Ele_Water, Ele_Nature, Ele_Light, Ele_Shadow, + ST_TOWN, + CRYSTAL_MIN, CRYSTAL_MAX, EXPRATE_MIN, EXPRATE_MAX, GPPRATE_MIN, GPPRATE_MAX, + SFLAG_CLEARGEMS, SFLAG_DOUBLEGEMS, SFLAG_SPECIAL, + SFLAG_FIRSTLOOT, SFLAG_DOUBLEEXP, SFLAG_DOUBLEGP, + SPH_WIDEATTACK, SPH_PIERCE, SPH_ASSAULT, SPH_HEAL, SPH_HEALALL, SPH_ATKUP, + SPH_DEFUP, SPH_NONE, SRV_SPHERE, SRV_WAVE) import random +import player + +############################################# # Check victory condition def check_enemy_alive(token): for i in Battle[token]["enemy"]: @@ -27,6 +36,7 @@ def check_enemy_alive(token): return True return False +############################################# # Check defeat condition def check_player_alive(token): for i in Battle[token]["party"]: @@ -34,6 +44,7 @@ def check_player_alive(token): return True return False +############################################# # Calculate damage. Unit is a member from Battle[token][scope]. Base is dmg value # crit is a customizable critical chance def calc_dmg(token, attacker, defender, base, crit=0.1): @@ -78,6 +89,7 @@ def calc_dmg(token, attacker, defender, base, crit=0.1): # Impossible to move or miss is handled before return int(dmg) +############################################# # Attack all, scope can be: "enemy" or "party" def attackall(token, atker, scope): for c, i in enumerate(Battle[token][scope]): @@ -92,6 +104,7 @@ def attackall(token, atker, scope): #Battle[token]["log"].append(["party", idx, sphere, dmg, scope, c]) return False +############################################# # Find and return target ID in Battle[token][scope]. def find_target(token, scope): global Battle @@ -124,4 +137,257 @@ def find_target(token, scope): stdout("Selected: %d" % opt, 2) return opt +############################################# +# Structural functions +############################################# +def conditions(token, spheres): + global Battle, Player + + ## Maybe you won? + if (not check_enemy_alive(token)): + # You won! Do we have a next wave? + stdout("You won!") + if (Battle[token]["wave"] < Battle[token]["max_wave"]): + stdout("Next wave detected: %s, Quest ID: %s\nWave: %s" % (token, Battle[token]["quest_id"], Battle[token]["wave"])) + advance_wave(token, Battle[token]["world"], Battle[token]["quest_id"], Battle[token]["wave"]) + # Do not continue this loop: End the turn now + return battle_endturn(token, spheres) + else: + # You won! + stdout("Total Victory") + Player[token]["status"]=ST_TOWN + # TODO: enemy die = add reward + result=get_result(token, True, Battle[token]["world"], Battle[token]["quest_id"]) + del Battle[token] + return compress(result) + + ## But maybe you lost? + if (not check_player_alive(token)): + # You lost! + Player[token]["status"]=ST_TOWN + result=get_result(token, False, Battle[token]["world"], Battle[token]["quest_id"]) + del Battle[token] + return compress(result) + + ## Return None so we know nothing happened + return None + +############################################# +# Update enemy list with the next wave data. No checks are conducted. +def advance_wave(token, world, quest_id, next_wave): + global Battle, allquests + Battle[token]["enemy"]=[] + Battle[token]["log"].append(["", 0, SRV_WAVE, 0, "", 0]) + stdout("advance_wave was called") + + quest=dl_search(allquests[world], "quest_id", quest_id) + if quest == "ERROR": + stdout("ERROR, INVALID QUEST: %d" % (quest_id), 0) + # TODO: HANDLE THIS ERROR (FIXME) + + for en in quest["waves"][next_wave]: + mil=quest["difficulty"]/10.0 + if (en["boss"]): + mil*=5.5 + + mil=mil + stdout("Recording new enemy with mil: %d" % mil, 2) + Battle[token]["enemy"].append({ + "name": en["name"], + "unit_id": en["sprite"], + "max_hp": int(900*mil), + "hp": int(900*mil), + "atk": int(100*mil), + "ele": en["attribute"], + "status_effects": 0 + }) + + # Update wave + stdout("Advancing wave", 2) + Battle[token]["wave"]+=1 + return True + +############################################# +# get_result(str, bool, str, int) +def get_result(token, victory, world, quest_id): + global Player, Battle, allquests + result={ + "result": "", + "gp": 0, + "exp": 0, + "crystals": 0, + "loot": [], + "rank": 0, + "log": Battle[token]["log"] + } + + stdout("GR: Begin", 2) + # You lost? + if not victory: + result["result"]="DEFEAT" + return result + + # Prepare data + result["result"]="VICTORY" + quest=dl_search(allquests[world], "quest_id", quest_id) + if quest == "ERROR": + print("ERROR, INVALID QUEST") + stdout("Quest %d is invalid" % quest_id, 0) + # TODO: HANDLE THIS ERROR (FIXME) + return result + + stdout("GR: Rolling", 2) + # Roll each wave + # Base quest experience gain + result["exp"]+=quest["difficulty"]*10 + for wave in quest["waves"]: + for en in wave: + # Roll GP for each wave + if en["boss"]: + result["gp"]+=quest["difficulty"]*30 + result["exp"]+=quest["difficulty"] + else: + result["gp"]+=quest["difficulty"]*10 + result["exp"]+=quest["difficulty"]/3 + + # TODO: Roll the loots for every enemy in every death + + result["exp"]=int(result["exp"]) + stdout("GR: Looting", 2) + # For now, loots are rolled for quest + # 2- Roll loot list + for loot, chance in quest["loot"]: + if (random.randint(0, 10000) < chance): + loot=int(loot) + # Crystals are... Tricky + if loot >= CRYSTAL_MIN and loot <= CRYSTAL_MAX: + result["crystals"]+=(loot-CRYSTAL_MIN) + elif loot >= EXPRATE_MIN and loot <= EXPRATE_MAX: + result["exp"]*=(loot-EXPRATE_MIN)/1000.0 + result["exp"]=int(result["exp"]) + elif loot >= GPPRATE_MIN and loot <= GPPRATE_MAX: + result["gp"]*=(loot-GPPRATE_MIN)/1000.0 + result["gp"]=int(result["gp"]) + else: + result["loot"].append(int(loot)*100) # Fix Unit ID from base to ID + + stdout("GR: Flagging", 2) + # Mark the quest as complete and grant you crystals for first clear + # But this is based on the flags (we can have special quests) + if (not (quest["flags"] & SFLAG_SPECIAL) and Player[token]["quest"] < quest["quest_id"]): + Player[token]["quest"]=0+quest["quest_id"] + if (quest["flags"] & SFLAG_CLEARGEMS): + result["crystals"]+=100 + if (quest["flags"] & SFLAG_DOUBLEGEMS): + result["crystals"]+=200 + if (quest["flags"] & SFLAG_FIRSTLOOT): + loot=int(quest["loot"][0][0]) + # Crystals are... Tricky + if loot >= CRYSTAL_MIN and loot <= CRYSTAL_MAX: + result["crystals"]+=(loot-CRYSTAL_MIN) + else: + result["loot"].append(int(loot)*100) # Fix Unit ID from base to ID + if (quest["flags"] & SFLAG_DOUBLEEXP): + result["exp"]*=2 + if (quest["flags"] & SFLAG_DOUBLEGP): + result["gp"]*=2 + + stdout("GR: Applying", 2) + # Apply the results to player data + Player[token]["gp"]+=result["gp"] + Player[token]["exp"]+=result["exp"] + Player[token]["crystals"]+=result["crystals"] + for it in result["loot"]: + ix=player.inventoryplace(token) + if (ix >= 0): + unit={ + "unit_id": it, + "level": 1, + "exp": 0 + } + Player[token]["inv"][ix]=unit + else: + result["loot"].remove(it) + # TODO: Send result ERR_FULL + + stdout("GR: EXPing", 2) + # Grant to party the same amount of experience + pid=Battle[token]["party_id"] + for m in Player[token]["party_"+str(pid)]: + # This can happen normally + if m["unit_id"] <= 0: + continue + player.grant_exp(token, m["inv_id"], result["exp"]) + v5=player.check_level_up(token, m["inv_id"]) + # FIXME: Don't disregard result + stdout("Unit %d levelled up %d times" % (m["inv_id"], v5)) + + + stdout("GR: Ranking", 2) + # Player rank up + rk=player.check_rank_up(token) + if rk["code"]: + result["rank"]=1+rk["ap"] + result["rk_exp"]=Player[token]["exp"] + result["rk_mexp"]=Player[token]["max_exp"] + + stdout("GR: Complete") + + # Send data + return result + +############################################# +# This is separate for loop logic reasons. Ends the turn. +# Gives sphere, handle rewards if needed and sends data to client +def battle_endturn(token, spheres): + global Battle + # We now have to handle turn end and conditions + Battle[token]["turn"]+=1 + stdout("Turn has ended") + + # Resave spheres + stdout("Sphere list: "+str(spheres), 2) + Battle[token]["spheres"]=[] + for i in spheres: + Battle[token]["spheres"].append(int(i)) + + # Remove an eventual none spheres + while (Battle[token]["spheres"].count(SPH_NONE)): + Battle[token]["spheres"].remove(SPH_NONE) + stdout("Exceeding spheres removed", 2) + + # Add 1~3 random spheres; But never go over 5 spheres + i=min(random.randint(1,3), 5 - len(Battle[token]["spheres"])) + while i > 0: + i-=1 + sp=random.choice([ + SPH_WIDEATTACK, + SPH_PIERCE, + SPH_ASSAULT, + SPH_HEAL, + SPH_HEALALL, + SPH_ATKUP, + SPH_DEFUP + ]) + Battle[token]["log"].append(["spheres", len(Battle[token]["spheres"]), SRV_SPHERE, sp, "", 0]) + Battle[token]["spheres"].append(sp) + + stdout("Spheres added; Status before adjust: %s" % (str(Battle[token]["spheres"])), 2) + + # Add empty spheres to keep length + while (len(Battle[token]["spheres"]) < 5): + Battle[token]["spheres"].append(SPH_NONE) + + # Delete Summon data if exists + try: + del Battle["s"] + except: + pass + + # Send data to client + stdout("Sending data", 2) + sjson=compress(Battle[token]) + stdout("Data sent: %s" % sjson) + return sjson + diff --git a/battle/main.py b/battle/main.py index 3cc92f8..19a558b 100644 --- a/battle/main.py +++ b/battle/main.py @@ -17,17 +17,14 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ######################################################################################## # Battle Module - Main module -from utils import stdout, dl_search, compress, allunits, Player, Battle, allquests -from consts import (CRYSTAL_MIN, CRYSTAL_MAX, EXPRATE_MIN, EXPRATE_MAX, - GPPRATE_MIN, GPPRATE_MAX, ERR_ERR, ERR_BAD, ST_TOWN, ST_QUEST, ACT_SPHERE, - SFLAG_CLEARGEMS, SFLAG_DOUBLEGEMS, SFLAG_SPECIAL, - SFLAG_FIRSTLOOT, SFLAG_DOUBLEEXP, SFLAG_DOUBLEGP, +from utils import stdout, compress, allunits, Player, Battle, allquests +from consts import (ERR_ERR, ERR_BAD, ST_TOWN, ST_QUEST, ACT_SPHERE, SPH_WIDEATTACK, SPH_PIERCE, SPH_ASSAULT, SPH_HEAL, SPH_HEALALL, SPH_ATKUP, - SPH_DEFUP, SPH_NONE, SRV_WAVE, SRV_SPHERE) + SPH_DEFUP, SPH_NONE) import json, random, traceback import player -from battle.common import (find_target, check_enemy_alive, check_player_alive, - calc_dmg) +from battle.common import (find_target, calc_dmg, conditions, advance_wave, + battle_endturn) from battle.spheres import sphere_attack #from battle.summons import summon # <-- Protocol? #from battle.skills import skill_core, handle_skill # <-- WIP @@ -35,170 +32,6 @@ from battle.spheres import sphere_attack # Prepare a randomness seed random.seed() -############################################# -# Structural functions -############################################# -# Update enemy list with the next wave data. No checks are conducted. -def advance_wave(token, world, quest_id, next_wave): - global Battle, allquests - Battle[token]["enemy"]=[] - Battle[token]["log"].append(["", 0, SRV_WAVE, 0, "", 0]) - stdout("advance_wave was called") - - quest=dl_search(allquests[world], "quest_id", quest_id) - if quest == "ERROR": - stdout("ERROR, INVALID QUEST: %d" % (quest_id), 0) - # TODO: HANDLE THIS ERROR (FIXME) - - for en in quest["waves"][next_wave]: - mil=quest["difficulty"]/10.0 - if (en["boss"]): - mil*=5.5 - - mil=mil - stdout("Recording new enemy with mil: %d" % mil, 2) - Battle[token]["enemy"].append({ - "name": en["name"], - "unit_id": en["sprite"], - "max_hp": int(900*mil), - "hp": int(900*mil), - "atk": int(100*mil), - "ele": en["attribute"], - "status_effects": 0 - }) - - # Update wave - stdout("Advancing wave", 2) - Battle[token]["wave"]+=1 - return True - -# get_result(str, bool, str, int) -def get_result(token, victory, world, quest_id): - global Player, Battle, allquests - result={ - "result": "", - "gp": 0, - "exp": 0, - "crystals": 0, - "loot": [], - "rank": 0, - "log": Battle[token]["log"] - } - - stdout("GR: Begin", 2) - # You lost? - if not victory: - result["result"]="DEFEAT" - return result - - # Prepare data - result["result"]="VICTORY" - quest=dl_search(allquests[world], "quest_id", quest_id) - if quest == "ERROR": - print("ERROR, INVALID QUEST") - stdout("Quest %d is invalid" % quest_id, 0) - # TODO: HANDLE THIS ERROR (FIXME) - return result - - stdout("GR: Rolling", 2) - # Roll each wave - # Base quest experience gain - result["exp"]+=quest["difficulty"]*10 - for wave in quest["waves"]: - for en in wave: - # Roll GP for each wave - if en["boss"]: - result["gp"]+=quest["difficulty"]*30 - result["exp"]+=quest["difficulty"] - else: - result["gp"]+=quest["difficulty"]*10 - result["exp"]+=quest["difficulty"]/3 - - # TODO: Roll the loots for every enemy in every death - - result["exp"]=int(result["exp"]) - stdout("GR: Looting", 2) - # For now, loots are rolled for quest - # 2- Roll loot list - for loot, chance in quest["loot"]: - if (random.randint(0, 10000) < chance): - loot=int(loot) - # Crystals are... Tricky - if loot >= CRYSTAL_MIN and loot <= CRYSTAL_MAX: - result["crystals"]+=(loot-CRYSTAL_MIN) - elif loot >= EXPRATE_MIN and loot <= EXPRATE_MAX: - result["exp"]*=(loot-EXPRATE_MIN)/1000.0 - result["exp"]=int(result["exp"]) - elif loot >= GPPRATE_MIN and loot <= GPPRATE_MAX: - result["gp"]*=(loot-GPPRATE_MIN)/1000.0 - result["gp"]=int(result["gp"]) - else: - result["loot"].append(int(loot)*100) # Fix Unit ID from base to ID - - stdout("GR: Flagging", 2) - # Mark the quest as complete and grant you crystals for first clear - # But this is based on the flags (we can have special quests) - if (not (quest["flags"] & SFLAG_SPECIAL) and Player[token]["quest"] < quest["quest_id"]): - Player[token]["quest"]=0+quest["quest_id"] - if (quest["flags"] & SFLAG_CLEARGEMS): - result["crystals"]+=100 - if (quest["flags"] & SFLAG_DOUBLEGEMS): - result["crystals"]+=200 - if (quest["flags"] & SFLAG_FIRSTLOOT): - loot=int(quest["loot"][0][0]) - # Crystals are... Tricky - if loot >= CRYSTAL_MIN and loot <= CRYSTAL_MAX: - result["crystals"]+=(loot-CRYSTAL_MIN) - else: - result["loot"].append(int(loot)*100) # Fix Unit ID from base to ID - if (quest["flags"] & SFLAG_DOUBLEEXP): - result["exp"]*=2 - if (quest["flags"] & SFLAG_DOUBLEGP): - result["gp"]*=2 - - stdout("GR: Applying", 2) - # Apply the results to player data - Player[token]["gp"]+=result["gp"] - Player[token]["exp"]+=result["exp"] - Player[token]["crystals"]+=result["crystals"] - for it in result["loot"]: - ix=player.inventoryplace(token) - if (ix >= 0): - unit={ - "unit_id": it, - "level": 1, - "exp": 0 - } - Player[token]["inv"][ix]=unit - else: - result["loot"].remove(it) - # TODO: Send result ERR_FULL - - stdout("GR: EXPing", 2) - # Grant to party the same amount of experience - pid=Battle[token]["party_id"] - for m in Player[token]["party_"+str(pid)]: - # This can happen normally - if m["unit_id"] <= 0: - continue - player.grant_exp(token, m["inv_id"], result["exp"]) - v5=player.check_level_up(token, m["inv_id"]) - # FIXME: Don't disregard result - stdout("Unit %d levelled up %d times" % (m["inv_id"], v5)) - - - stdout("GR: Ranking", 2) - # Player rank up - rk=player.check_rank_up(token) - if rk["code"]: - result["rank"]=1+rk["ap"] - result["rk_exp"]=Player[token]["exp"] - result["rk_mexp"]=Player[token]["max_exp"] - - stdout("GR: Complete") - - # Send data - return result ############################################# # Client commands @@ -328,59 +161,6 @@ def begin_quest(args, token, client_side=True): -# This is separate for loop logic reasons. Ends the turn. -# Gives sphere, handle rewards if needed and sends data to client -def battle_endturn(token, spheres): - global Battle - # We now have to handle turn end and conditions - Battle[token]["turn"]+=1 - stdout("Turn has ended") - - # Resave spheres - stdout("Sphere list: "+str(spheres), 2) - Battle[token]["spheres"]=[] - for i in spheres: - Battle[token]["spheres"].append(int(i)) - - # Remove an eventual none spheres - while (Battle[token]["spheres"].count(SPH_NONE)): - Battle[token]["spheres"].remove(SPH_NONE) - stdout("Exceeding spheres removed", 2) - - # Add 1~3 random spheres; But never go over 5 spheres - i=min(random.randint(1,3), 5 - len(Battle[token]["spheres"])) - while i > 0: - i-=1 - sp=random.choice([ - SPH_WIDEATTACK, - SPH_PIERCE, - SPH_ASSAULT, - SPH_HEAL, - SPH_HEALALL, - SPH_ATKUP, - SPH_DEFUP - ]) - Battle[token]["log"].append(["spheres", len(Battle[token]["spheres"]), SRV_SPHERE, sp, "", 0]) - Battle[token]["spheres"].append(sp) - - stdout("Spheres added; Status before adjust: %s" % (str(Battle[token]["spheres"])), 2) - - # Add empty spheres to keep length - while (len(Battle[token]["spheres"]) < 5): - Battle[token]["spheres"].append(SPH_NONE) - - # Delete Summon data if exists - try: - del Battle["s"] - except: - pass - - # Send data to client - stdout("Sending data", 2) - sjson=compress(Battle[token]) - stdout("Data sent: %s" % sjson) - return sjson - # Advance turn button. Main turn logic # Receives a list of units position, and if they'll use sphere: # { "unit": [uid1, uid2 ....], "sphere": [True/False array, in future will save skill as 2] } @@ -482,34 +262,13 @@ def battle(args, token): if (bat["hp"] > bat["max_hp"]): bat["hp"]=0+bat["max_hp"] - # HOLD THAT! Handle event - if (not check_enemy_alive(token)): - # You won! Do we have a next wave? - stdout("You won!") - if (Battle[token]["wave"] < Battle[token]["max_wave"]): - stdout("Next wave detected: %s, Quest ID: %s\nWave: %s" % (token, Battle[token]["quest_id"], Battle[token]["wave"])) - advance_wave(token, Battle[token]["world"], Battle[token]["quest_id"], Battle[token]["wave"]) - # Do not continue this loop: End the turn now - return battle_endturn(token, spheres) - else: - # You won! - stdout("Total Victory") - Player[token]["status"]=ST_TOWN - # TODO: enemy die = add reward - result=get_result(token, True, Battle[token]["world"], Battle[token]["quest_id"]) - del Battle[token] - return compress(result) + # HOLD THAT! Handle victory/defeat conditions + check = conditions(token, spheres) + if check is not None: + return check stdout("Friends executed", 2) - # It never harms checking for suicide... - if (not check_player_alive(token)): - # You lost! - Player[token]["status"]=ST_TOWN - result=get_result(token, False, Battle[token]["world"], Battle[token]["quest_id"]) - del Battle[token] - return compress(result) - ############ # Enemies for idx, bat in enumerate(Battle[token]["enemy"]): @@ -533,13 +292,10 @@ def battle(args, token): bat["hp"]=0+bat["max_hp"] continue - # HOLD THAT! Handle event - if (not check_player_alive(token)): - # You lost! - Player[token]["status"]=ST_TOWN - result=get_result(token, False, Battle[token]["world"], Battle[token]["quest_id"]) - del Battle[token] - return compress(result) + # HOLD THAT! Handle victory/defeat conditions + check = conditions(token, spheres) + if check is not None: + return check stdout("Enemies executed", 2) diff --git a/battle/summons.py b/battle/summons.py index f3f4fd1..ef1b107 100644 --- a/battle/summons.py +++ b/battle/summons.py @@ -19,10 +19,10 @@ import json from utils import (compress, allsummons, stdout, dl_search, Player, Battle) -from consts import (ERR_ERR, ERR_BAD, ST_TOWN) +from consts import (ERR_ERR, ERR_BAD) from battle.skills import skill_core -from battle.common import check_enemy_alive, check_player_alive -from battle.main import advance_wave, battle_endturn, get_result +from battle.common import conditions +#from battle.main import advance_wave, battle_endturn, get_result def handle_summon(token, summon): global Battle, Player @@ -36,35 +36,12 @@ def handle_summon(token, summon): except: return ERR_ERR - # Check for victory/defeat conditions - # Huge code duplication - stdout("Summon: Victory/Defeat Check") - # HOLD THAT! Handle event - if (not check_enemy_alive(token)): - # You won! Do we have a next wave? - stdout("You won!") - if (Battle[token]["wave"] < Battle[token]["max_wave"]): - stdout("Next wave detected: %s, Quest ID: %s\nWave: %s" % (token, Battle[token]["quest_id"], Battle[token]["wave"])) - advance_wave(token, Battle[token]["world"], Battle[token]["quest_id"], Battle[token]["wave"]) - # Do not continue this loop: End the turn now - return battle_endturn(token, Battle[token]["spheres"]) - else: - # You won! - stdout("Total Victory") - Player[token]["status"]=ST_TOWN - # TODO: enemy die = add reward - result=get_result(token, True, Battle[token]["world"], Battle[token]["quest_id"]) - del Battle[token] - return compress(result) - - # It never harms checking for suicide... - if (not check_player_alive(token)): - # You lost! - Player[token]["status"]=ST_TOWN - result=get_result(token, False, Battle[token]["quest_id"]) - del Battle[token] - return compress(result) + + # HOLD THAT! Handle victory/defeat conditions + check = conditions(token, Battle[token]["spheres"]) + if check is not None: + return check # The check is: if not ERR_ERR or ERR_BAD, then reload battle/result sjson=compress(Battle[token]) -- cgit v1.2.3-60-g2f50