########################################################################################
# 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
########################################################################################
# Definitions (critical appliances, classes, imports, renpy internals, updater)
init -3 python:
renpy.add_python_directory("python-extra")
import requests, zlib, base64, sys, copy, uuid, time, json, traceback
import os.path
from ws4py.client.threadedclient import WebSocketClient
# set PYTHON_VERSION variable (should be 2713, 3605 could fail)
PYTHON_VERSION="%d%d%02d" % (sys.version_info.major, sys.version_info.minor, sys.version_info.micro)
PYTHON_VERSION=int(PYTHON_VERSION)
# Ren'Py should come with Python 2.7.10 (2710), but just in case
# After all, I only tested with 2.7.10, 2.7.13 and 2.7.15
if (PYTHON_VERSION < 2700 or PYTHON_VERSION > 3000):
raise Exception("WARNING: Python version is not 2.7\nStrange bugs may happen on your client.\n\nClick on \"Ignore\" to continue.\nClick on \"Quit\" to exit.")
# Configuration
config.autoreload = False
config.save_on_mobile_background = False
persistent.release_name = "Diamond Python"
if persistent.host is None:
persistent.host="spheres.tmw2.org"
persistent.serverlist=[["TMW2", "spheres.tmw2.org", 61000]]
# FIXME: Set good defaults (=bad) for Android
if renpy.android:
persistent.ssl_enabled=False
else:
persistent.serverlist.append(["Localhost", "localhost", 61000])
if (persistent.allfiles is None):
persistent.allfiles=[]
allfiles=[]
HOST=str(persistent.host)
PORT=str(persistent.port)
FAILUREMSG="Request failed, Please try again"
OFFLINEMSG="401 Unauthorized"
OKMSG="200 OK"
TIMEOUT_INTERVAL=5.0
MAX_IRC_BUFFER=50
SSL_IS_BROKEN=False
CERT_NONE=0
INT_MAX=2147483647
MAX_RETRIES = 5
debug=copy.copy(config.developer)
TERMINATE=False
CLOSING=False
# Error Codes
ERR_JSONDECODER=101
ERR_LOGIN_DEFAULT=102
ERR_INVALID=103
ERR_TIMEOUT=104
ERR_NOGEMS=105
ERR_INVFULL=106
ERR_SUMMON=108
ERR_OK=200
ERR_OUTDATED=401
# All error code library
ERRNO=[FAILUREMSG, OFFLINEMSG, OKMSG, ERR_LOGIN_DEFAULT, ERR_INVALID, ERR_TIMEOUT, ERR_NOGEMS, ERR_INVFULL, ERR_OK]
# Core musics/sfx
MUSIC_OPENING="sfx/bgm01.mp3"
MUSIC_TOWN="sfx/bgm02.mp3"
# Battle actions
ACT_NONE =0
ACT_TURN =1
ACT_SUMMON =2
# Spheres actions
AP_NONE =False
AP_SPHERE =1
AP_SKILL =2
# Status
ST_TOWN =0
ST_QUEST =1
# Unit flags
UF_NOLVL =1
UF_NOPART =2
UF_EXPUP =4
UF_EVOMAT =8
UF_SUPEREVO =64
# Jobs
Job_Swordsman =1
Job_Undefined1 =2
Job_Mage =3
Job_Archer =4
Job_Undefined2 =5
Job_Special =6
# IRC flags
IRC_AUTH_NONE =0
IRC_AUTH_USER =1
IRC_AUTH_NICK =2
IRC_AUTH_CHAN =3
# Spheres
SPH_NONE =0
SPH_WIDEATTACK =1
SPH_PIERCE =2
SPH_ASSAULT =3
SPH_HEAL =4
SPH_HEALALL =5
SPH_ATKUP =6
SPH_DEFUP =7
# Combat code
SRV_SKILL =1000
SRV_SERVER =9900
SRV_WAVE =9901
SRV_SPHERE =9902
SRV_SUMMON =9903
SRV_NOCAST =9904
# Special error structs
ERR_MOBSTRUCT ={
"name": "ERROR",
"unit_id": 0,
"max_hp": 0,
"hp": 0,
"atk": 0,
"ele": 0,
"status_effects": 0
}
ERR_PLAYERSTRUCT={
"unit_id": 0,
"max_hp": 0,
"hp": 0,
"atk": 0,
"ele": 0,
"status_effects": 0
}
# Returns human readable job
def parse_job(JOB):
if (JOB == Job_Swordsman):
return _("Swordsmaster")
elif (JOB == Job_Mage):
return _("Wizard")
elif (JOB == Job_Archer):
return _("Ranger")
elif (JOB == Job_Special):
return _("Special")
else:
return _("???")
# Smart Print command
def stdout(message):
if debug:
if renpy.android:
if not renpy.is_init_phase():
renpy.notify(message)
else:
print(message)
renpy.write_log("[DEBUG] %s" % message)
else:
renpy.write_log("[GAME] %s" % message)
return
# Smart wait
def sdelay(delta=0.02):
try:
renpy.pause(delta, hard=True)
except:
time.sleep(delta)
return
# IF Then Else (IFTE)
def ifte(ifs, then, elses):
if (ifs):
return then
else:
return elses
# Returns number of seconds since UNIX EPOCH
def now():
return int(time.time())
# Global classes
# We need to override standard list method. Original by Triptych (stackoverflow)
class dlist(list):
def __setitem__(self, index, value):
size = len(self)
if index >= size:
self.extend(None for _ in range(size, index + 1))
list.__setitem__(self, index, value)
class ExecuteOnCall():
def __init__(self, callable, *args, **kwargs):
self.callable = callable
self.args = args
self.kwargs = kwargs
def __call__(self):
rv = self.callable(*self.args, **self.kwargs)
return rv
def id(self):
return self.__call__()
class RetString():
def __init__(self, string):
self.string = string
def __call__(self):
return self.string
def id(self):
return self.__call__()
# Screen Functions/class
# Override
class SpheresMainMenu(MainMenu):
def __call__(self):
if not self.get_sensitive():
return
if self.confirm:
if config.autosave_on_quit:
renpy.force_autosave()
layout.yesno_screen(layout.MAIN_MENU, SpheresMainMenu(False))
else:
# Flush labels/sockets/timers as needed
renpy.call_in_new_context("quit")
# Restart
renpy.full_restart(config.game_main_transition)
class ExtraImage(renpy.display.im.Image):
"""
Custom image manipulator, based on bink's code, topic 11732
"""
def __init__(self, loc, **properties):
"""
@param loc: Where the image really is (get_path already applied)
"""
super(ExtraImage, self).__init__(loc, **properties)
self.loc = loc
def load(self, unscaled=False): # W0221
try:
#page = open(self.loc, "rb")
#pd = page.read()
#picdata = os.tmpfile()
#picdata.write(pd)
#picdata.seek(0) # reset seek position
#stdout("Requested to open: %s" % repr(self.loc))
picdata = open(self.loc, "rb")
#stdout("Picdata is open (%s)" % self.loc)
if unscaled:
surf = renpy.display.pgrender.load_image_unscaled(picdata, self.loc)
else:
surf = renpy.display.pgrender.load_image(picdata, self.loc)
#stdout("Picdata was closed")
picdata.close()
#page.close()
return surf
except Exception, e:
if renpy.config.missing_image_callback:
im = renpy.config.missing_image_callback(self.loc)
if im is None:
raise e
return im.load()
raise
def virtpos(posix):
if isinstance(posix, float):
return posix*1024
else:
return posix/1024.0
# File Managment Functions
def get_path(path):
if not renpy.windows:
path=path.replace("/", "_")
#return renpy.loader.get_path(path)
return renpy.config.savedir + "/" + path
else:
return renpy.loader.get_path(path)
def get_path_if_exists(path):
if not renpy.windows:
path=path.replace("/", "_")
#return renpy.loader.get_path(path)
return renpy.config.savedir + "/" + path
else:
return renpy.loader.transfn(path)
# Override music audio loader
def SpheresLoadAudioFile(fn):
"""
Returns a file-like object for the given filename.
"""
try:
fna = get_path_if_exists(fn)
if not os.path.isfile(fna):
raise Exception("Not a file")
rv = renpy.loader.open_file(fna, "rb")
except:
rv = renpy.loader.load(fn)
return rv
renpy.audio.audio.load=SpheresLoadAudioFile
## Post setup
if (persistent.summon is None):
persistent.summon=ifte(config.developer, 15, 0)
#############################################################################
# URL3 Function
def GAME_UPDATER():
global tr_uptodate, tr_fatality
tr_uptodate=False
# If no version is provided, we are using default files
# Default files version is "1" (Should never happen)
if (persistent.version is None):
persistent.version=1
# Download upstream version
x=requests.get("http://"+HOST+'/version.txt', verify=False)
try:
ver=int(x.text)
except:
stdout("IMPOSSIBLE TO DETERMINE VERSION")
raise Exception("Could not estabilish a connection to update server:\n%s is not valid." % x.text) # TODO: Show this beautifully?
# TODO: Should we set a "ver"?
if (int(persistent.version) < ver):
try:
# TODO: Check if the server have SSL support
stdout("Downloading updated game data...")
# Download quests.json
f=open(get_path("quests.json"), "w")
stdout("Downloading quests.json")
x=requests.get("http://"+HOST+'/quests.json', verify=False)
f.write(x.text)
f.close()
# Download units.json
f=open(get_path("units.json"), "w")
stdout("Downloading units.json")
x=requests.get("http://"+HOST+'/units.json', verify=False)
f.write(x.text)
f.close()
# Download story.json
f=open(get_path("story.json"), "w")
stdout("Downloading story.json")
x=requests.get("http://"+HOST+'/story.json', verify=False)
f.write(x.text)
f.close()
# Download world.json
f=open(get_path("world.json"), "w")
stdout("Downloading world.json")
x=requests.get("http://"+HOST+'/world.json', verify=False)
f.write(x.text)
f.close()
# Download bar.json
f=open(get_path("bar.json"), "w")
stdout("Downloading bar.json")
x=requests.get("http://"+HOST+'/bar.json', verify=False)
f.write(x.text)
f.close()
# Download summons.json
f=open(get_path("summons.json"), "w")
stdout("Downloading summons.json")
x=requests.get("http://"+HOST+'/summons.json', verify=False)
f.write(x.text)
f.close()
persistent.version=ver
stdout("Update complete")
except:
tr_fatality = False
traceback.print_exc()
stdout("[FATAL] Unable to update JSON files; Aborted")
# Download server news
# Handled by GAME_LOADER
tr_uptodate=True
return tr_load