From f320a81084dbeaa3d08e2cbe5b9c8c240bba4858 Mon Sep 17 00:00:00 2001
From: Jesusaves <cpntb1@ymail.com>
Date: Fri, 18 Dec 2020 01:57:33 -0300
Subject: Split combat.rpy in three files, move python+screen code away from
 the label. Combat is, by far, one of the messiest parts of the script.

---
 game/battle.rpy     | 210 ++++++++++++++++++++++++++
 game/gui/battle.rpy | 419 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 game/quest.rpy      | 166 +++++++++++++++++++++
 3 files changed, 795 insertions(+)
 create mode 100644 game/battle.rpy
 create mode 100644 game/gui/battle.rpy
 create mode 100644 game/quest.rpy

(limited to 'game')

diff --git a/game/battle.rpy b/game/battle.rpy
new file mode 100644
index 0000000..745bef0
--- /dev/null
+++ b/game/battle.rpy
@@ -0,0 +1,210 @@
+########################################################################################
+#     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
+
+label combat:
+    # Implement combat view
+    $ hud_clear()
+    $ show_img("bg battle", False)
+    scene bg battle
+    $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]
+    call screen battle
+    $stdout("================= call ended")
+    # TODO: Send to server new arrangement of units
+
+    # Update Battle with server response
+    $ 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 (x2)
+        $ 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
+
+
+    # TODO The server should send a JSON of results...
+
+    # Wait for server to confirm the fight end
+    if (response["result"] != ""):
+        $ renpy.free_memory()
+        # TODO: Announce the result screen
+        jump results
+
+    # Fight continues
+    # TODO this is suboptimal, and the loops are also wrong
+    # Maybe we should get info about what was used and whatnot?
+    show screen battle
+    python:
+        # TODO: Render sphere usage
+        # 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
+
+        # 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
+
+    # 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!")
+
+    $ Battle=response
+    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, "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
+            update_ap(Player["max_ap"]-Player["ap"])
+            #Player["exp"]+=1
+            expmsg="PLAYER {b}RANK UP!{/b} (Max Ap: %d -> %d)" % (Player["max_ap"]-response["rank"]+1, Player["max_ap"])
+
+        # Report screen
+        renpy.call_screen("msgbox", "Result:\n%s\n\nGp: %d\n%s\nLoot:\n%s" % (
+                                    _("VICTORY!"),
+                                    response["gp"],
+                                    expmsg,
+                                    "{size=12}"+loot+"{/size}"
+    ))
+
+    # 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?!
+        if Player["quest"] < qid:
+            story=dl_search(allstory, "quest_id", qid)
+
+            if (story != ERR_INVALID):
+                hud_story()
+                print ".:: Story logs (%d) ::." % qid
+
+                if isinstance(story["post_dialog"], str) or isinstance(story["post_dialog"], unicode):
+                    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", ext=".jpg")
+                            bg_is_showing=True
+
+                        show_img("dialog_"+dial["left_sprite"], at_list=[left], tag="l")
+                        show_img("dialog_"+dial["center_sprite"], at_list=[center], tag="c")
+                        show_img("dialog_"+dial["right_sprite"], at_list=[right], 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"]=Battle["quest_id"]
+
+    jump restore
+
diff --git a/game/gui/battle.rpy b/game/gui/battle.rpy
new file mode 100644
index 0000000..0142842
--- /dev/null
+++ b/game/gui/battle.rpy
@@ -0,0 +1,419 @@
+########################################################################################
+#     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 Interface
+init python:
+    ####################### Client rendering
+    def c_dragid(dg):
+        if dg == "party1":
+            return 0
+        elif dg == "party2":
+            return 1
+        elif dg == "party3":
+            return 2
+        elif dg == "party4":
+            return 3
+        elif dg == "party5":
+            return 4
+        else:
+            return 0# We can't return -1...
+
+    def combat_action(drags, drop):
+        global use_sphere
+        if not drop:
+            return
+
+        # Define your char slot, and reset the sphere usage for it
+        idn=c_dragid(drags[0].drag_name)
+        use_sphere[idn]=AP_NONE
+
+        # Check if you asked for a skill
+        if (drop.drag_name == "Skill"):
+            renpy.notify("You can't use a skill!")
+            drags[0].snap(0, 30, delay=0.1)
+            #use_sphere[idn]=AP_SKILL
+            return
+
+        # Check if you asked for a sphere
+        if (drop.drag_name == "Sphere"):
+            if (Battle["spheres"][idn]):
+                # Mark to use the sphere - if it exists!
+                drags[0].snap(0, 60, delay=0.1)
+                use_sphere[idn]=AP_SPHERE
+                return
+            else:
+                renpy.notify("You can't use a sphere!")
+                drags[0].snap(0, 30, delay=0.1)
+                return
+        return
+
+# FIXME: Allow to drag spheres instead of whole chars.
+# More checks needed on client, but less on server
+# And spheres are 1D, so much easier on RAM/CPU as well
+init 2 python:
+    def blayout():
+        renpy.call_in_new_context("bl_context")
+        return
+    def blayout2():
+        o1=renpy.call_screen("battle_layout", "Select unit to swap")
+        o2=renpy.call_screen("battle_layout", "Select unit to swap with")
+        if (o1 == o2):
+            return
+        tmp=copy.copy(Battle["party"][o2])
+        Battle["party"][o2]=copy.copy(Battle["party"][o1])
+        Battle["party"][o1]=tmp
+        renpy.notify("Done!")
+        return
+
+label bl_context:
+    $ blayout2()
+    return
+
+#############################################################################
+screen battle():
+    # Background
+    add "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={"hp": 0}
+        try:
+            en2=Battle["enemy"][1]
+            show_img("mob_"+str(en2["unit_id"]), False) # Validate
+        except:
+            en2={"hp": 0}
+        try:
+            en3=Battle["enemy"][2]
+            show_img("mob_"+str(en3["unit_id"]), False) # Validate
+        except:
+            en3={"hp": 0}
+
+    ####################################################
+    # Render HUD
+    frame:
+        xalign 0.0
+        yalign 0.0
+        xfill True
+        ymaximum 60
+        yfill True
+        background Frame("gui/frame.png", 0, 0)
+        hbox:
+            # Preferences button
+            imagebutton auto "gfx/gui/cog_%s.png" action ShowMenu('preferences')
+            textbutton _("Party") action Function(blayout)
+            null width 20
+            text "%d " % (Battle["turn"])
+            text _(" Turn")
+            null width 300
+            text _("Wave %d/%d" % (Battle["wave"], Battle["max_wave"]))
+            null width 300
+            textbutton _("I'm ready") action Return()
+
+    ####################################################
+    # Render enemies
+
+    # Enemy 1
+    if (en1["hp"] > 0):
+        add At("mob_"+str(en1["unit_id"]), enemy1)
+        vbox:
+            xalign 0.5
+            yanchor 0.5
+            ypos 0.24
+            xmaximum 180
+            ymaximum 20
+            text en1["name"]
+            bar value en1["hp"] range en1["max_hp"]
+
+    # Enemy 2
+    if (en2["hp"] > 0):
+        add At("mob_"+str(en2["unit_id"]), enemy2)
+        vbox:
+            xalign 1.0
+            yanchor 0.5
+            ypos 0.24
+            xmaximum 180
+            ymaximum 20
+            text en2["name"]
+            bar value en2["hp"] range en2["max_hp"]
+
+    # Enemy 3
+    if (en3["hp"] > 0):
+        add At("mob_"+str(en3["unit_id"]), enemy3)
+        vbox:
+            xalign 0.0
+            yanchor 0.5
+            ypos 0.24
+            xmaximum 180
+            ymaximum 20
+            text en3["name"]
+            bar value en3["hp"] range en3["max_hp"]
+
+    ####################################################
+    # Render allies
+    # TODO: Gray out and unmovable if dead
+    # One drag group per party member defined in Battle
+    draggroup:
+        xalign 0.2
+        yalign 1.0
+        xmaximum 160
+        ymaximum 300 # 240 + 60: 30 px each dimension
+        # Display the background
+        drag:
+            child At("gfx/action.png", c_party1)
+            draggable False
+            droppable False
+        # Display the card (if there's one)
+        if (fx1):
+            drag:
+                drag_name "party1"
+                child At(fx1, c_party1)
+                droppable False
+                if (Battle["party"][0]["hp"] > 0):
+                    dragged combat_action
+                else:
+                    draggable False
+                ypos 30
+            # The action areas
+            drag:
+                drag_name "Skill"
+                child At("gfx/actionarea.png", c_party1)
+                draggable False
+                ypos 0.0
+            drag:
+                drag_name "Sphere"
+                child At("gfx/actionarea.png", c_party1)
+                draggable False
+                yalign 1.0
+        # Display the sphere
+        drag:
+            child ("gfx/sphere/"+str(Battle["spheres"][0])+".png")
+            draggable False
+            droppable False
+            yalign 1.0
+            xalign 0.5
+    if (fx1 and Battle["party"][0]["hp"] <= 0):
+        add At("gfx/off.png", c_party1)
+
+    # One drag group per party member defined in Battle
+    draggroup:
+        xalign 0.4
+        yalign 1.0
+        xmaximum 160
+        ymaximum 300
+        drag:
+            child At("gfx/action.png", c_party2)
+            draggable False
+            droppable False
+        if (fx2):
+            drag:
+                drag_name "party2"
+                child At(fx2, c_party2)
+                droppable False
+                if (Battle["party"][1]["hp"] > 0):
+                    dragged combat_action
+                else:
+                    draggable False
+                ypos 30
+            drag:
+                drag_name "Skill"
+                child At("gfx/actionarea.png", c_party2)
+                draggable False
+                ypos 0.0
+            drag:
+                drag_name "Sphere"
+                child At("gfx/actionarea.png", c_party2)
+                draggable False
+                yalign 1.0
+        drag:
+            child ("gfx/sphere/"+str(Battle["spheres"][1])+".png")
+            draggable False
+            droppable False
+            yalign 1.0
+            xalign 0.5
+
+    if (fx2 and Battle["party"][1]["hp"] <= 0):
+        add At("gfx/off.png", c_party2)
+    # One drag group per party member defined in Battle
+    draggroup:
+        xalign 0.6
+        yalign 1.0
+        xmaximum 160
+        ymaximum 300 
+        drag:
+            child At("gfx/action.png", c_party3)
+            draggable False
+            droppable False
+        if (fx3):
+            drag:
+                drag_name "party3"
+                child At(fx3, c_party3)
+                droppable False
+                if (Battle["party"][2]["hp"] > 0):
+                    dragged combat_action
+                else:
+                    draggable False
+                ypos 30
+            drag:
+                drag_name "Skill"
+                child At("gfx/actionarea.png", c_party3)
+                draggable False
+                ypos 0.0
+            drag:
+                drag_name "Sphere"
+                child At("gfx/actionarea.png", c_party3)
+                draggable False
+                yalign 1.0
+        drag:
+            child ("gfx/sphere/"+str(Battle["spheres"][2])+".png")
+            draggable False
+            droppable False
+            yalign 1.0
+            xalign 0.5
+
+    if (fx3 and Battle["party"][2]["hp"] <= 0):
+        add At("gfx/off.png", c_party3)
+    # One drag group per party member defined in Battle
+    draggroup:
+        xalign 0.8
+        yalign 1.0
+        xmaximum 160
+        ymaximum 300 
+        drag:
+            child At("gfx/action.png", c_party4)
+            draggable False
+            droppable False
+        if (fx4):
+            drag:
+                drag_name "party4"
+                child At(fx4, c_party4)
+                droppable False
+                if (Battle["party"][3]["hp"] > 0):
+                    dragged combat_action
+                else:
+                    draggable False
+                ypos 30
+            drag:
+                drag_name "Skill"
+                child At("gfx/actionarea.png", c_party4)
+                draggable False
+                ypos 0.0
+            drag:
+                drag_name "Sphere"
+                child At("gfx/actionarea.png", c_party4)
+                draggable False
+                yalign 1.0
+        drag:
+            child ("gfx/sphere/"+str(Battle["spheres"][3])+".png")
+            draggable False
+            droppable False
+            yalign 1.0
+            xalign 0.5
+
+    if (fx4 and Battle["party"][3]["hp"] <= 0):
+        add At("gfx/off.png", c_party4)
+
+
+    ####################################################
+    # Render HPBARs
+    if (fx1):
+        frame:
+            xalign 0.21
+            yalign 1.0
+            ymaximum 10
+            xmaximum 120
+            bar value Battle["party"][0]["hp"] range Battle["party"][0]["max_hp"] xmaximum 120
+
+    if (fx2):
+        frame:
+            xalign 0.4
+            yalign 1.0
+            ymaximum 10
+            xmaximum 120
+            bar value Battle["party"][1]["hp"] range Battle["party"][1]["max_hp"] xmaximum 120
+
+    if (fx3):
+        frame:
+            xalign 0.6
+            yalign 1.0
+            ymaximum 10
+            xmaximum 120
+            bar value Battle["party"][2]["hp"] range Battle["party"][2]["max_hp"] xmaximum 120
+
+    if (fx4):
+        frame:
+            xalign 0.8
+            yalign 1.0
+            ymaximum 10
+            xmaximum 120
+            bar value Battle["party"][3]["hp"] range Battle["party"][3]["max_hp"] xmaximum 120
+
+screen battle_layout(lb="Select unit"):
+    vbox:
+        xalign 0.5
+        yalign 0.3
+        label _("{size=32}{color=#f00}%s{/color}{/size}" % lb)
+        null height 20
+        hbox:
+            for i, item in enumerate(Battle["party"]):
+                imagebutton:
+                    if item["unit_id"] > 0:
+                        idle At(Composite(
+                                (340, 340),
+                                (0, 0), "gfx/square/bg.png",
+                                (0, 0), "gfx/square/units/%d.png" % item["unit_id"],
+                                (0, 0), "gfx/square/%d.png" % allunits[item["unit_id"]]["rare"],
+                            ), czoom_70)
+                    else:
+                        idle At("gfx/square/bg.png", czoom_70)
+                    action Return(i)
+        null height 80
+        # The close button returns -1 and comes last (TODO)
+        imagebutton:
+                idle At("gfx/square/back_idle.png", czoom_75)
+                hover At("gfx/square/back_hover.png", czoom_75)
+                action Return(-1)
+
diff --git a/game/quest.rpy b/game/quest.rpy
new file mode 100644
index 0000000..76b89ce
--- /dev/null
+++ b/game/quest.rpy
@@ -0,0 +1,166 @@
+########################################################################################
+#     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
+########################################################################################
+# Mission Selection & Quest Handling
+init python:
+    ######################## Server Communications
+    def loadquest(quest_id, party_id=1):
+        import json
+        raw=send_packet("begin_quest", questdata(quest_id, party_id))
+
+        bt=json_decode(raw)
+        if (bt == ERR_JSONDECODER):
+            return ERR_JSONDECODER
+        return bt
+
+    def loadbattle(new_arrange):
+        raw=send_packet("battle", battledata(new_arrange, use_sphere))
+
+        bt=json_decode(raw)
+        if (bt == ERR_JSONDECODER):
+            return ERR_JSONDECODER
+        return bt
+
+###################################################################
+# TODO: Quest menus and selections should be auto-generated
+# World map structure: ["name", min_level, {quest1}, {quest2} ...]
+label quest_select:
+    $ show_img("bg battle2", False, ext=".jpg")
+    scene bg battle2
+    play music MUSIC_WORLDMAP.id() fadein 0.5
+    python:
+        from copy import copy
+        worldmap=[]
+        areamap=[]
+        for arx in allworld:
+            arena=copy(arx)
+            name=arena.pop(0)
+            req=arena.pop(0)
+            if Player["quest"] >= req:
+                worldmap.append((name, arena))
+
+        # Display world map menu
+        worldmap.append(("Return", -1))
+        mapselected=renpy.display_menu(worldmap)
+        del worldmap
+        if mapselected == -1:
+            renpy.jump("restore")
+
+        # Now we have the mapselected array, with dict
+        for arx in mapselected:
+            quest=copy(arx)
+            name=quest["name"]
+            qid=quest["quest_id"]
+
+            # We also want to show cost and requeriments
+            entry=dl_search(allquests, "quest_id", qid)
+            if entry != ERR_INVALID:
+                cost=entry["cost"]
+                req=entry["requeriment"]
+            else:
+                cost=-1
+                req=99999
+
+            # Add entry (if valid)
+            if Player["quest"] >= req:
+                if Player["ap"] >= cost:
+                    areamap.append(("%s (%d AP)" % (name, cost), qid))
+                else:
+                    areamap.append(("{s}%s (%d AP){/s}" % (name, cost), None))
+
+        # Display area menu
+        areamap.append(("Return", -1))
+        qid=renpy.display_menu(areamap)
+        del areamap
+        if qid == -1 or qid is None:
+            renpy.jump("quest_select")
+
+    jump quest_selected
+
+###################################################################
+label quest_selected:
+    # Get quest data
+    python:
+        quest=dl_search(allquests, "quest_id", qid)
+
+        # Uhm, how did this happen? Means client-data is not fully updated!
+        if (quest == ERR_INVALID):
+            renpy.call_screen("msgbox", "ERROR:\n\nRequested Quest does not exist client-side\nAn update is required. We'll now close the app.")
+            raise KeyboardInterrupt()
+
+    # Confirm the quest cost
+    $apmsg=_("Quest cost: %d/%d AP" % (quest["cost"], Player["ap"]))
+    menu:
+        "[apmsg]"
+        "Accept Quest" if Player["ap"] >= quest["cost"]:
+            pass
+        "Decline Quest":
+            jump quest_select
+
+    # Begin the quest
+    $ Battle=loadquest(qid)
+
+    # Check for error
+    if (Battle in [FAILUREMSG, OFFLINEMSG, ERR_JSONDECODER, ERR_LOGIN_DEFAULT]):
+        $ renpy.call_screen("msgbox", "Error:\n\n%s\nYou'll be taken to town." % Battle)
+        jump restore
+
+    # Reduce the paid AP
+    python:
+
+        # Consume the AP
+        update_ap(-(quest["cost"]))
+
+        # Before fighting, should we perhaps show story?
+        if Player["quest"] < qid:
+            story=dl_search(allstory, "quest_id", qid)
+
+            if (story != ERR_INVALID):
+                hud_story()
+                print ".:: Story logs (%d) ::." % qid
+
+                if isinstance(story["pre_dialog"], str) or isinstance(story["pre_dialog"], unicode):
+                    renpy.call_in_new_context(story["pre_dialog"])
+                else:
+                    bg_is_showing=False
+                    for dial in story["pre_dialog"]:
+                        # Background
+                        if str(dial["bg"]) != "":
+                            if bg_is_showing:
+                                renpy.hide("sbg")
+                            show_img("bg "+dial["bg"], tag="sbg", ext=".jpg")
+                            bg_is_showing=True
+
+                        show_img("dialog_"+dial["left_sprite"], at_list=[left], tag="l")
+                        show_img("dialog_"+dial["center_sprite"], at_list=[center], tag="c")
+                        show_img("dialog_"+dial["right_sprite"], at_list=[right], 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
+
+    # Okay, story-telling time is over: To arms!
+    play music MUSIC_BATTLE.id() fadein 0.5
+    $ renpy.free_memory()
+    window hide
+
-- 
cgit v1.2.3-70-g09d2