########################################################################################
# This file is part of Spheres.
# Copyright (C) 2019 Jesusalva
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
########################################################################################
# Combat Functions
init python:
######################## Server Communications
def reload_battle():
raw=send_packet("reload_battle", "")
bt=json_decode(raw)
if (bt == ERR_JSONDECODER):
return ERR_JSONDECODER
return bt
def bt_handlelog(entry):
if entry[2] in [SPH_NONE, SPH_PIERCE, SPH_ASSAULT, SPH_HEAL]:
## Play audio (WIP)
## FIXME: Play -2 versions as well, randomly
try:
audi="sfx/w/%d-1.ogg" % Battle[entry[0]][entry[1]]["job"]
except:
audi="invalid job on source"
if renpy.loadable(audi):
renpy.play(audi)
else:
print("WARNING: Audio file \"%s\" does not exist" % audi)
## Animate
hit_someone_verbose(Battle[entry[4]][entry[5]],
entry[3])
sdelay(0.1)
print("Attacked Battle[%s][%d]" % (entry[4], entry[5]))
elif entry[2] == SRV_SUMMON:
# FIXME
print("Summoned %d to your aid! Show cutscene" % entry[3])
show_img("summon_%d" % entry[3], False)
renpy.show("summon_%d" % entry[3], at_list=[truecenter, tzoomin], tag="summon", zorder=29150, layer="overlay")
renpy.pause(2.0)
renpy.hide("summon", layer="overlay")
#renpy.with_statement(Dissolve(1.0))
return
label combat:
# Implement combat view
$stdout("================= prepare for combat")
$ hud_clear()
$ show_img("bg %s" % TMP_BACKG, False)
scene bg battle
# Load variables
python:
try:
fx1="unit_"+str(Battle["party"][0]["unit_id"])
show_img(fx1, False) # Validate
except:
fx1=""
try:
fx2="unit_"+str(Battle["party"][1]["unit_id"])
show_img(fx2, False) # Validate
except:
fx2=""
try:
fx3="unit_"+str(Battle["party"][2]["unit_id"])
show_img(fx3, False) # Validate
except:
fx3=""
try:
fx4="unit_"+str(Battle["party"][3]["unit_id"])
show_img(fx4, False) # Validate
except:
fx4=""
try:
fx5="unit_"+str(Battle["party"][4]["unit_id"])
show_img(fx5, False) # Validate
except:
fx5=""
try:
en1=Battle["enemy"][0]
show_img("mob_"+str(en1["unit_id"]), False) # Validate
except:
en1=copy.copy(ERR_MOBSTRUCT)
try:
en2=Battle["enemy"][1]
show_img("mob_"+str(en2["unit_id"]), False) # Validate
except:
en2=copy.copy(ERR_MOBSTRUCT)
try:
en3=Battle["enemy"][2]
show_img("mob_"+str(en3["unit_id"]), False) # Validate
except:
en3=copy.copy(ERR_MOBSTRUCT)
while len(Battle["party"]) < 4:
Battle["party"].append(copy.copy(ERR_PLAYERSTRUCT))
$stdout("================= call begin")
# TODO: Swap units support
# TODO: Display Spheres in the right place
$ use_sphere=[AP_NONE, AP_NONE, AP_NONE, AP_NONE, AP_NONE]
$ btl_ready = False
$ do_action = ACT_NONE
$ csid = None
python:
try:
do_action = renpy.call_screen("battle", _with_none=True)
except:
traceback.print_exc()
stdout("FAILED to render battle screen, trying again...")
renpy.hide_screen("battle")
sdelay(1.5)
if (debug):
raise
renpy.jump("combat")
stdout("================= call ended")
btl_ready=True
renpy.show_screen("battle")
# TODO: Send to server new arrangement of units
# Update Battle with server response
if (do_action == ACT_TURN):
$ response=loadbattle(Battle["party"])
elif (do_action == ACT_SUMMON):
$ response=loadsummon()
else:
$ stdout("INVALID ACTION CODE: %d" % do_action)
jump combat
# Maybe nothing happened
if response == ERR_SUMMON:
$ response = Battle
# Maybe we error'ed out and should relog
if (response in [FAILUREMSG, OFFLINEMSG, ERR_JSONDECODER, ERR_LOGIN_DEFAULT]):
$ renpy.call_screen("msgbox", "Error:\n\n%s" % response)
# Try again (x2) (FIXME: If packet was malformated, might be a bad idea)
$ response=loadbattle(Battle["party"])
# Maybe we error'ed out and should relog
if (response in [FAILUREMSG, OFFLINEMSG, ERR_JSONDECODER, ERR_LOGIN_DEFAULT]):
$ renpy.call_screen("msgbox", "Error:\n\n%s" % response)
# Try again (x3)
$ response=loadbattle(Battle["party"])
# Maybe we error'ed out and should relog
if (response in [FAILUREMSG, OFFLINEMSG, ERR_JSONDECODER, ERR_LOGIN_DEFAULT]):
$ renpy.call_screen("msgbox", "Error:\n\n%s\nYou'll be taken to main screen." % response)
# Better no longer insist
return
# The server should send a JSON of results...
# Which is being stored as `log` field in an array
# [who, id, what, damage, target, id]
# [ 0, 1, 2, 3, 4, 5]
python:
print str(response["log"])
print len(response["log"])
for entry in response["log"]:
# The most important is entry[2], which contains the action
# Oh noes - I forgot the damage -.-
try:
print("ACTION %d (DMG %d)" % (entry[2], entry[3]))
# TODO: Highlight caster
# TODO: Unmark cards
# TODO: SRV_WAVE (ends waves) SRV_SPHERE (updates spheres)
# TODO: SRV_NONE (saves action in tmp memory)
# (then at next non-none, bt_handlelog() tmp memory)
bt_handlelog(entry)
except:
traceback.print_exc()
## Not everything can be calculated from the logs...
## Legacy render system cannot handle finished battles
if (response["result"] == ""):
stdout("Extrapolating...")
# Extrapolate & Render enemy damage
idx=0
while idx < len(Battle["enemy"]):
try:
# TODO: Draw the updates in "sequence"
if (response["wave"] != Battle["wave"]):
hit_someone_verbose(Battle["enemy"][idx],
Battle["enemy"][idx]["hp"])
else:
hit_someone_verbose(Battle["enemy"][idx],
Battle["enemy"][idx]["hp"]-response["enemy"][idx]["hp"])
idx+=1
renpy.pause(0.1)
except:
idx+=1
pass
# Extrapolate & Render party damage
idx=0
try:
while idx < len(Battle["party"]):
hit_someone_verbose(Battle["party"][idx], Battle["party"][idx]["hp"]-response["party"][idx]["hp"])
idx+=1
renpy.pause(0.1)
except:
idx+=1
pass
# Maybe we received the victory code
if (response["result"] != ""):
$ renpy.free_memory()
# TODO: Announce the result screen
jump results
# We may be going to boss fight
if (response["wave"] != Battle["wave"] and response["wave"] == response["max_wave"]):
play music MUSIC_BOSS.id() fadein 0.5
$ renpy.notify("BOSS FIGHT!")
# Continue the combat
$ Battle=response
$ Battle["log"]=[]
jump combat
label results:
$ Player["status"]=ST_TOWN
if (response["result"] == "DEFEAT"):
$ renpy.call_screen("msgbox", "Result:\n%s" % _("DEFEAT"))
jump restore
play music MUSIC_VICTORY.id() fadein 0.5
# TODO: Use small icons
python:
# Save current quest to 'quest' variable (wasn't it set before?)
quest=dl_search(allquests[Battle["world"]], "quest_id", Battle["quest_id"])
if (quest == ERR_INVALID):
raise Exception("ERROR, QUEST NOT FOUND, %d" % Battle["quest_id"])
# Update other data
Player["crystals"]+=int(response["crystals"])
Player["gp"]+=int(response["gp"])
Player["exp"]+=int(response["exp"])
# Write loot array
loot="\n"
if (response["crystals"]):
loot+="%d Crystals\n" % response["crystals"]
for unit in response["loot"]:
try:
loot+="%s %s\n" % (star_write(allunits[unit]["rare"]), allunits[unit]["name"])
except:
loot+="error"
# Maybe you ranked up?
expmsg="Exp: %d -> %d" % (Player["exp"]-response["exp"], Player["exp"])
if (response["rank"]):
Player["level"]+=1
Player["max_ap"]+=response["rank"]-1
# TODO: Update exp/max_exp fields
update_ap()
#Player["exp"]+=1
expmsg="PLAYER {b}RANK UP!{/b} (Max Ap: %d -> %d)" % (Player["max_ap"]-response["rank"]+1, Player["max_ap"])
Player["exp"]=response["rk_exp"]
Player["max_exp"]=response["rk_mexp"]
# Report screen
try:
renpy.call_screen("msgbox", "Result:\n%s\n\nGp: %d\n%s\nLoot:\n%s" % (
_("VICTORY!"),
response["gp"],
expmsg,
"{size=12}"+loot+"{/size}"
))
except:
stdout("ERROR SHOWING VICTORY BOX")
pass
# Update inventory data and restore
$ inv=get_inventory()
python:
try:
renpy.call_screen("msgbox", "Error: %d" % int(inv))
except:
Player["inv"]=dlist()
for a in inv:
Player["inv"].append(a)
qid=Battle["quest_id"]
# WAIT THERE! Perhaps we have some sort of history to show?!
# TODO: Memory
if Player["quest"] < qid:
story=dl_search(allstory, "quest_id", qid)
if (story != ERR_INVALID):
try:
renpy.hide_screen("battle")
except:
stdout("Failed to hide screen battle D:")
pass
hud_story()
print(".:: Story logs (%d) ::." % qid)
if isinstance(story["post_dialog"], str) or isinstance(story["post_dialog"], unicode):
print("Calling in new context: %s" % (story["post_dialog"]))
renpy.call_in_new_context(story["post_dialog"])
else:
bg_is_showing=False
for dial in story["post_dialog"]:
# Background
if str(dial["bg"]) != "":
if bg_is_showing:
renpy.hide("sbg")
show_img("bg "+dial["bg"], tag="sbg")
bg_is_showing=True
show_img("dialog_"+dial["left_sprite"], at_list=[tleft], tag="l")
show_img("dialog_"+dial["center_sprite"], at_list=[tcenter], tag="c")
show_img("dialog_"+dial["right_sprite"], at_list=[tright], tag="r")
renpy.say(dial["name"], dial["message"])
renpy.hide("l")
renpy.hide("c")
renpy.hide("r")
print("%s: %s" % (dial["name"], dial["message"]))
# Background Clean up
if bg_is_showing:
renpy.hide("sbg")
del bg_is_showing
# Maybe we should update player quest
if not (quest["flags"] & 4):
if Player["quest"] < Battle["quest_id"]:
Player["quest"]=copy.copy(Battle["quest_id"])
# Cleaning up
python:
del Battle
jump restore